From 203ce836134aee9779024975517986f0138acc82 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Tue, 14 Nov 2023 13:22:23 +0000 Subject: [PATCH] test: restructure --- --- a/js-packages/.gitignore +++ b/js-packages/.gitignore @@ -1,8 +1,11 @@ /node_modules/ /*/dist/ -/*/tsconfig.tsbuildinfo +/**/tsconfig.tsbuildinfo properties.csv erc721.csv erc20.csv .yarn/cache -.yarn/install-state.gz \ No newline at end of file +.yarn/install-state.gz +/scripts/metadata.json +/**/*.js +/**/*.d.ts --- a/js-packages/package.json +++ b/js-packages/package.json @@ -2,6 +2,9 @@ "name": "unique-tests", "version": "1.0.0", "description": "Unique Chain Tests", + "author": "", + "license": "SEE LICENSE IN ../LICENSE", + "homepage": "", "main": "", "private": true, "dependencies": { @@ -29,6 +32,7 @@ "chai-subset": "^1.6.0", "eslint": "^8.53.0", "eslint-plugin-mocha": "^10.2.0", + "ts-node": "^10.9.1", "typescript": "^5.2.2" }, "mocha": { @@ -38,16 +42,13 @@ ] }, "scripts": { - "lint": "eslint --ext .ts,.js src/", + "lint": "eslint --ext .ts .", "fix": "yarn lint --fix", - "polkadot-types-fetch-metadata": "yarn ts-node --esm scripts/src/fetchMetadata.ts", - "polkadot-types-from-defs": "ts-node --esm ./node_modules/.bin/polkadot-types-from-defs --endpoint types/src/metadata.json --input types/src/ --package .", - "polkadot-types-from-chain": "ts-node --esm ./node_modules/.bin/polkadot-types-from-chain --endpoint types/src/metadata.json --output types/src/ --package .", - "polkadot-types": "echo \"export default {}\" > types/src/lookup.ts && yarn polkadot-types-fetch-metadata && yarn polkadot-types-from-defs && yarn polkadot-types-from-defs && yarn polkadot-types-from-chain" + "polkadot-types-fetch-metadata": "yarn ts-node --esm scripts/fetchMetadata.ts", + "polkadot-types-from-defs": "ts-node --esm ./node_modules/.bin/polkadot-types-from-defs --endpoint scripts/metadata.json --input types/ --package .", + "polkadot-types-from-chain": "ts-node --esm ./node_modules/.bin/polkadot-types-from-chain --endpoint scripts/metadata.json --output types/ --package .", + "polkadot-types": "echo \"export default {}\" > types/lookup.ts && yarn polkadot-types-fetch-metadata && yarn polkadot-types-from-defs && yarn polkadot-types-from-defs && yarn polkadot-types-from-chain && rm types/registry.ts" }, - "author": "", - "license": "SEE LICENSE IN ../LICENSE", - "homepage": "", "resolutions": { "decode-uri-component": "^0.2.1" }, --- a/js-packages/playgrounds/src/types.ts +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -import type {IKeyringPair} from '@polkadot/types/types'; - -export const NON_EXISTENT_COLLECTION_ID = 4_294_967_295; - -export const MILLISECS_PER_BLOCK = 12000; -export const MINUTES = 60_000 / MILLISECS_PER_BLOCK; -export const HOURS = MINUTES * 60; -export const DAYS = HOURS * 24; - -export interface IEvent { - section: string; - method: string; - index: [number, number] | string; - data: any[]; - phase: {applyExtrinsic: number} | 'Initialization', -} - -export interface IPhasicEvent { - phase: any, // {ApplyExtrinsic: number} | 'Initialization', - event: IEvent; -} - -export interface ITransactionResult { - status: 'Fail' | 'Success'; - result: { - dispatchError: any, - events: IPhasicEvent[]; - }, - blockHash: string, - moduleError?: string | object; -} - -export interface ISubscribeBlockEventsData { - number: number; - hash: string; - timestamp: number; - events: IEvent[]; -} - -export interface ILogger { - log: (msg: any, level?: string) => void; - level: { - ERROR: 'ERROR'; - WARNING: 'WARNING'; - INFO: 'INFO'; - [key: string]: string; - } -} - -export interface IUniqueHelperLog { - executedAt: number; - executionTime: number; - type: 'extrinsic' | 'rpc'; - status: 'Fail' | 'Success'; - call: string; - params: any[]; - moduleError?: string; - dispatchError?: any; - events?: any; -} - -export interface IApiListeners { - connected?: (...args: any[]) => any; - disconnected?: (...args: any[]) => any; - error?: (...args: any[]) => any; - ready?: (...args: any[]) => any; - decorated?: (...args: any[]) => any; -} - -export type ICrossAccountId = { - Substrate: TSubstrateAccount; -} | { - Ethereum: TEthereumAccount; -} - -export type ICrossAccountIdLower = { - substrate: TSubstrateAccount; -} | { - ethereum: TEthereumAccount; -}; - -export interface IEthCrossAccountId { - 0: TEthereumAccount; - 1: TSubstrateAccount; - eth: TEthereumAccount; - sub: TSubstrateAccount; -} - -export interface ICollectionLimits { - accountTokenOwnershipLimit?: number | null; - sponsoredDataSize?: number | null; - sponsoredDataRateLimit?: {blocks: number} | {sponsoringDisabled: null} | null; - tokenLimit?: number | null; - sponsorTransferTimeout?: number | null; - sponsorApproveTimeout?: number | null; - ownerCanTransfer?: boolean | null; - ownerCanDestroy?: boolean | null; - transfersEnabled?: boolean | null; -} - -export interface INestingPermissions { - tokenOwner?: boolean; - collectionAdmin?: boolean; - restricted?: number[] | null; -} - -export interface ICollectionPermissions { - access?: 'Normal' | 'AllowList'; - mintMode?: boolean; - nesting?: INestingPermissions; -} - -export interface IProperty { - key: string; - value?: string; -} - -export interface ITokenPropertyPermission { - key: string; - permission: { - mutable?: boolean; - tokenOwner?: boolean; - collectionAdmin?: boolean; - } -} - -export interface IToken { - collectionId: number; - tokenId: number; -} - -export interface IBlock { - extrinsics: IExtrinsic[] - header: { - parentHash: string, - number: number, - }; -} - -export interface IExtrinsic { - isSigned: boolean, - method: { - method: string, - section: string, - args: any[] - } -} - -export interface ICollectionFlags { - foreign: boolean, - erc721metadata: boolean, -} - -export enum CollectionFlag { - None = 0, - /// External collections can't be managed using `unique` api - External = 1, - /// Supports ERC721Metadata - Erc721metadata = 64, - /// Tokens in foreign collections can be transferred, but not burnt - Foreign = 128, -} - -export interface ICollectionCreationOptions { - name?: string | number[]; - description?: string | number[]; - tokenPrefix?: string | number[]; - mode?: { - nft?: null; - refungible?: null; - fungible?: number; - } - permissions?: ICollectionPermissions; - properties?: IProperty[]; - tokenPropertyPermissions?: ITokenPropertyPermission[]; - limits?: ICollectionLimits; - pendingSponsor?: ICrossAccountId; - adminList?: ICrossAccountId[]; - flags?: number[] | CollectionFlag[] , -} - -export interface IChainProperties { - ss58Format: number; - tokenDecimals: number[]; - tokenSymbol: string[] -} - -export interface ISubstrateBalance { - free: bigint, - reserved: bigint, - frozen: bigint, -} - -export interface IStakingInfo { - block: bigint, - amount: bigint, -} - -export interface IPovInfo { - proofSize: number, - compactProofSize: number, - compressedProofSize: number, - results: any[], - kv: any, -} - -export interface ISchedulerOptions { - scheduledId?: string, - priority?: number, - periodic?: { - period: number, - repetitions: number, - }, -} - -export interface DemocracySplitAccount { - aye: bigint, - nay: bigint, -} - -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' -export type TCollectionMode = 'nft' | 'rft' | 'ft'; --- a/js-packages/playgrounds/src/types.xcm.ts +++ /dev/null @@ -1,36 +0,0 @@ -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 --- a/js-packages/playgrounds/src/unique.dev.ts +++ /dev/null @@ -1,1547 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -import '@unique/opal-types/src/augment-api.js'; -import '@unique/opal-types/src/augment-types.js'; -import '@unique/opal-types/src/types-lookup.js'; - -import {stringToU8a} from '@polkadot/util'; -import {blake2AsHex, encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto'; -import type {ChainHelperBaseConstructor, UniqueHelperConstructor} from './unique.js'; -import {UniqueHelper, ChainHelperBase, HelperGroup} from './unique.js'; -import {ApiPromise, Keyring, WsProvider} from '@polkadot/api'; -import * as defs from '@unique/opal-types/src/definitions.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import type {EventRecord} from '@polkadot/types/interfaces'; -import type {ICrossAccountId, ILogger, IPovInfo, ISchedulerOptions, ITransactionResult, TSigner} from './types.js'; -import type {FrameSystemEventRecord, StagingXcmV2TraitsError, StagingXcmV3TraitsOutcome} from '@polkadot/types/lookup'; -import type {SignerOptions, VoidFn} from '@polkadot/api/types'; -import {spawnSync} from 'child_process'; -import {AcalaHelper, AstarHelper, MoonbeamHelper, PolkadexHelper, RelayHelper, WestmintHelper, ForeignAssetsGroup, XcmGroup, XTokensGroup, TokensGroup} from './unique.xcm.js'; -import {CollectiveGroup, CollectiveMembershipGroup, DemocracyGroup, RankedCollectiveGroup, ReferendaGroup} from './unique.governance.js'; -import type {ICollectiveGroup, IFellowshipGroup} from './unique.governance.js'; - -export class SilentLogger { - log(_msg: any, _level: any): void { } - level = { - ERROR: 'ERROR' as const, - WARNING: 'WARNING' as const, - INFO: 'INFO' as const, - }; -} - -export class SilentConsole { - // TODO: Remove, this is temporary: Filter unneeded API output - // (Jaco promised it will be removed in the next version) - consoleErr: any; - consoleLog: any; - consoleWarn: any; - - constructor() { - this.consoleErr = console.error; - this.consoleLog = console.log; - this.consoleWarn = console.warn; - } - - enable() { - const outFn = (printer: any) => (...args: any[]) => { - for(const arg of args) { - if(typeof arg !== 'string') - continue; - const skippedWarnings = ['1000:: Normal connection closure', 'Not decorating unknown runtime apis:', 'RPC methods not decorated:', 'Not decorating runtime apis', 'Bad input data provided to validate_transaction', 'account balance too low', '1006:: Abnormal Closure']; - const needToSkip = skippedWarnings.reduce((a, b) => a || arg.includes(b), false); - if(needToSkip || arg === 'Normal connection closure') - return; - } - printer(...args); - }; - - console.error = outFn(this.consoleErr.bind(console)); - console.log = outFn(this.consoleLog.bind(console)); - console.warn = outFn(this.consoleWarn.bind(console)); - } - - disable() { - console.error = this.consoleErr; - console.log = this.consoleLog; - console.warn = this.consoleWarn; - } -} - -export interface IEventHelper { - section(): string; - - method(): string; - - wrapEvent(data: any[]): any; -} - -// eslint-disable-next-line @typescript-eslint/naming-convention -function EventHelper(section: string, method: string, wrapEvent: (data: any[]) => any) { - const helperClass = class implements IEventHelper { - wrapEvent: (data: any[]) => any; - _section: string; - _method: string; - - constructor() { - this.wrapEvent = wrapEvent; - this._section = section; - this._method = method; - } - - section(): string { - return this._section; - } - - method(): string { - return this._method; - } - - filter(txres: ITransactionResult) { - return txres.result.events.filter(e => e.event.section === section && e.event.method === method) - .map(e => this.wrapEvent(e.event.data)); - } - - find(txres: ITransactionResult) { - const e = txres.result.events.find(e => e.event.section === section && e.event.method === method); - return e ? this.wrapEvent(e.event.data) : null; - } - - expect(txres: ITransactionResult) { - const e = this.find(txres); - if(e) { - return e; - } else { - throw Error(`Expected event ${section}.${method}`); - } - } - }; - - return helperClass; -} - -function eventJsonData(data: any[], index: number) { - return data[index].toJSON() as T; -} - -function eventHumanData(data: any[], index: number) { - return data[index].toHuman(); -} - -function eventData(data: any[], index: number) { - return data[index] as T; -} - -// eslint-disable-next-line @typescript-eslint/naming-convention -function EventSection(section: string) { - return class Section { - static section = section; - - static Method(name: string, wrapEvent: (data: any[]) => any = () => {}) { - const helperClass = EventHelper(Section.section, name, wrapEvent); - return new helperClass(); - } - }; -} - -function schedulerSection(schedulerInstance: string) { - return class extends EventSection(schedulerInstance) { - static Dispatched = this.Method('Dispatched', data => ({ - task: eventJsonData(data, 0), - id: eventHumanData(data, 1), - result: data[2], - })); - - static PriorityChanged = this.Method('PriorityChanged', data => ({ - task: eventJsonData(data, 0), - priority: eventJsonData(data, 1), - })); - }; -} - -export class Event { - static Democracy = class extends EventSection('democracy') { - static Proposed = this.Method('Proposed', data => ({ - proposalIndex: eventJsonData(data, 0), - })); - - static ExternalTabled = this.Method('ExternalTabled'); - - static Started = this.Method('Started', data => ({ - referendumIndex: eventJsonData(data, 0), - threshold: eventHumanData(data, 1), - })); - - static Voted = this.Method('Voted', data => ({ - voter: eventJsonData(data, 0), - referendumIndex: eventJsonData(data, 1), - vote: eventJsonData(data, 2), - })); - - static Passed = this.Method('Passed', data => ({ - referendumIndex: eventJsonData(data, 0), - })); - - static ProposalCanceled = this.Method('ProposalCanceled', data => ({ - propIndex: eventJsonData(data, 0), - })); - - static Cancelled = this.Method('Cancelled', data => ({ - propIndex: eventJsonData(data, 0), - })); - - static Vetoed = this.Method('Vetoed', data => ({ - who: eventHumanData(data, 0), - proposalHash: eventHumanData(data, 1), - until: eventJsonData(data, 1), - })); - }; - - static Council = class extends EventSection('council') { - static Proposed = this.Method('Proposed', data => ({ - account: eventHumanData(data, 0), - proposalIndex: eventJsonData(data, 1), - proposalHash: eventHumanData(data, 2), - threshold: eventJsonData(data, 3), - })); - static Closed = this.Method('Closed', data => ({ - proposalHash: eventHumanData(data, 0), - yes: eventJsonData(data, 1), - no: eventJsonData(data, 2), - })); - static Executed = this.Method('Executed', data => ({ - proposalHash: eventHumanData(data, 0), - })); - }; - - static TechnicalCommittee = class extends EventSection('technicalCommittee') { - static Proposed = this.Method('Proposed', data => ({ - account: eventHumanData(data, 0), - proposalIndex: eventJsonData(data, 1), - proposalHash: eventHumanData(data, 2), - threshold: eventJsonData(data, 3), - })); - static Closed = this.Method('Closed', data => ({ - proposalHash: eventHumanData(data, 0), - yes: eventJsonData(data, 1), - no: eventJsonData(data, 2), - })); - static Approved = this.Method('Approved', data => ({ - proposalHash: eventHumanData(data, 0), - })); - static Executed = this.Method('Executed', data => ({ - proposalHash: eventHumanData(data, 0), - result: eventHumanData(data, 1), - })); - }; - - static FellowshipReferenda = class extends EventSection('fellowshipReferenda') { - static Submitted = this.Method('Submitted', data => ({ - referendumIndex: eventJsonData(data, 0), - trackId: eventJsonData(data, 1), - proposal: eventJsonData(data, 2), - })); - - static Cancelled = this.Method('Cancelled', data => ({ - index: eventJsonData(data, 0), - tally: eventJsonData(data, 1), - })); - }; - - static UniqueScheduler = schedulerSection('uniqueScheduler'); - static Scheduler = schedulerSection('scheduler'); - - static XcmpQueue = class extends EventSection('xcmpQueue') { - static XcmpMessageSent = this.Method('XcmpMessageSent', data => ({ - messageHash: eventJsonData(data, 0), - })); - - static Success = this.Method('Success', data => ({ - messageHash: eventJsonData(data, 0), - })); - - static Fail = this.Method('Fail', data => ({ - messageHash: eventJsonData(data, 0), - outcome: eventData(data, 2), - })); - }; - - static DmpQueue = class extends EventSection('dmpQueue') { - static ExecutedDownward = this.Method('ExecutedDownward', data => ({ - outcome: eventData(data, 2), - })); - }; -} - -// eslint-disable-next-line @typescript-eslint/naming-convention -export function SudoHelper(Base: T) { - return class extends Base { - constructor(...args: any[]) { - super(...args); - } - - override async executeExtrinsic( - sender: IKeyringPair, - extrinsic: string, - params: any[], - expectSuccess?: boolean, - options: Partial | null = null, - ): Promise { - 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; - } - override async executeExtrinsicUncheckedWeight( - sender: IKeyringPair, - extrinsic: string, - params: any[], - expectSuccess?: boolean, - options: Partial | null = null, - ): Promise { - 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 { - 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( - executionBlockNumber: number, - options: ISchedulerOptions = {}, - ) { - return this.schedule('schedule', executionBlockNumber, options); - } - - scheduleAfter( - blocksBeforeExecution: number, - options: ISchedulerOptions = {}, - ) { - return this.schedule('scheduleAfter', blocksBeforeExecution, options); - } - - schedule( - 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 { - //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 { - 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 { - 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 { - 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 { - 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 { - return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman()); - } -} - -export class DevUniqueHelper extends UniqueHelper { - /** - * Arrange methods for tests - */ - arrange: ArrangeGroup; - wait: WaitGroup; - admin: AdminGroup; - session: SessionGroup; - testUtils: TestUtilGroup; - foreignAssets: ForeignAssetsGroup; - xcm: XcmGroup; - xTokens: XTokensGroup; - tokens: TokensGroup; - 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; - - super(logger, options); - this.arrange = new ArrangeGroup(this); - this.wait = new WaitGroup(this); - 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); - } - - override async connect(wsEndpoint: string, _listeners?: any): Promise { - if(!wsEndpoint) throw new Error('wsEndpoint was not set'); - const wsProvider = new WsProvider(wsEndpoint); - this.api = new ApiPromise({ - provider: wsProvider, - signedExtensions: { - ContractHelpers: { - extrinsic: {}, - payload: {}, - }, - CheckMaintenance: { - extrinsic: {}, - payload: {}, - }, - DisableIdentityCalls: { - extrinsic: {}, - payload: {}, - }, - FakeTransactionFinalizer: { - extrinsic: {}, - payload: {}, - }, - }, - rpc: { - unique: defs.unique.rpc, - appPromotion: defs.appPromotion.rpc, - povinfo: defs.povinfo.rpc, - eth: { - feeHistory: { - description: 'Dummy', - params: [], - type: 'u8', - }, - maxPriorityFeePerGas: { - description: 'Dummy', - params: [], - type: 'u8', - }, - }, - }, - }); - await this.api.isReadyOrError; - this.network = await UniqueHelper.detectNetwork(this.api); - this.wsEndpoint = wsEndpoint; - } - getSudo() { - // eslint-disable-next-line @typescript-eslint/naming-convention - const SudoHelperType = SudoHelper(this.helperBase); - return this.clone(SudoHelperType) as T; - } -} - -export class DevRelayHelper extends RelayHelper { - wait: WaitGroup; - - constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { - options.helperBase = options.helperBase ?? DevRelayHelper; - - super(logger, options); - this.wait = new WaitGroup(this); - } - - getSudo() { - // eslint-disable-next-line @typescript-eslint/naming-convention - const SudoHelperType = SudoHelper(this.helperBase); - return this.clone(SudoHelperType) as DevRelayHelper; - } -} - -export class DevWestmintHelper extends WestmintHelper { - wait: WaitGroup; - - constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { - options.helperBase = options.helperBase ?? DevWestmintHelper; - - super(logger, options); - this.wait = new WaitGroup(this); - } -} - -export class DevStatemineHelper extends DevWestmintHelper {} - -export class DevStatemintHelper extends DevWestmintHelper {} - -export class DevMoonbeamHelper extends MoonbeamHelper { - account: MoonbeamAccountGroup; - wait: WaitGroup; - fastDemocracy: MoonbeamFastDemocracyGroup; - - constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { - options.helperBase = options.helperBase ?? DevMoonbeamHelper; - options.notePreimagePallet = options.notePreimagePallet ?? 'preimage'; - - super(logger, options); - this.account = new MoonbeamAccountGroup(this); - this.wait = new WaitGroup(this); - this.fastDemocracy = new MoonbeamFastDemocracyGroup(this); - } -} - -export class DevMoonriverHelper extends DevMoonbeamHelper { - constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { - options.notePreimagePallet = options.notePreimagePallet ?? 'preimage'; - super(logger, options); - } -} - -export class DevAstarHelper extends AstarHelper { - wait: WaitGroup; - - constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { - options.helperBase = options.helperBase ?? DevAstarHelper; - - super(logger, options); - this.wait = new WaitGroup(this); - } - - getSudo() { - // 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; - - 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); - } - getSudo() { - // eslint-disable-next-line @typescript-eslint/naming-convention - const SudoHelperType = SudoHelper(this.helperBase); - return this.clone(SudoHelperType) as DevAcalaHelper; - } -} - -export class DevPolkadexHelper extends PolkadexHelper { - wait: WaitGroup; - constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { - options.helperBase = options.helperBase ?? PolkadexHelper; - - super(logger, options); - this.wait = new WaitGroup(this); - } - - getSudo() { - // eslint-disable-next-line @typescript-eslint/naming-convention - const SudoHelperType = SudoHelper(this.helperBase); - return this.clone(SudoHelperType) as DevPolkadexHelper; - } -} - -export class DevKaruraHelper extends DevAcalaHelper {} - -export class ArrangeGroup { - helper: DevUniqueHelper; - - scheduledIdSlider = 0; - - constructor(helper: DevUniqueHelper) { - this.helper = helper; - } - - /** - * Generates accounts with the specified UNQ token balance - * @param balances balances for generated accounts. Each balance will be multiplied by the token nominal. - * @param donor donor account for balances - * @returns array of newly created accounts - * @example const [acc1, acc2, acc3] = await createAccounts([0n, 10n, 20n], donor); - */ - createAccounts = async (balances: bigint[], donor: IKeyringPair): Promise => { - let nonce = await this.helper.chain.getNonce(donor.address); - const wait = new WaitGroup(this.helper); - const ss58Format = this.helper.chain.getChainProperties().ss58Format; - const tokenNominal = this.helper.balance.getOneTokenNominal(); - const transactions = []; - const accounts: IKeyringPair[] = []; - for(const balance of balances) { - const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format); - accounts.push(recipient); - if(balance !== 0n) { - const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recipient.address}, balance * tokenNominal]); - transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation')); - nonce++; - } - } - - await Promise.all(transactions).catch(_e => {}); - - //#region TODO remove this region, when nonce problem will be solved - const checkBalances = async () => { - let isSuccess = true; - for(let i = 0; i < balances.length; i++) { - const balance = await this.helper.balance.getSubstrate(accounts[i].address); - if(balance !== balances[i] * tokenNominal) { - isSuccess = false; - break; - } - } - return isSuccess; - }; - - let accountsCreated = false; - const maxBlocksChecked = await this.helper.arrange.isDevNode() ? 50 : 5; - // checkBalances retry up to 5-50 blocks - for(let index = 0; index < maxBlocksChecked; index++) { - accountsCreated = await checkBalances(); - if(accountsCreated) break; - await wait.newBlocks(1); - } - - if(!accountsCreated) throw Error('Accounts generation failed'); - //#endregion - - return accounts; - }; - - // TODO combine this method and createAccounts into one - createCrowd = async (accountsToCreate: number, withBalance: bigint, donor: IKeyringPair): Promise => { - const createAsManyAsCan = async () => { - let transactions: any = []; - const accounts: IKeyringPair[] = []; - let nonce = await this.helper.chain.getNonce(donor.address); - const tokenNominal = this.helper.balance.getOneTokenNominal(); - const ss58Format = this.helper.chain.getChainProperties().ss58Format; - for(let i = 0; i < accountsToCreate; i++) { - if(i === 500) { // if there are too many accounts to create - await Promise.allSettled(transactions); // wait while first 500 (should be 100 for devnode) tx will be settled - transactions = []; // - nonce = await this.helper.chain.getNonce(donor.address); // update nonce - } - const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format); - accounts.push(recipient); - if(withBalance !== 0n) { - const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recipient.address}, withBalance * tokenNominal]); - transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation')); - nonce++; - } - } - - const fullfilledAccounts = []; - await Promise.allSettled(transactions); - for(const account of accounts) { - const accountBalance = await this.helper.balance.getSubstrate(account.address); - if(accountBalance === withBalance * tokenNominal) { - fullfilledAccounts.push(account); - } - } - return fullfilledAccounts; - }; - - - const crowd: IKeyringPair[] = []; - // do up to 5 retries - for(let index = 0; index < 5 && accountsToCreate !== 0; index++) { - const asManyAsCan = await createAsManyAsCan(); - crowd.push(...asManyAsCan); - accountsToCreate -= asManyAsCan.length; - } - - if(accountsToCreate !== 0) throw Error(`Crowd generation failed: ${accountsToCreate} accounts left`); - - return crowd; - }; - - /** - * Generates one account with zero balance - * @returns the newly generated account - * @example const account = await helper.arrange.createEmptyAccount(); - */ - createEmptyAccount = (): IKeyringPair => { - const ss58Format = this.helper.chain.getChainProperties().ss58Format; - return this.helper.util.fromSeed(mnemonicGenerate(), ss58Format); - }; - - isDevNode = async () => { - let blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON(); - if(blockNumber == 0) { - await this.helper.wait.newBlocks(1); - blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON(); - } - const block2 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])]); - const block1 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber - 1])]); - const findCreationDate = (block: any) => { - const humanBlock = block.toHuman(); - let date; - humanBlock.block.extrinsics.forEach((ext: any) => { - if(ext.method.section === 'timestamp') { - date = Number(ext.method.args.now.replaceAll(',', '')); - } - }); - return date; - }; - const block1date = await findCreationDate(block1); - const block2date = await findCreationDate(block2); - if(block2date! - block1date! < 9000) return true; - return false; - }; - - async calculcateFee(payer: ICrossAccountId, promise: () => Promise): Promise { - const address = 'Substrate' in payer ? payer.Substrate : this.helper.address.ethToSubstrate(payer.Ethereum); - let balance = await this.helper.balance.getSubstrate(address); - - await promise(); - - balance -= await this.helper.balance.getSubstrate(address); - - return balance; - } - - async calculatePoVInfo(txs: any[]): Promise { - const rawPovInfo = await this.helper.callRpc('api.rpc.povinfo.estimateExtrinsicPoV', [txs]); - - const kvJson: {[key: string]: string} = {}; - - for(const kv of rawPovInfo.keyValues) { - kvJson[kv.key.toHex()] = kv.value.toHex(); - } - - const kvStr = JSON.stringify(kvJson); - - const chainql = spawnSync( - 'chainql', - [ - `--tla-code=data=${kvStr}`, - '-e', `function(data) cql.dump(cql.chain("${this.helper.getEndpoint()}").latest._meta, data, {omit_empty:true})`, - ], - ); - - if(!chainql.stdout) { - throw Error('unable to get an output from the `chainql`'); - } - - return { - proofSize: rawPovInfo.proofSize.toNumber(), - compactProofSize: rawPovInfo.compactProofSize.toNumber(), - compressedProofSize: rawPovInfo.compressedProofSize.toNumber(), - results: rawPovInfo.results, - kv: JSON.parse(chainql.stdout.toString()), - }; - } - - calculatePalletAddress(palletId: any) { - const address = stringToU8a(('modl' + palletId).padEnd(32, '\0')); - return encodeAddress(address, this.helper.chain.getChainProperties().ss58Format); - } - - makeScheduledIds(num: number): string[] { - function makeId(slider: number) { - const scheduledIdSize = 64; - const hexId = slider.toString(16); - const prefixSize = scheduledIdSize - hexId.length; - - const scheduledId = '0x' + '0'.repeat(prefixSize) + hexId; - - return scheduledId; - } - - const ids = []; - for(let i = 0; i < num; i++) { - ids.push(makeId(this.scheduledIdSlider)); - this.scheduledIdSlider += 1; - } - - return ids; - } - - makeScheduledId(): string { - return (this.makeScheduledIds(1))[0]; - } - - async captureEvents(eventSection: string, eventMethod: string): Promise { - const capture = new EventCapture(this.helper, eventSection, eventMethod); - await capture.startCapture(); - - return capture; - } - - makeXcmProgramWithdrawDeposit(beneficiary: Uint8Array, id: any, amount: bigint) { - return { - V2: [ - { - WithdrawAsset: [ - { - id, - fun: { - Fungible: amount, - }, - }, - ], - }, - { - BuyExecution: { - fees: { - id, - fun: { - Fungible: amount, - }, - }, - weightLimit: 'Unlimited', - }, - }, - { - DepositAsset: { - assets: { - Wild: 'All', - }, - maxAssets: 1, - beneficiary: { - parents: 0, - interior: { - X1: { - AccountId32: { - network: 'Any', - id: beneficiary, - }, - }, - }, - }, - }, - }, - ], - }; - } - - makeXcmProgramReserveAssetDeposited(beneficiary: Uint8Array, id: any, amount: bigint) { - return { - V2: [ - { - ReserveAssetDeposited: [ - { - id, - fun: { - Fungible: amount, - }, - }, - ], - }, - { - BuyExecution: { - fees: { - id, - fun: { - Fungible: amount, - }, - }, - weightLimit: 'Unlimited', - }, - }, - { - DepositAsset: { - assets: { - Wild: 'All', - }, - maxAssets: 1, - beneficiary: { - parents: 0, - interior: { - X1: { - AccountId32: { - network: 'Any', - id: beneficiary, - }, - }, - }, - }, - }, - }, - ], - }; - } - - makeUnpaidSudoTransactProgram(info: {weightMultiplier: number, call: string}) { - return { - V3: [ - { - UnpaidExecution: { - weightLimit: 'Unlimited', - checkOrigin: null, - }, - }, - { - Transact: { - originKind: 'Superuser', - requireWeightAtMost: { - refTime: info.weightMultiplier * 200000000, - proofSize: info.weightMultiplier * 3000, - }, - call: { - encoded: info.call, - }, - }, - }, - ], - }; - } -} - -class MoonbeamAccountGroup { - helper: MoonbeamHelper; - - keyring: Keyring; - _alithAccount: IKeyringPair; - _baltatharAccount: IKeyringPair; - _dorothyAccount: IKeyringPair; - - constructor(helper: MoonbeamHelper) { - this.helper = helper; - - this.keyring = new Keyring({type: 'ethereum'}); - const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133'; - const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b'; - const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68'; - - this._alithAccount = this.keyring.addFromUri(alithPrivateKey, undefined, 'ethereum'); - this._baltatharAccount = this.keyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum'); - this._dorothyAccount = this.keyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum'); - } - - alithAccount() { - return this._alithAccount; - } - - baltatharAccount() { - return this._baltatharAccount; - } - - dorothyAccount() { - return this._dorothyAccount; - } - - create() { - return this.keyring.addFromUri(mnemonicGenerate()); - } -} - -class MoonbeamFastDemocracyGroup { - helper: DevMoonbeamHelper; - - constructor(helper: DevMoonbeamHelper) { - this.helper = helper; - } - - async executeProposal(proposalDesciption: string, encodedProposal: string) { - const proposalHash = blake2AsHex(encodedProposal); - - const alithAccount = this.helper.account.alithAccount(); - const baltatharAccount = this.helper.account.baltatharAccount(); - const dorothyAccount = this.helper.account.dorothyAccount(); - - const councilVotingThreshold = 2; - const technicalCommitteeThreshold = 2; - const fastTrackVotingPeriod = 3; - const fastTrackDelayPeriod = 0; - - console.log(`[democracy] executing '${proposalDesciption}' proposal`); - - // >>> Propose external motion through council >>> - console.log('\t* Propose external motion through council.......'); - const externalMotion = this.helper.democracy.externalProposeMajority({Inline: encodedProposal}); - const encodedMotion = externalMotion?.method.toHex() || ''; - const motionHash = blake2AsHex(encodedMotion); - console.log('\t* Motion hash is %s', motionHash); - - await this.helper.collective.council.propose( - baltatharAccount, - councilVotingThreshold, - externalMotion, - externalMotion.encodedLength, - ); - - const councilProposalIdx = await this.helper.collective.council.proposalCount() - 1; - await this.helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true); - await this.helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true); - - await this.helper.collective.council.close( - dorothyAccount, - motionHash, - councilProposalIdx, - { - refTime: 1_000_000_000, - proofSize: 1_000_000, - }, - externalMotion.encodedLength, - ); - console.log('\t* Propose external motion through council.......DONE'); - // <<< Propose external motion through council <<< - - // >>> Fast track proposal through technical committee >>> - console.log('\t* Fast track proposal through technical committee.......'); - const fastTrack = this.helper.democracy.fastTrack(proposalHash, fastTrackVotingPeriod, fastTrackDelayPeriod); - const encodedFastTrack = fastTrack?.method.toHex() || ''; - const fastTrackHash = blake2AsHex(encodedFastTrack); - console.log('\t* FastTrack hash is %s', fastTrackHash); - - await this.helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength); - - const techProposalIdx = await this.helper.collective.techCommittee.proposalCount() - 1; - await this.helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true); - await this.helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true); - - await this.helper.collective.techCommittee.close( - baltatharAccount, - fastTrackHash, - techProposalIdx, - { - refTime: 1_000_000_000, - proofSize: 1_000_000, - }, - fastTrack.encodedLength, - ); - console.log('\t* Fast track proposal through technical committee.......DONE'); - // <<< Fast track proposal through technical committee <<< - - const democracyStarted = await this.helper.wait.expectEvent(3, Event.Democracy.Started); - const referendumIndex = democracyStarted.referendumIndex; - - // >>> Referendum voting >>> - console.log(`\t* Referendum #${referendumIndex} voting.......`); - await this.helper.democracy.referendumVote(dorothyAccount, referendumIndex, { - balance: 10_000_000_000_000_000_000n, - vote: {aye: true, conviction: 1}, - }); - console.log(`\t* Referendum #${referendumIndex} voting.......DONE`); - // <<< Referendum voting <<< - - // Wait the proposal to pass - await this.helper.wait.expectEvent(3, Event.Democracy.Passed, event => event.referendumIndex == referendumIndex); - - await this.helper.wait.newBlocks(1); - - console.log(`[democracy] executing '${proposalDesciption}' proposal.......DONE`); - } -} - -class WaitGroup { - helper: ChainHelperBase; - - constructor(helper: ChainHelperBase) { - this.helper = helper; - } - - sleep(milliseconds: number) { - return new Promise((resolve) => setTimeout(resolve, milliseconds)); - } - - private async waitWithTimeout(promise: Promise, timeout: number) { - let isBlock = false; - promise.then(() => isBlock = true).catch(() => isBlock = true); - let totalTime = 0; - const step = 100; - while(!isBlock) { - await this.sleep(step); - totalTime += step; - if(totalTime >= timeout) throw Error('Blocks production failed'); - } - return promise; - } - - /** - * Launch some async operation, or throw an error after some time. Note that it will still continue executing after the timeout. - * @param promise async operation to race against the timeout - * @param timeoutMS time after which to time out - * @param timeoutError error message to throw - * @returns promise of the same type the operation had - */ - withTimeout( - promise: Promise, - timeoutMS = 30000, - timeoutError = 'The operation has timed out!', - ): Promise { - const timeout = new Promise((_, reject) => { - setTimeout(() => { - reject(new Error(timeoutError)); - }, timeoutMS); - }); - - return Promise.race([promise, timeout]).catch(e => {throw new Error(e);}); - } - - /** - * Wait for specified number of blocks - * @param blocksCount number of blocks to wait - * @returns - */ - async newBlocks(blocksCount = 1, timeout?: number): Promise { - timeout = timeout ?? blocksCount * 60_000; - // eslint-disable-next-line no-async-promise-executor - const promise = new Promise(async (resolve) => { - const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(() => { - if(blocksCount > 0) { - blocksCount--; - } else { - unsubscribe(); - resolve(); - } - }); - }); - await this.waitWithTimeout(promise, timeout); - return promise; - } - - /** - * Wait for the specified number of sessions to pass. - * Only applicable if the Session pallet is turned on. - * @param sessionCount number of sessions to wait - * @param blockTimeout time in ms until panicking that the chain has stopped producing blocks - * @returns - */ - async newSessions(sessionCount = 1, blockTimeout = 60000): Promise { - console.log(`Waiting for ${sessionCount} new session${sessionCount > 1 ? 's' : ''}.` - + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.'); - - const expectedSessionIndex = await (this.helper as DevUniqueHelper).session.getIndex() + sessionCount; - let currentSessionIndex = -1; - - while(currentSessionIndex < expectedSessionIndex) { - // eslint-disable-next-line no-async-promise-executor - currentSessionIndex = await this.withTimeout(new Promise(async (resolve) => { - await this.newBlocks(1); - const res = await (this.helper as DevUniqueHelper).session.getIndex(); - resolve(res); - }), blockTimeout, 'The chain has stopped producing blocks!'); - } - } - - async forParachainBlockNumber(blockNumber: bigint | number, timeout?: number) { - timeout = timeout ?? 30 * 60 * 1000; - // eslint-disable-next-line no-async-promise-executor - const promise = new Promise(async (resolve) => { - const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => { - if(data.number.toNumber() >= blockNumber) { - unsubscribe(); - resolve(); - } - }); - }); - await this.waitWithTimeout(promise, timeout); - return promise; - } - - async forRelayBlockNumber(blockNumber: bigint | number, timeout?: number) { - timeout = timeout ?? 30 * 60 * 1000; - // eslint-disable-next-line no-async-promise-executor - const promise = new Promise(async (resolve) => { - const unsubscribe = await this.helper.getApi().query.parachainSystem.validationData((data: any) => { - if(data.value.relayParentNumber.toNumber() >= blockNumber) { - // @ts-ignore - unsubscribe(); - resolve(); - } - }); - }); - await this.waitWithTimeout(promise, timeout); - return promise; - } - - noScheduledTasks() { - const api = this.helper.getApi(); - - // eslint-disable-next-line no-async-promise-executor - const promise = new Promise(async resolve => { - const unsubscribe = await api.rpc.chain.subscribeNewHeads(async () => { - const areThereScheduledTasks = await api.query.scheduler.lookup.entries(); - - if(areThereScheduledTasks.length == 0) { - unsubscribe(); - resolve(); - } - }); - }); - - return promise; - } - - parachainBlockMultiplesOf(val: bigint) { - // eslint-disable-next-line no-async-promise-executor - const promise = new Promise(async resolve => { - const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => { - if(data.number.toBigInt() % val == 0n) { - console.log(`from waiter: ${data.number.toBigInt()}`); - unsubscribe(); - resolve(); - } - }); - }); - return promise; - } - - event( - maxBlocksToWait: number, - eventHelper: T, - filter: (_: any) => boolean = () => true, - ): any { - // eslint-disable-next-line no-async-promise-executor - const promise = new Promise(async (resolve) => { - const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(async header => { - const blockNumber = header.number.toJSON(); - const blockHash = header.hash; - const eventIdStr = `${eventHelper.section()}.${eventHelper.method()}`; - const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`; - - this.helper.logger.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`); - - const apiAt = await this.helper.getApi().at(blockHash); - const eventRecords = (await apiAt.query.system.events()) as any; - - const neededEvent = eventRecords.toArray() - .filter((r: FrameSystemEventRecord) => r.event.section == eventHelper.section() && r.event.method == eventHelper.method()) - .map((r: FrameSystemEventRecord) => eventHelper.wrapEvent(r.event.data)) - .find(filter); - - if(neededEvent) { - unsubscribe(); - resolve(neededEvent); - } else if(maxBlocksToWait > 0) { - maxBlocksToWait--; - } else { - this.helper.logger.log(`Eligible event \`${eventIdStr}\` is NOT found. - The wait lasted until block ${blockNumber} inclusive`); - unsubscribe(); - resolve(null); - } - }); - }); - return promise; - } - - async expectEvent( - maxBlocksToWait: number, - eventHelper: T, - filter: (e: any) => boolean = () => true, - ) { - const e = await this.event(maxBlocksToWait, eventHelper, filter); - if(e == null) { - throw Error(`The event '${eventHelper.section()}.${eventHelper.method()}' is expected`); - } else { - return e; - } - } -} - -class SessionGroup { - helper: ChainHelperBase; - - constructor(helper: ChainHelperBase) { - this.helper = helper; - } - - //todo:collator documentation - async getIndex(): Promise { - return (await this.helper.callRpc('api.query.session.currentIndex', [])).toNumber(); - } - - newSessions(sessionCount = 1, blockTimeout = 24000): Promise { - return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout); - } - - setOwnKeys(signer: TSigner, key: string) { - return this.helper.executeExtrinsic( - signer, - 'api.tx.session.setKeys', - [key, '0x0'], - true, - ); - } - - setOwnKeysFromAddress(signer: TSigner) { - return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex')); - } -} - -class TestUtilGroup { - helper: DevUniqueHelper; - - constructor(helper: DevUniqueHelper) { - this.helper = helper; - } - - async enable(testUtilsPalletName: string) { - if(this.helper.fetchMissingPalletNames([testUtilsPalletName]).length != 0) { - return; - } - - const signer = this.helper.util.fromSeed('//Alice'); - await this.helper.getSudo().executeExtrinsic(signer, 'api.tx.testUtils.enable', [], true); - } - - async setTestValue(signer: TSigner, testVal: number) { - await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValue', [testVal], true); - } - - async incTestValue(signer: TSigner) { - await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.incTestValue', [], true); - } - - async setTestValueAndRollback(signer: TSigner, testVal: number) { - await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValueAndRollback', [testVal], true); - } - - async testValue(blockIdx?: number) { - const api = blockIdx - ? await this.helper.getApi().at(await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockIdx])) - : this.helper.getApi(); - - return (await api.query.testUtils.testValue()).toJSON(); - } - - async justTakeFee(signer: TSigner) { - await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.justTakeFee', [], true); - } - - async selfCancelingInc(signer: TSigner, scheduledId: string, maxTestVal: number) { - await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.selfCancelingInc', [scheduledId, maxTestVal], true); - } -} - -class EventCapture { - helper: DevUniqueHelper; - eventSection: string; - eventMethod: string; - events: EventRecord[] = []; - unsubscribe: VoidFn | null = null; - - constructor( - helper: DevUniqueHelper, - eventSection: string, - eventMethod: string, - ) { - this.helper = helper; - this.eventSection = eventSection; - this.eventMethod = eventMethod; - } - - async startCapture() { - this.stopCapture(); - this.unsubscribe = (await this.helper.getApi().query.system.events((eventRecords: FrameSystemEventRecord[]) => { - const newEvents = eventRecords.filter(r => r.event.section == this.eventSection && r.event.method == this.eventMethod); - - this.events.push(...newEvents); - })) as any; - } - - stopCapture() { - if(this.unsubscribe !== null) { - this.unsubscribe(); - } - } - - extractCapturedEvents() { - return this.events; - } -} - -class AdminGroup { - helper: UniqueHelper; - - constructor(helper: UniqueHelper) { - this.helper = helper; - } - - async payoutStakers(signer: IKeyringPair, stakersToPayout: number): Promise<{staker: string, stake: bigint, payout: bigint}[]> { - const payoutResult = await this.helper.executeExtrinsic(signer, 'api.tx.appPromotion.payoutStakers', [stakersToPayout], true); - return payoutResult.result.events.filter(e => e.event.method === 'StakingRecalculation').map(e => ({ - staker: e.event.data[0].toString(), - stake: e.event.data[1].toBigInt(), - payout: e.event.data[2].toBigInt(), - })); - } -} - -// eslint-disable-next-line @typescript-eslint/naming-convention -function ScheduledUniqueHelper(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; - } - - override executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise { - 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, - ); - } - }; -} --- a/js-packages/playgrounds/src/unique.governance.ts +++ /dev/null @@ -1,531 +0,0 @@ -import {blake2AsHex} from '@polkadot/util-crypto'; -import type {PalletDemocracyConviction} from '@polkadot/types/lookup'; -import type {IPhasicEvent, TSigner} from './types.js'; -import {HelperGroup, UniqueHelper} from './unique.js'; - -export class CollectiveGroup extends HelperGroup { - /** - * 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]); - } -} - -export class CollectiveMembershipGroup extends HelperGroup { - /** - * 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`, []); - } -} - -export class RankedCollectiveGroup extends HelperGroup { - /** - * 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; - } -} - -export class ReferendaGroup extends HelperGroup { - /** - * 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; -} - -export class DemocracyGroup extends HelperGroup { - // 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); - }*/ -} --- a/js-packages/playgrounds/src/unique.ts +++ /dev/null @@ -1,3498 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable function-call-argument-newline */ -/* eslint-disable no-prototype-builtins */ - -import {ApiPromise, WsProvider, Keyring} from '@polkadot/api'; -import type {SignerOptions} from '@polkadot/api/types'; -import type {AugmentedSubmittables} from '@polkadot/api-base/types/submittable'; -import type {ApiInterfaceEvents} from '@polkadot/api/types'; -import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {hexToU8a} from '@polkadot/util/hex'; -import {u8aConcat} from '@polkadot/util/u8a'; -import type { - IApiListeners, - IBlock, - IEvent, - IChainProperties, - ICollectionCreationOptions, - ICollectionLimits, - ICollectionPermissions, - ICrossAccountId, - ICrossAccountIdLower, - ILogger, - INestingPermissions, - IProperty, - IStakingInfo, - ISubstrateBalance, - IToken, - ITokenPropertyPermission, - ITransactionResult, - IUniqueHelperLog, - TApiAllowedListeners, - TEthereumAccount, - TSigner, - TSubstrateAccount, - TNetworks, - IEthCrossAccountId, -} from './types.js'; -import type {RuntimeDispatchInfo} from '@polkadot/types/interfaces'; - -export class CrossAccountId { - account: ICrossAccountId; - - constructor(account: ICrossAccountId) { - this.account = account; - } - - static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') { - switch (domain) { - case 'Substrate': return new CrossAccountId({Substrate: account.address}); - case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum(); - } - } - - static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId { - if('substrate' in address) return new CrossAccountId({Substrate: address.substrate}); - else return new CrossAccountId({Ethereum: address.ethereum}); - } - - static normalizeSubstrateAddress(address: {Substrate: TSubstrateAccount}, ss58Format = 42): TSubstrateAccount { - return encodeAddress(decodeAddress(address.Substrate), ss58Format); - } - - static withNormalizedSubstrate(address: ICrossAccountId, ss58Format = 42): ICrossAccountId { - if('Substrate' in address) return {Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)}; - return address; - } - - withNormalizedSubstrate(ss58Format = 42): CrossAccountId { - if('Substrate' in this.account) this.account = CrossAccountId.withNormalizedSubstrate(this.account, ss58Format); - return this; - } - - static translateSubToEth(address: TSubstrateAccount): TEthereumAccount { - return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join('')); - } - - toEthereum(): CrossAccountId { - this.account = CrossAccountId.toEthereum(this.account); - return this; - } - - static toEthereum(account: ICrossAccountId): ICrossAccountId { - if('Substrate' in account) return {Ethereum: CrossAccountId.translateSubToEth(account.Substrate)}; - return account; - } - - static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount { - return evmToAddress(address, ss58Format); - } - - toSubstrate(ss58Format?: number): CrossAccountId { - this.account = CrossAccountId.toSubstrate(this.account, ss58Format); - return this; - } - - static toSubstrate(account: ICrossAccountId, ss58Format?: number): ICrossAccountId { - if('Ethereum' in account) return {Substrate: CrossAccountId.translateEthToSub(account.Ethereum, ss58Format)}; - return account; - } - - toLowerCase(): CrossAccountId { - this.account = CrossAccountId.toLowerCase(this.account); - return this; - } - - static toLowerCase(account: ICrossAccountId) { - if('Substrate' in account) return {Substrate: account.Substrate.toLowerCase()}; - if('Ethereum' in account) return {Ethereum: account.Ethereum.toLowerCase()}; - return account; - } - - toICrossAccountId(): ICrossAccountId { - return this.account; - } -} - -const nesting = { - toChecksumAddress(address: string): string { - if(typeof address === 'undefined') return ''; - - if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`); - - address = address.toLowerCase().replace(/^0x/i, ''); - const addressHash = keccakAsHex(address).replace(/^0x/i, ''); - const checksumAddress = ['0x']; - - for(let i = 0; i < address.length; i++) { - // If ith character is 8 to f then make it uppercase - if(parseInt(addressHash[i], 16) > 7) { - checksumAddress.push(address[i].toUpperCase()); - } else { - checksumAddress.push(address[i]); - } - } - return checksumAddress.join(''); - }, - tokenIdToAddress(collectionId: number, tokenId: number) { - return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`); - }, -}; - -class UniqueUtil { - static transactionStatus = { - NOT_READY: 'NotReady', - FAIL: 'Fail', - SUCCESS: 'Success', - }; - - static chainLogType = { - EXTRINSIC: 'extrinsic', - RPC: 'rpc', - }; - - static getTokenAccount(token: IToken): ICrossAccountId { - return {Ethereum: this.getTokenAddress(token)}; - } - - static getTokenAddress(token: IToken): string { - return nesting.tokenIdToAddress(token.collectionId, token.tokenId); - } - - static getDefaultLogger(): ILogger { - return { - log(msg: any, level = 'INFO') { - console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg])); - }, - level: { - ERROR: 'ERROR', - WARNING: 'WARNING', - INFO: 'INFO', - }, - }; - } - - static vec2str(arr: string[] | number[]) { - return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join(''); - } - - static str2vec(string: string) { - if(typeof string !== 'string') return string; - return Array.from(string).map(x => x.charCodeAt(0)); - } - - static fromSeed(seed: string, ss58Format = 42) { - const keyring = new Keyring({type: 'sr25519', ss58Format}); - return keyring.addFromUri(seed); - } - - static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number { - if(creationResult.status !== this.transactionStatus.SUCCESS) { - throw Error('Unable to create collection!'); - } - - let collectionId = null; - creationResult.result.events.forEach(({event: {data, method, section}}) => { - if((section === 'common') && (method === 'CollectionCreated')) { - collectionId = parseInt(data[0].toString(), 10); - } - }); - - if(collectionId === null) { - throw Error('No CollectionCreated event was found!'); - } - - return collectionId; - } - - static extractTokensFromCreationResult(creationResult: ITransactionResult): { - success: boolean, - tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[], - } { - if(creationResult.status !== this.transactionStatus.SUCCESS) { - throw Error('Unable to create tokens!'); - } - let success = false; - const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[]; - creationResult.result.events.forEach(({event: {data, method, section}}) => { - if(method === 'ExtrinsicSuccess') { - success = true; - } else if((section === 'common') && (method === 'ItemCreated')) { - tokens.push({ - collectionId: parseInt(data[0].toString(), 10), - tokenId: parseInt(data[1].toString(), 10), - owner: data[2].toHuman(), - amount: data[3].toBigInt(), - }); - } - }); - return {success, tokens}; - } - - static extractTokensFromBurnResult(burnResult: ITransactionResult): { - success: boolean, - tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[], - } { - if(burnResult.status !== this.transactionStatus.SUCCESS) { - throw Error('Unable to burn tokens!'); - } - let success = false; - const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[]; - burnResult.result.events.forEach(({event: {data, method, section}}: any) => { - if(method === 'ExtrinsicSuccess') { - success = true; - } else if((section === 'common') && (method === 'ItemDestroyed')) { - tokens.push({ - collectionId: parseInt(data[0].toString(), 10), - tokenId: parseInt(data[1].toString(), 10), - owner: data[2].toHuman(), - amount: data[3].toBigInt(), - }); - } - }); - return {success, tokens}; - } - - static findCollectionInEvents(events: { event: IEvent }[], collectionId: number, expectedSection: string, expectedMethod: string): boolean { - let eventId = null; - events.forEach(({event: {data, method, section}}) => { - if((section === expectedSection) && (method === expectedMethod)) { - eventId = parseInt(data[0].toString(), 10); - } - }); - - if(eventId === null) { - throw Error(`No ${expectedMethod} event was found!`); - } - return eventId === collectionId; - } - - static isTokenTransferSuccess(events: { event: IEvent }[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { - const normalizeAddress = (address: string | ICrossAccountId) => { - if(typeof address === 'string') return address; - if('Substrate' in address) return CrossAccountId.withNormalizedSubstrate(address); - if('Ethereum' in address) return CrossAccountId.toLowerCase(address); - return address; - }; - let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any; - events.forEach(({event: {data, method, section}}) => { - if((section === 'common') && (method === 'Transfer')) { - transfer = { - collectionId: data[0].toJSON(), - tokenId: data[1].toJSON(), - from: normalizeAddress(data[2].toHuman()), - to: normalizeAddress(data[3].toHuman()), - amount: BigInt(data[4].toJSON()), - }; - } - }); - let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId; - isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from); - isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to); - isSuccess = isSuccess && amount === transfer.amount; - return isSuccess; - } - - static bigIntToDecimals(number: bigint, decimals = 18) { - const numberStr = number.toString(); - const dotPos = numberStr.length - decimals; - - if(dotPos <= 0) { - return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr; - } else { - const intPart = numberStr.substring(0, dotPos); - const fractPart = numberStr.substring(dotPos); - return intPart + '.' + fractPart; - } - } -} - -class UniqueEventHelper { - static extractIndex(index: any): [number, number] | string { - if(index.toRawType() === '[u8;2]') return [index[0], index[1]]; - return index.toJSON(); - } - - static extractSub(data: any, subTypes: any): { [key: string]: any } { - let obj: any = {}; - let index = 0; - - if(data.entries) { - for(const [key, value] of data.entries()) { - obj[key] = this.extractData(value, subTypes[index]); - index++; - } - } else obj = data.toJSON(); - - return obj; - } - - static toHuman(data: any) { - return data && data.toHuman ? data.toHuman() : `${data}`; - } - - static extractData(data: any, type: any): any { - if(!type) return this.toHuman(data); - if(['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber(); - if(['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt(); - if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub); - return this.toHuman(data); - } - - public static extractEvents(events: { event: any, phase: any }[]): IEvent[] { - const parsedEvents: IEvent[] = []; - - events.forEach((record) => { - const {event, phase} = record; - const types = event.typeDef; - - const eventData: IEvent = { - section: event.section.toString(), - method: event.method.toString(), - index: this.extractIndex(event.index), - data: [], - phase: phase.toJSON(), - }; - - event.data.forEach((val: any, index: number) => { - eventData.data.push(this.extractData(val, types[index])); - }); - - parsedEvents.push(eventData); - }); - - return parsedEvents; - } -} -const InvalidTypeSymbol = Symbol('Invalid type'); -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export type Invalid = - | (( - invalidType: typeof InvalidTypeSymbol, - ..._: typeof InvalidTypeSymbol[] - ) => typeof InvalidTypeSymbol) - | null - | undefined; -// Has slightly better error messages than Get -type Get2 = - P extends `${infer Key}.${infer Key2}` ? Key extends keyof T ? Key2 extends keyof T[Key] ? T[Key][Key2] : E : E : E; -type ForceFunction = T extends (...args: any) => any ? T : (...args: any) => Invalid; - -export class ChainHelperBase { - helperBase: any; - - transactionStatus = UniqueUtil.transactionStatus; - chainLogType = UniqueUtil.chainLogType; - util: typeof UniqueUtil; - eventHelper: typeof UniqueEventHelper; - logger: ILogger; - api: ApiPromise | null; - forcedNetwork: TNetworks | null; - network: TNetworks | null; - wsEndpoint: string | null; - chainLog: IUniqueHelperLog[]; - children: ChainHelperBase[]; - address: AddressGroup; - chain: ChainGroup; - - constructor(logger?: ILogger, helperBase?: any) { - this.helperBase = helperBase; - - this.util = UniqueUtil; - this.eventHelper = UniqueEventHelper; - if(typeof logger == 'undefined') logger = this.util.getDefaultLogger(); - this.logger = logger; - this.api = null; - this.forcedNetwork = null; - this.network = null; - this.wsEndpoint = null; - this.chainLog = []; - this.children = []; - this.address = new AddressGroup(this); - this.chain = new ChainGroup(this); - } - - clone(helperCls: ChainHelperBaseConstructor, options: { [key: string]: any } = {}) { - Object.setPrototypeOf(helperCls.prototype, this); - const newHelper = new helperCls(this.logger, options); - - newHelper.api = this.api; - newHelper.network = this.network; - newHelper.forceNetwork = this.forceNetwork; - - this.children.push(newHelper); - - return newHelper; - } - - getEndpoint(): string { - if(this.wsEndpoint === null) throw Error('No connection was established'); - return this.wsEndpoint; - } - - getApi(): ApiPromise { - if(this.api === null) throw Error('API not initialized'); - return this.api; - } - - async subscribeEvents(expectedEvents: { section: string, names: string[] }[]) { - const collectedEvents: IEvent[] = []; - const unsubscribe = await this.getApi().query.system.events((events: any) => { - const ievents = this.eventHelper.extractEvents(events); - ievents.forEach((event) => { - expectedEvents.forEach((e => { - if(event.section === e.section && e.names.includes(event.method)) { - collectedEvents.push(event); - } - })); - }); - }); - return {unsubscribe: unsubscribe as any, collectedEvents}; - } - - clearChainLog(): void { - this.chainLog = []; - } - - forceNetwork(value: TNetworks): void { - this.forcedNetwork = value; - } - - async connect(wsEndpoint: string, listeners?: IApiListeners) { - if(this.api !== null) throw Error('Already connected'); - const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork); - this.wsEndpoint = wsEndpoint; - this.api = api; - this.network = network; - } - - async disconnect() { - for(const child of this.children) { - child.clearApi(); - } - - if(this.api === null) return; - await this.api.disconnect(); - this.clearApi(); - } - - clearApi() { - this.api = null; - this.network = null; - } - - static async detectNetwork(api: ApiPromise): Promise { - const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any; - const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver']; - - if(xcmChains.indexOf(spec.specName) > -1) return spec.specName; - - if(['quartz', 'unique', 'sapphire'].indexOf(spec.specName) > -1) return spec.specName; - return 'opal'; - } - - static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise { - if(!wsEndpoint) throw new Error('wsEndpoint was not set'); - const api = new ApiPromise({provider: new WsProvider(wsEndpoint)}); - await api.isReady; - - const network = await this.detectNetwork(api); - - await api.disconnect(); - - return network; - } - - static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{ - api: ApiPromise; - network: TNetworks; - }> { - if(typeof network === 'undefined' || network === null) network = 'opal'; - if(!wsEndpoint) throw new Error('wsEndpoint was not set'); - const supportedRPC = { - opal: { - unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc, - }, - quartz: { - unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc, - }, - unique: { - unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc, - }, - rococo: {}, - westend: {}, - moonbeam: {}, - moonriver: {}, - acala: {}, - karura: {}, - westmint: {}, - }; - if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint); - const rpc = supportedRPC[network] as any; - - // TODO: investigate how to replace rpc in runtime - // api._rpcCore.addUserInterfaces(rpc); - - const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc}); - - await api.isReadyOrError; - - if(typeof listeners === 'undefined') listeners = {}; - for(const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) { - if(!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue; - api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any); - } - - return {api, network}; - } - - getTransactionStatus(data: { events: { event: IEvent }[], status: any }) { - const {events, status} = data; - if(status.isReady) { - return this.transactionStatus.NOT_READY; - } - if(status.isBroadcast) { - return this.transactionStatus.NOT_READY; - } - if(status.isInBlock || status.isFinalized) { - const errors = events.filter(e => e.event.method === 'ExtrinsicFailed'); - if(errors.length > 0) { - return this.transactionStatus.FAIL; - } - if(events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) { - return this.transactionStatus.SUCCESS; - } - } - - return this.transactionStatus.FAIL; - } - - signTransaction(sender: TSigner, transaction: any, options: Partial | null = null, label = 'transaction') { - const sign = (callback: any) => { - if(options !== null) return transaction.signAndSend(sender, options, callback); - return transaction.signAndSend(sender, callback); - }; - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - try { - const unsub = await sign((result: any) => { - const status = this.getTransactionStatus(result); - - if(status === this.transactionStatus.SUCCESS) { - this.logger.log(`${label} successful`); - unsub(); - resolve({result, status, blockHash: result.status.asInBlock.toHuman()}); - } else if(status === this.transactionStatus.FAIL) { - let moduleError = null; - - if(result.hasOwnProperty('dispatchError')) { - const dispatchError = result['dispatchError']; - - if(dispatchError) { - if(dispatchError.isModule) { - const modErr = dispatchError.asModule; - const errorMeta = dispatchError.registry.findMetaError(modErr); - - moduleError = `${errorMeta.section}.${errorMeta.name}`; - } else if(dispatchError.isToken) { - moduleError = `Token: ${dispatchError.asToken}`; - } else { - // May be [object Object] in case of unhandled non-unit enum - moduleError = `Misc: ${dispatchError.toHuman()}`; - } - } else { - this.logger.log(result, this.logger.level.ERROR); - } - } - - this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR); - unsub(); - reject({status, moduleError, result}); - } - }); - } catch (e) { - this.logger.log(e, this.logger.level.ERROR); - reject(e); - } - }); - } - - async signTransactionWithoutSending(signer: TSigner, tx: any) { - const api = this.getApi(); - const signingInfo = await api.derive.tx.signingInfo(signer.address); - - tx.sign(signer, { - blockHash: api.genesisHash, - genesisHash: api.genesisHash, - runtimeVersion: api.runtimeVersion, - nonce: signingInfo.nonce, - }); - - return tx.toHex(); - } - - async getPaymentInfo(signer: TSigner, tx: any, len: number | null) { - const api = this.getApi(); - const signingInfo = await api.derive.tx.signingInfo(signer.address); - - // We need to sign the tx because - // unsigned transactions does not have an inclusion fee - tx.sign(signer, { - blockHash: api.genesisHash, - genesisHash: api.genesisHash, - runtimeVersion: api.runtimeVersion, - nonce: signingInfo.nonce, - }); - - if(len === null) { - return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo; - } else { - return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo; - } - } - - constructApiCall(apiCall: string, params: any[]) { - if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`); - let call = this.getApi() as any; - for(const part of apiCall.slice(4).split('.')) { - call = call[part]; - if(!call) { - const advice = part.includes('_') ? ' Looks like it needs to be converted to camel case.' : ''; - throw Error(`Function ${part} of api call ${apiCall} not found.${advice}`); - } - } - return call(...params); - } - - encodeApiCall(apiCall: string, params: any[]) { - return this.constructApiCall(apiCall, params).method.toHex(); - } - - async executeExtrinsic< - E extends string, - V extends ( - ...args: any) => any = ForceFunction< - Get2< - AugmentedSubmittables<'promise'>, - E, (...args: any) => Invalid - > - > - >( - sender: TSigner, - extrinsic: `api.tx.${E}`, - params: Parameters, - expectSuccess = true, - options: Partial | null = null,/*, failureMessage='expected success'*/ - ): Promise { - if(this.api === null) throw Error('API not initialized'); - - const startTime = (new Date()).getTime(); - let result: ITransactionResult; - let events: IEvent[] = []; - try { - result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult; - events = this.eventHelper.extractEvents(result.result.events); - const errorEvent = events.find((event) => event.method == 'ExecutedFailed' || event.method == 'CreatedFailed'); - if(errorEvent) - throw Error(errorEvent.method + ': ' + extrinsic); - } - catch (e) { - if(!(e as object).hasOwnProperty('status')) throw e; - result = e as ITransactionResult; - } - - const endTime = (new Date()).getTime(); - - const log = { - executedAt: endTime, - executionTime: endTime - startTime, - type: this.chainLogType.EXTRINSIC, - status: result.status, - call: extrinsic, - signer: this.getSignerAddress(sender), - params, - } as IUniqueHelperLog; - - let errorMessage = ''; - - if(result.status !== this.transactionStatus.SUCCESS) { - if(result.moduleError) { - errorMessage = typeof result.moduleError === 'string' - ? result.moduleError - : `${Object.keys(result.moduleError)[0]}: ${Object.values(result.moduleError)[0]}`; - log.moduleError = errorMessage; - } - else if(result.result.dispatchError) log.dispatchError = result.result.dispatchError; - } - if(events.length > 0) log.events = events; - - this.chainLog.push(log); - - if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) { - if(result.moduleError) throw Error(`${errorMessage}`); - else if(result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError)); - } - return result as any; - } - executeExtrinsicUncheckedWeight< - E extends string, - V extends ( - ...args: any) => any = ForceFunction< - Get2< - AugmentedSubmittables<'promise'>, - E, (...args: any) => Invalid - > - > - >( - _sender: TSigner, - _extrinsic: `api.tx.${E}`, - _params: Parameters, - _expectSuccess = true, - _options: Partial | null = null,/*, failureMessage='expected success'*/ - ): Promise { - throw new Error('executeExtrinsicUncheckedWeight only supported in sudo'); - } - - async callRpc - // TODO: make it strongly typed, or use api.query/api.rpc directly - // < - // K extends 'rpc' | 'query', - // E extends string, - // V extends (...args: any) => any = ForceFunction< - // Get2< - // K extends 'rpc' ? DecoratedRpc<'promise', RpcInterface> : QueryableStorage<'promise'>, - // E, (...args: any) => Invalid<'not found'> - // > - // >, - // P = Parameters, - // > - (rpc: string, params?: any[]): Promise { - - if(typeof params === 'undefined') params = [] as any; - if(this.api === null) throw Error('API not initialized'); - if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`); - - const startTime = (new Date()).getTime(); - let result; - let error = null; - const log = { - type: this.chainLogType.RPC, - call: rpc, - params, - } as any as IUniqueHelperLog; - - try { - result = await this.constructApiCall(rpc, params as any); - } - catch (e) { - error = e; - } - - const endTime = (new Date()).getTime(); - - log.executedAt = endTime; - log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success'; - log.executionTime = endTime - startTime; - - this.chainLog.push(log); - - if(error !== null) throw error; - - return result; - } - - getSignerAddress(signer: IKeyringPair | string): string { - if(typeof signer === 'string') return signer; - return signer.address; - } - - fetchAllPalletNames(): string[] { - if(this.api === null) throw Error('API not initialized'); - return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase()).sort(); - } - - fetchMissingPalletNames(requiredPallets: readonly string[]): string[] { - const palletNames = this.fetchAllPalletNames(); - return requiredPallets.filter(p => !palletNames.includes(p)); - } -} - - -export class HelperGroup { - helper: T; - - constructor(uniqueHelper: T) { - this.helper = uniqueHelper; - } -} - - -class CollectionGroup extends HelperGroup { - /** - * Get number of blocks when sponsored transaction is available. - * - * @param collectionId ID of collection - * @param tokenId ID of token - * @param addressObj address for which the sponsorship is checked - * @example await getTokenNextSponsored(1, 2, {Substrate: '5DfhbVfww7ThF8q6f3...'}); - * @returns number of blocks or null if sponsorship hasn't been set - */ - async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise { - return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON(); - } - - /** - * Get the number of created collections. - * - * @returns number of created collections - */ - async getTotalCount(): Promise { - return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber(); - } - - /** - * Get information about the collection with additional data, - * including the number of tokens it contains, its administrators, - * the normalized address of the collection's owner, and decoded name and description. - * - * @param collectionId ID of collection - * @example await getData(2) - * @returns collection information object - */ - async getData(collectionId: number): Promise<{ - id: number; - name: string; - description: string; - tokensCount: number; - admins: CrossAccountId[]; - normalizedOwner: TSubstrateAccount; - raw: any - } | null> { - const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]); - const humanCollection = collection.toHuman(), collectionData = { - id: collectionId, name: null, description: null, tokensCount: 0, admins: [], - raw: humanCollection, - } as any, jsonCollection = collection.toJSON(); - if(humanCollection === null) return null; - collectionData.raw.limits = jsonCollection.limits; - collectionData.raw.permissions = jsonCollection.permissions; - collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner); - for(const key of ['name', 'description']) { - collectionData[key] = this.helper.util.vec2str(humanCollection[key]); - } - - collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode)) - ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId) - : 0; - collectionData.admins = await this.getAdmins(collectionId); - - return collectionData; - } - - /** - * Get the addresses of the collection's administrators, optionally normalized. - * - * @param collectionId ID of collection - * @param normalize whether to normalize the addresses to the default ss58 format - * @example await getAdmins(1) - * @returns array of administrators - */ - async getAdmins(collectionId: number, normalize = false): Promise { - const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman() as ICrossAccountId[]; - - return normalize - ? admins.map(address => CrossAccountId.withNormalizedSubstrate(address)) - : admins; - } - - /** - * Get the addresses added to the collection allow-list, optionally normalized. - * @param collectionId ID of collection - * @param normalize whether to normalize the addresses to the default ss58 format - * @example await getAllowList(1) - * @returns array of allow-listed addresses - */ - async getAllowList(collectionId: number, normalize = false): Promise { - const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman() as ICrossAccountId[]; - return normalize - ? allowListed.map(address => CrossAccountId.withNormalizedSubstrate(address)) - : allowListed; - } - - /** - * Get the effective limits of the collection instead of null for default values - * - * @param collectionId ID of collection - * @example await getEffectiveLimits(2) - * @returns object of collection limits - */ - async getEffectiveLimits(collectionId: number): Promise { - return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON(); - } - - /** - * Burns the collection if the signer has sufficient permissions and collection is empty. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @example await helper.collection.burn(aliceKeyring, 3); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async burn(signer: TSigner, collectionId: number): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.destroyCollection', [collectionId], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed'); - } - - /** - * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param sponsorAddress Sponsor substrate address - * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...") - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet'); - } - - /** - * Confirms consent to sponsor the collection on behalf of the signer. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @example confirmSponsorship(aliceKeyring, 10) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async confirmSponsorship(signer: TSigner, collectionId: number): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.confirmSponsorship', [collectionId], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed'); - } - - /** - * Removes the sponsor of a collection, regardless if it consented or not. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @example removeSponsor(aliceKeyring, 10) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async removeSponsor(signer: TSigner, collectionId: number): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.removeCollectionSponsor', [collectionId], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved'); - } - - /** - * Sets the limits of the collection. At least one limit must be specified for a correct call. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param limits collection limits object - * @example - * await setLimits( - * aliceKeyring, - * 10, - * { - * sponsorTransferTimeout: 0, - * ownerCanDestroy: false - * } - * ) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.setCollectionLimits', [collectionId, limits], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet'); - } - - /** - * Changes the owner of the collection to the new Substrate address. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param ownerAddress substrate address of new owner - * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...") - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnerChanged'); - } - - /** - * Adds a collection administrator. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param adminAddressObj Administrator address (substrate or ethereum) - * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded'); - } - - /** - * Removes a collection administrator. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param adminAddressObj Administrator address (substrate or ethereum) - * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved'); - } - - /** - * Check if user is in allow list. - * - * @param collectionId ID of collection - * @param user Account to check - * @example await getAdmins(1) - * @returns is user in allow list - */ - async allowed(collectionId: number, user: ICrossAccountId): Promise { - return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON(); - } - - /** - * Adds an address to allow list - * @param signer keyring of signer - * @param collectionId ID of collection - * @param addressObj address to add to the allow list - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.addToAllowList', [collectionId, addressObj], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded'); - } - - /** - * Removes an address from allow list - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param addressObj address to remove from the allow list - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.removeFromAllowList', [collectionId, addressObj], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved'); - } - - /** - * Sets onchain permissions for selected collection. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param permissions collection permissions object - * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}}); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.setCollectionPermissions', [collectionId, permissions], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet'); - } - - /** - * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param permissions nesting permissions object - * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true}); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise { - return await this.setPermissions(signer, collectionId, {nesting: permissions}); - } - - /** - * Disables nesting for selected collection. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @example disableNesting(aliceKeyring, 10); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async disableNesting(signer: TSigner, collectionId: number): Promise { - return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}}); - } - - /** - * Sets onchain properties to the collection. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param properties array of property objects - * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.setCollectionProperties', [collectionId, properties], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet'); - } - - /** - * Get collection properties. - * - * @param collectionId ID of collection - * @param propertyKeys optionally filter the returned properties to only these keys - * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']); - * @returns array of key-value pairs - */ - async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise { - return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman(); - } - - async getPropertiesConsumedSpace(collectionId: number): Promise { - const api = this.helper.getApi(); - const props = (await api.query.common.collectionProperties(collectionId)).toJSON(); - - return (props! as any).consumedSpace; - } - - async getCollectionOptions(collectionId: number) { - return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman(); - } - - /** - * Deletes onchain properties from the collection. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param propertyKeys array of property keys to delete - * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted'); - } - - /** - * Changes the owner of the token. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param addressObj address of a new owner - * @param amount amount of tokens to be transfered. For NFT must be set to 1n - * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}) - * @returns true if the token success, otherwise false - */ - async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount], - true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`, - ); - - return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount); - } - - /** - * - * Change ownership of a token(s) on behalf of the owner. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param fromAddressObj address on behalf of which the token will be sent - * @param toAddressObj new token owner - * @param amount amount of tokens to be transfered. For NFT must be set to 1n - * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg"}, {Ethereum: "0x9F0583DbB85..."}) - * @returns true if the token success, otherwise false - */ - async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount], - true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`, - ); - return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount); - } - - /** - * - * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param amount amount of tokens to be burned. For NFT must be set to 1n - * @example burnToken(aliceKeyring, 10, 5); - * @returns ```true``` if the extrinsic is successful, otherwise ```false``` - */ - async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount = 1n): Promise { - const burnResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.burnItem', [collectionId, tokenId, amount], - true, // `Unable to burn token for ${label}`, - ); - const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult); - if(burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens'); - return burnedTokens.success; - } - - /** - * Destroys a concrete instance of NFT on behalf of the owner - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param fromAddressObj address on behalf of which the token will be burnt - * @param amount amount of tokens to be burned. For NFT must be set to 1n - * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."}) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise { - const burnResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount], - true, // `Unable to burn token from for ${label}`, - ); - const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult); - return burnedTokens.success && burnedTokens.tokens.length > 0; - } - - /** - * Set, change, or remove approved address to transfer the ownership of the NFT. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens - * @param amount amount of token to be approved. For NFT must be set to 1n - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) { - const approveResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount], - true, // `Unable to approve token for ${label}`, - ); - - return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved'); - } - - /** - * Set, change, or remove approved address to transfer the ownership of the NFT from eth mirror. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param fromAddressObj Signer's Ethereum address containing her tokens - * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens - * @param amount amount of token to be approved. For NFT must be set to 1n - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async approveTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { - const approveResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.approveFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount], - true, // `Unable to approve token for ${label}`, - ); - - return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved'); - } - - /** - * Set, change, or remove approved address to transfer the ownership of the NFT from eth mirror. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens - * @param amount amount of token to be approved. For NFT must be set to 1n - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async approveTokenFromEth(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) { - const ethMirror = CrossAccountId.fromKeyring(signer).toEthereum().toICrossAccountId(); - return await this.approveTokenFrom(signer, collectionId, tokenId, ethMirror, toAddressObj, amount); - } - - /** - * Get the amount of token pieces approved to transfer or burn. Normally 0. - * - * @param collectionId ID of collection - * @param tokenId ID of token - * @param toAccountObj address which is approved to use token pieces - * @param fromAccountObj address which may have allowed the use of its owned tokens - * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."}) - * @returns number of approved to transfer pieces - */ - async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise { - return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt(); - } - - /** - * Get the last created token ID in a collection - * - * @param collectionId ID of collection - * @example getLastTokenId(10); - * @returns id of the last created token - */ - async getLastTokenId(collectionId: number): Promise { - return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber(); - } - - /** - * Check if token exists - * - * @param collectionId ID of collection - * @param tokenId ID of token - * @example doesTokenExist(10, 20); - * @returns true if the token exists, otherwise false - */ - async doesTokenExist(collectionId: number, tokenId: number): Promise { - return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON(); - } -} - -class NFTnRFT extends CollectionGroup { - /** - * Get tokens owned by account - * - * @param collectionId ID of collection - * @param addressObj tokens owner - * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."}) - * @returns array of token ids owned by account - */ - async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise { - return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON(); - } - - /** - * Get token data - * - * @param collectionId ID of collection - * @param tokenId ID of token - * @param propertyKeys optionally filter the token properties to only these keys - * @param blockHashAt optionally query the data at some block with this hash - * @example getToken(10, 5); - * @returns human readable token data - */ - async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{ - properties: IProperty[]; - owner: ICrossAccountId; - normalizedOwner: ICrossAccountId; - } | null> { - let args; - if(typeof blockHashAt === 'undefined') { - args = [collectionId, tokenId]; - } - else { - if(propertyKeys.length == 0) { - const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman(); - if(!collection) return null; - propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key); - } - args = [collectionId, tokenId, propertyKeys, blockHashAt]; - } - const tokenData = (await this.helper.callRpc('api.rpc.unique.tokenData', args)).toHuman(); - if(tokenData === null || tokenData.owner === null) return null; - tokenData.normalizedOwner = CrossAccountId.withNormalizedSubstrate(tokenData.owner); - return tokenData; - } - - /** - * Get token's owner - * @param collectionId ID of collection - * @param tokenId ID of token - * @param blockHashAt optionally query the data at the block with this hash - * @example getTokenOwner(10, 5); - * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."} - */ - async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise { - let owner; - if(typeof blockHashAt === 'undefined') { - owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]); - } else { - owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]); - } - return CrossAccountId.fromLowerCaseKeys(owner.toJSON()).toICrossAccountId(); - } - - /** - * Recursively find the address that owns the token - * @param collectionId ID of collection - * @param tokenId ID of token - * @param blockHashAt - * @example getTokenTopmostOwner(10, 5); - * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."} - */ - async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise { - let owner; - if(typeof blockHashAt === 'undefined') { - owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]); - } else { - owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]); - } - - if(owner === null) return null; - - return owner.toHuman(); - } - - /** - * Nest one token into another - * @param signer keyring of signer - * @param tokenObj token to be nested - * @param rootTokenObj token to be parent - * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise { - const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj); - const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress); - if(!result) { - throw Error('Unable to nest token!'); - } - return result; - } - - /** - * Remove token from nested state - * @param signer keyring of signer - * @param tokenObj token to unnest - * @param rootTokenObj parent of a token - * @param toAddressObj address of a new token owner - * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."}); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise { - const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj); - const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj); - if(!result) { - throw Error('Unable to unnest token!'); - } - return result; - } - - /** - * Set permissions to change token properties - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param permissions permissions to change a property by the collection admin or token owner - * @example setTokenPropertyPermissions( - * aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}] - * ) - * @returns true if extrinsic success otherwise false - */ - async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet'); - } - - /** - * Get token property permissions. - * - * @param collectionId ID of collection - * @param propertyKeys optionally filter the returned property permissions to only these keys - * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']); - * @returns array of key-permission pairs - */ - async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise { - return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman(); - } - - /** - * Set token properties - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection - * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}]) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet'); - } - - /** - * Get properties, metadata assigned to a token. - * - * @param collectionId ID of collection - * @param tokenId ID of token - * @param propertyKeys optionally filter the returned properties to only these keys - * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']); - * @returns array of key-value pairs - */ - async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise { - return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman(); - } - - /** - * Delete the provided properties of a token - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param propertyKeys property keys to be deleted - * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"]) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys], - true, - ); - - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted'); - } - - /** - * Mint new collection - * - * @param signer keyring of signer - * @param collectionOptions basic collection options and properties - * @param mode NFT or RFT type of a collection - * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT") - * @returns object of the created collection - */ - async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise { - collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object - collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null}; - for(const key of ['name', 'description', 'tokenPrefix']) { - if(typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string); - } - - let flags = 0; - // convert CollectionFlags to number and join them in one number - if(collectionOptions.flags) { - for(let i = 0; i < collectionOptions.flags.length; i++){ - const flag = collectionOptions.flags[i]; - flags = flags | flag; - } - } - collectionOptions.flags = [flags]; - - const creationResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.createCollectionEx', [collectionOptions], - true, // errorLabel, - ); - return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult)); - } - - getCollectionObject(_collectionId: number): any { - return null; - } - - getTokenObject(_collectionId: number, _tokenId: number): any { - return null; - } - - /** - * Tells whether the given `owner` approves the `operator`. - * @param collectionId ID of collection - * @param owner owner address - * @param operator operator addrees - * @returns true if operator is enabled - */ - async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise { - return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON(); - } - - /** Sets or unsets the approval of a given operator. - * The `operator` is allowed to transfer all tokens of the `caller` on their behalf. - * @param operator Operator - * @param approved Should operator status be granted or revoked? - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise { - const result = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved], - true, - ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll'); - } -} - - -class NFTGroup extends NFTnRFT { - /** - * Get collection object - * @param collectionId ID of collection - * @example getCollectionObject(2); - * @returns instance of UniqueNFTCollection - */ - override getCollectionObject(collectionId: number): UniqueNFTCollection { - return new UniqueNFTCollection(collectionId, this.helper); - } - - /** - * Get token object - * @param collectionId ID of collection - * @param tokenId ID of token - * @example getTokenObject(10, 5); - * @returns instance of UniqueNFTToken - */ - override getTokenObject(collectionId: number, tokenId: number): UniqueNFToken { - return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId)); - } - - /** - * Is token approved to transfer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param toAccountObj address to be approved - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise { - return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, (await this.getTokenOwner(collectionId, tokenId)))) === 1n; - } - - /** - * Changes the owner of the token. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param addressObj address of a new owner - * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - override async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise { - return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n); - } - - /** - * - * Change ownership of a NFT on behalf of the owner. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param fromAddressObj address on behalf of which the token will be sent - * @param toAddressObj new token owner - * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Ethereum: "0x9F0583DbB85..."}) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - override async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise { - return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n); - } - - /** - * Get tokens nested in the provided token - * @param collectionId ID of collection - * @param tokenId ID of token - * @param blockHashAt optionally query the data at the block with this hash - * @example getTokenChildren(10, 5); - * @returns tokens whose depth of nesting is <= 5 - */ - async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise { - let children; - if(typeof blockHashAt === 'undefined') { - children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]); - } else { - children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]); - } - - return children.toJSON().map((x: any) => ({collectionId: x.collection, tokenId: x.token})); - } - - /** - * Mint new collection - * @param signer keyring of signer - * @param collectionOptions Collection options - * @example - * mintCollection(aliceKeyring, { - * name: 'New', - * description: 'New collection', - * tokenPrefix: 'NEW', - * }) - * @returns object of the created collection - */ - override async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise { - return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection; - } - - /** - * Mint new token - * @param signer keyring of signer - * @param data token data - * @returns created token object - */ - async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise { - const creationResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, { - NFT: { - properties: data.properties, - }, - }], - true, - ); - const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult); - if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens'); - if(createdTokens.tokens.length < 1) throw Error('No tokens minted'); - return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId); - } - - /** - * Mint multiple NFT tokens - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokens array of tokens with owner and properties - * @example - * mintMultipleTokens(aliceKeyring, 10, [{ - * owner: {Substrate: "5DyN4Y92vZCjv38fg..."}, - * properties: [{key: "gender", value: "male"},{key: "age", value: "45"}], - * },{ - * owner: {Ethereum: "0x9F0583DbB855d..."}, - * properties: [{key: "gender", value: "female"},{key: "age", value: "22"}], - * }]); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]): Promise { - const creationResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}], - true, - ); - const collection = this.getCollectionObject(collectionId); - return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); - } - - /** - * Mint multiple NFT tokens with one owner - * @param signer keyring of signer - * @param collectionId ID of collection - * @param owner tokens owner - * @param tokens array of tokens with owner and properties - * @example - * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{ - * properties: [{ - * key: "gender", - * value: "female", - * },{ - * key: "age", - * value: "33", - * }], - * }]); - * @returns array of newly created tokens - */ - async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { properties?: IProperty[] }[]): Promise { - const rawTokens = []; - for(const token of tokens) { - const raw = {NFT: {properties: token.properties}}; - rawTokens.push(raw); - } - const creationResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens], - true, - ); - const collection = this.getCollectionObject(collectionId); - return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); - } - - /** - * Set, change, or remove approved address to transfer the ownership of the NFT. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param toAddressObj address to approve - * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - override approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) { - return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount); - } -} - - -class RFTGroup extends NFTnRFT { - /** - * Get collection object - * @param collectionId ID of collection - * @example getCollectionObject(2); - * @returns instance of UniqueRFTCollection - */ - override getCollectionObject(collectionId: number): UniqueRFTCollection { - return new UniqueRFTCollection(collectionId, this.helper); - } - - /** - * Get token object - * @param collectionId ID of collection - * @param tokenId ID of token - * @example getTokenObject(10, 5); - * @returns instance of UniqueNFTToken - */ - override getTokenObject(collectionId: number, tokenId: number): UniqueRFToken { - return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId)); - } - - /** - * Get top 10 token owners with the largest number of pieces - * @param collectionId ID of collection - * @param tokenId ID of token - * @example getTokenTop10Owners(10, 5); - * @returns array of top 10 owners - */ - async getTokenTop10Owners(collectionId: number, tokenId: number): Promise { - return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys).map(a => a.toICrossAccountId()); - } - - /** - * Get number of pieces owned by address - * @param collectionId ID of collection - * @param tokenId ID of token - * @param addressObj address token owner - * @example getTokenBalance(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}); - * @returns number of pieces ownerd by address - */ - async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise { - return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt(); - } - - /** - * Transfer pieces of token to another address - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param addressObj address of a new owner - * @param amount number of pieces to be transfered - * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - override async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise { - return await super.transferToken(signer, collectionId, tokenId, addressObj, amount); - } - - /** - * Change ownership of some pieces of RFT on behalf of the owner. - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param fromAddressObj address on behalf of which the token will be sent - * @param toAddressObj new token owner - * @param amount number of pieces to be transfered - * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - override async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise { - return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount); - } - - /** - * Mint new collection - * @param signer keyring of signer - * @param collectionOptions Collection options - * @example - * mintCollection(aliceKeyring, { - * name: 'New', - * description: 'New collection', - * tokenPrefix: 'NEW', - * }) - * @returns object of the created collection - */ - override async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise { - return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection; - } - - /** - * Mint new token - * @param signer keyring of signer - * @param data token data - * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n}); - * @returns created token object - */ - async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise { - const creationResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, { - ReFungible: { - pieces: data.pieces, - properties: data.properties, - }, - }], - true, - ); - const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult); - if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens'); - if(createdTokens.tokens.length < 1) throw Error('No tokens minted'); - return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId); - } - - mintMultipleTokens(_signer: TSigner, _collectionId: number, _tokens: { owner: ICrossAccountId, pieces: bigint, properties?: IProperty[] }[]): Promise { - throw Error('Not implemented'); - // const creationResult = await this.helper.executeExtrinsic( - // signer, - // 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}], - // true, // `Unable to mint RFT tokens for ${label}`, - // ); - // const collection = this.getCollectionObject(collectionId); - // return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); - } - - /** - * Mint multiple RFT tokens with one owner - * @param signer keyring of signer - * @param collectionId ID of collection - * @param owner tokens owner - * @param tokens array of tokens with properties and pieces - * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]); - * @returns array of newly created RFT tokens - */ - async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { pieces: bigint, properties?: IProperty[] }[]): Promise { - const rawTokens = []; - for(const token of tokens) { - const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}}; - rawTokens.push(raw); - } - const creationResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens], - true, - ); - const collection = this.getCollectionObject(collectionId); - return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); - } - - /** - * Destroys a concrete instance of RFT. - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param amount number of pieces to be burnt - * @example burnToken(aliceKeyring, 10, 5); - * @returns ```true``` if the extrinsic is successful, otherwise ```false``` - */ - override async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount = 1n): Promise { - return await super.burnToken(signer, collectionId, tokenId, amount); - } - - /** - * Destroys a concrete instance of RFT on behalf of the owner. - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param fromAddressObj address on behalf of which the token will be burnt - * @param amount number of pieces to be burnt - * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - override async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise { - return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount); - } - - /** - * Set, change, or remove approved address to transfer the ownership of the RFT. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param toAddressObj address to approve - * @param amount number of pieces to be approved - * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n); - * @returns true if the token success, otherwise false - */ - override approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) { - return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount); - } - - /** - * Get total number of pieces - * @param collectionId ID of collection - * @param tokenId ID of token - * @example getTokenTotalPieces(10, 5); - * @returns number of pieces - */ - async getTokenTotalPieces(collectionId: number, tokenId: number): Promise { - return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt(); - } - - /** - * Change number of token pieces. Signer must be the owner of all token pieces. - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param amount new number of pieces - * @example repartitionToken(aliceKeyring, 10, 5, 12345n); - * @returns true if the repartion was success, otherwise false - */ - async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise { - const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId); - const repartitionResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.repartition', [collectionId, tokenId, amount], - true, - ); - if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated'); - return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed'); - } -} - - -class FTGroup extends CollectionGroup { - /** - * Get collection object - * @param collectionId ID of collection - * @example getCollectionObject(2); - * @returns instance of UniqueFTCollection - */ - getCollectionObject(collectionId: number): UniqueFTCollection { - return new UniqueFTCollection(collectionId, this.helper); - } - - /** - * Mint new fungible collection - * @param signer keyring of signer - * @param collectionOptions Collection options - * @param decimalPoints number of token decimals - * @example - * mintCollection(aliceKeyring, { - * name: 'New', - * description: 'New collection', - * tokenPrefix: 'NEW', - * }, 18) - * @returns newly created fungible collection - */ - async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise { - collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object - if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions'); - collectionOptions.mode = {fungible: decimalPoints}; - for(const key of ['name', 'description', 'tokenPrefix']) { - if(typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string); - } - const creationResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.createCollectionEx', [collectionOptions], - true, - ); - return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult)); - } - - /** - * Mint tokens - * @param signer keyring of signer - * @param collectionId ID of collection - * @param owner address owner of new tokens - * @param amount amount of tokens to be meanted - * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise { - const creationResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, { - Fungible: { - value: amount, - }, - }], - true, // `Unable to mint fungible tokens for ${label}`, - ); - return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated'); - } - - /** - * Mint multiple Fungible tokens with one owner - * @param signer keyring of signer - * @param collectionId ID of collection - * @param owner tokens owner - * @param tokens array of tokens with properties and pieces - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: { value: bigint }[], owner: ICrossAccountId): Promise { - const rawTokens = []; - for(const token of tokens) { - const raw = {Fungible: {Value: token.value}}; - rawTokens.push(raw); - } - const creationResult = await this.helper.executeExtrinsic( - signer, - 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens], - true, - ); - return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated'); - } - - /** - * Get the top 10 owners with the largest balance for the Fungible collection - * @param collectionId ID of collection - * @example getTop10Owners(10); - * @returns array of ```ICrossAccountId``` - */ - async getTop10Owners(collectionId: number): Promise { - return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys).map(a => a.toICrossAccountId()); - } - - /** - * Get account balance - * @param collectionId ID of collection - * @param addressObj address of owner - * @example getBalance(10, {Substrate: "5GHoZe9c73RYbVzq..."}) - * @returns amount of fungible tokens owned by address - */ - async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise { - return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt(); - } - - /** - * Transfer tokens to address - * @param signer keyring of signer - * @param collectionId ID of collection - * @param toAddressObj address recipient - * @param amount amount of tokens to be sent - * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) { - return await super.transferToken(signer, collectionId, 0, toAddressObj, amount); - } - - /** - * Transfer some tokens on behalf of the owner. - * @param signer keyring of signer - * @param collectionId ID of collection - * @param fromAddressObj address on behalf of which tokens will be sent - * @param toAddressObj address where token to be sent - * @param amount number of tokens to be sent - * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { - return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount); - } - - /** - * Destroy some amount of tokens - * @param signer keyring of signer - * @param collectionId ID of collection - * @param amount amount of tokens to be destroyed - * @example burnTokens(aliceKeyring, 10, 1000n); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async burnTokens(signer: IKeyringPair, collectionId: number, amount = 1n): Promise { - return await super.burnToken(signer, collectionId, 0, amount); - } - - /** - * Burn some tokens on behalf of the owner. - * @param signer keyring of signer - * @param collectionId ID of collection - * @param fromAddressObj address on behalf of which tokens will be burnt - * @param amount amount of tokens to be burnt - * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise { - return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount); - } - - /** - * Get total collection supply - * @param collectionId - * @returns - */ - async getTotalPieces(collectionId: number): Promise { - return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt(); - } - - /** - * Set, change, or remove approved address to transfer tokens. - * - * @param signer keyring of signer - * @param collectionId ID of collection - * @param toAddressObj address to be approved - * @param amount amount of tokens to be approved - * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n) - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) { - return super.approveToken(signer, collectionId, 0, toAddressObj, amount); - } - - /** - * Get amount of fungible tokens approved to transfer - * @param collectionId ID of collection - * @param fromAddressObj owner of tokens - * @param toAddressObj the address approved for the transfer of tokens on behalf of the owner - * @returns number of tokens approved for the transfer - */ - getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { - return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj); - } -} - - -class ChainGroup extends HelperGroup { - /** - * Get system properties of a chain - * @example getChainProperties(); - * @returns ss58Format, token decimals, and token symbol - */ - getChainProperties(): IChainProperties { - const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON(); - return { - ss58Format: properties.ss58Format.toJSON(), - tokenDecimals: properties.tokenDecimals.toJSON(), - tokenSymbol: properties.tokenSymbol.toJSON(), - }; - } - - /** - * Get chain header - * @example getLatestBlockNumber(); - * @returns the number of the last block - */ - async getLatestBlockNumber(): Promise { - return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber(); - } - - /** - * Get block hash by block number - * @param blockNumber number of block - * @example getBlockHashByNumber(12345); - * @returns hash of a block - */ - async getBlockHashByNumber(blockNumber: number): Promise { - const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON(); - if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null; - return blockHash; - } - - // TODO add docs - async getBlock(blockHashOrNumber: string | number): Promise { - const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber); - if(!blockHash) return null; - return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block; - } - - /** - * Get latest relay block - * @returns {number} relay block - */ - async getRelayBlockNumber(): Promise { - const blockNumber = (await this.helper.callRpc('api.query.parachainSystem.validationData')).toJSON().relayParentNumber; - return BigInt(blockNumber); - } - - /** - * Get account nonce - * @param address substrate address - * @example getNonce("5GrwvaEF5zXb26Fz..."); - * @returns number, account's nonce - */ - async getNonce(address: TSubstrateAccount): Promise { - return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber(); - } -} - -export class SubstrateBalanceGroup extends HelperGroup { - /** - * Get substrate address balance - * @param address substrate address - * @example getSubstrate("5GrwvaEF5zXb26Fz...") - * @returns amount of tokens on address - */ - async getSubstrate(address: TSubstrateAccount): Promise { - return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt(); - } - - /** - * Transfer tokens to substrate address - * @param signer keyring of signer - * @param address substrate address of a recipient - * @param amount amount of tokens to be transfered - * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise { - 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}`*/); - - let transfer = {from: null, to: null, amount: 0n} as any; - result.result.events.forEach(({event: {data, method, section}}: any) => { - if((section === 'balances') && (method === 'Transfer')) { - transfer = { - from: this.helper.address.normalizeSubstrate(data[0]), - to: this.helper.address.normalizeSubstrate(data[1]), - amount: BigInt(data[2]), - }; - } - }); - const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from - && this.helper.address.normalizeSubstrate(address) === transfer.to - && BigInt(amount) === transfer.amount; - return isSuccess; - } - - /** - * Get full substrate balance including free, frozen, and reserved - * @param address substrate address - * @returns - */ - async getSubstrateFull(address: TSubstrateAccount): Promise { - const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data; - return {free: accountInfo.free.toBigInt(), frozen: accountInfo.frozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()}; - } - - /** - * Get total issuance - * @returns - */ - async getTotalIssuance(): Promise { - const total = (await this.helper.callRpc('api.query.balances.totalIssuance', [])); - return total.toBigInt(); - } - - async getLocked(address: TSubstrateAccount): Promise<{ id: string, amount: bigint, reason: string }[]> { - const locks = (await this.helper.callRpc('api.query.balances.locks', [address])).toHuman(); - return locks.map((lock: any) => ({id: lock.id, amount: BigInt(lock.amount.replace(/,/g, '')), reasons: lock.reasons})); - } - async getFrozen(address: TSubstrateAccount): Promise<{ id: string, amount: bigint }[]> { - const locks = (await this.helper.api!.query.balances.freezes(address)) as unknown as Array; - return locks.map(lock => ({id: lock.id.toUtf8(), amount: lock.amount.toBigInt()})); - } -} - -export class EthereumBalanceGroup extends HelperGroup { - /** - * Get ethereum address balance - * @param address ethereum address - * @example getEthereum("0x9F0583DbB855d...") - * @returns amount of tokens on address - */ - async getEthereum(address: TEthereumAccount): Promise { - return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt(); - } - - /** - * Transfer tokens to address - * @param signer keyring of signer - * @param address Ethereum address of a recipient - * @param amount amount of tokens to be transfered - * @example transferToEthereum(alithKeyring, "0x9F0583DbB855d...", 100_000_000_000n); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise { - const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true); - - let transfer = {from: null, to: null, amount: 0n} as any; - result.result.events.forEach(({event: {data, method, section}}: any) => { - if((section === 'balances') && (method === 'Transfer')) { - transfer = { - from: data[0].toString(), - to: data[1].toString(), - amount: BigInt(data[2]), - }; - } - }); - const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from - && address === transfer.to - && BigInt(amount) === transfer.amount; - return isSuccess; - } -} - -class BalanceGroup extends HelperGroup { - subBalanceGroup: SubstrateBalanceGroup; - ethBalanceGroup: EthereumBalanceGroup; - - constructor(helper: T) { - super(helper); - this.subBalanceGroup = new SubstrateBalanceGroup(helper); - this.ethBalanceGroup = new EthereumBalanceGroup(helper); - } - - getCollectionCreationPrice(): bigint { - return 2n * this.getOneTokenNominal(); - } - /** - * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ). - * @example getOneTokenNominal() - * @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ. - */ - getOneTokenNominal(): bigint { - const chainProperties = this.helper.chain.getChainProperties(); - return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]); - } - - /** - * Get substrate address balance - * @param address substrate address - * @example getSubstrate("5GrwvaEF5zXb26Fz...") - * @returns amount of tokens on address - */ - getSubstrate(address: TSubstrateAccount): Promise { - return this.subBalanceGroup.getSubstrate(address); - } - - /** - * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved - * @param address substrate address - * @returns - */ - getSubstrateFull(address: TSubstrateAccount): Promise { - return this.subBalanceGroup.getSubstrateFull(address); - } - - /** - * Get total issuance - * @returns - */ - getTotalIssuance(): Promise { - return this.subBalanceGroup.getTotalIssuance(); - } - - /** - * Get locked balances - * @param address substrate address - * @returns locked balances with reason via api.query.balances.locks - * @deprecated all the methods should switch to getFrozen - */ - getLocked(address: TSubstrateAccount) { - return this.subBalanceGroup.getLocked(address); - } - - /** - * Get frozen balances - * @param address substrate address - * @returns frozen balances with id via api.query.balances.freezes - */ - getFrozen(address: TSubstrateAccount) { - return this.subBalanceGroup.getFrozen(address); - } - - /** - * Get ethereum address balance - * @param address ethereum address - * @example getEthereum("0x9F0583DbB855d...") - * @returns amount of tokens on address - */ - getEthereum(address: TEthereumAccount): Promise { - return this.ethBalanceGroup.getEthereum(address); - } - - async setBalanceSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint) { - await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceSetBalance', [address, amount], true); - } - - /** - * Transfer tokens to substrate address - * @param signer keyring of signer - * @param address substrate address of a recipient - * @param amount amount of tokens to be transfered - * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n); - * @returns ```true``` if extrinsic success, otherwise ```false``` - */ - transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise { - return this.subBalanceGroup.transferToSubstrate(signer, address, amount); - } - - async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise { - const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true); - - let transfer = {from: null, to: null, amount: 0n} as any; - result.result.events.forEach(({event: {data, method, section}}: any) => { - if((section === 'balances') && (method === 'Transfer')) { - transfer = { - from: this.helper.address.normalizeSubstrate(data[0]), - to: this.helper.address.normalizeSubstrate(data[1]), - amount: BigInt(data[2]), - }; - } - }); - let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from; - isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to; - isSuccess = isSuccess && BigInt(amount) === transfer.amount; - return isSuccess; - } - - /** - * Transfer tokens with the unlock period - * @param signer signers Keyring - * @param address Substrate address of recipient - * @param schedule Schedule params - * @example vestedTransfer(signer, recepient.address, 20000, 100, 10, 50 * nominal); // total amount of vested tokens will be 100 * 50 = 5000 - */ - async vestedTransfer(signer: TSigner, address: TSubstrateAccount, schedule: { start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }): Promise { - const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.vestedTransfer', [address, schedule]); - const event = result.result.events - .find((e: any) => e.event.section === 'vesting' && - e.event.method === 'VestingScheduleAdded' && - e.event.data[0].toHuman() === signer.address); - if(!event) throw Error('Cannot find transfer in events'); - } - - /** - * Get schedule for recepient of vested transfer - * @param address Substrate address of recipient - * @returns - */ - async getVestingSchedules(address: TSubstrateAccount): Promise<{ start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }[]> { - const schedule = (await this.helper.callRpc('api.query.vesting.vestingSchedules', [address])).toJSON(); - return schedule.map((schedule: any) => ({ - start: BigInt(schedule.start), - period: BigInt(schedule.period), - periodCount: BigInt(schedule.periodCount), - perPeriod: BigInt(schedule.perPeriod), - })); - } - - /** - * Claim vested tokens - * @param signer signers Keyring - */ - async claim(signer: TSigner) { - const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.claim', []); - const event = result.result.events - .find((e: any) => e.event.section === 'vesting' && - e.event.method === 'Claimed' && - e.event.data[0].toHuman() === signer.address); - if(!event) throw Error('Cannot find claim in events'); - } -} - -class AddressGroup extends HelperGroup { - /** - * Normalizes the address to the specified ss58 format, by default ```42```. - * @param address substrate address - * @param ss58Format format for address conversion, by default ```42``` - * @example normalizeSubstrate("unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx") // returns 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY - * @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation - */ - normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount { - return CrossAccountId.normalizeSubstrateAddress({Substrate: address}, ss58Format); - } - - /** - * Get address in the connected chain format - * @param address substrate address - * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network - * @returns address in chain format - */ - normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount { - return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format); - } - - /** - * Get substrate mirror of an ethereum address - * @param ethAddress ethereum address - * @param toChainFormat false for normalized account - * @example ethToSubstrate('0x9F0583DbB855d...') - * @returns substrate mirror of a provided ethereum address - */ - ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat = false): TSubstrateAccount { - return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined); - } - - /** - * Get ethereum mirror of a substrate address - * @param subAddress substrate account - * @example substrateToEth("5DnSF6RRjwteE3BrC...") - * @returns ethereum mirror of a provided substrate address - */ - substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount { - return CrossAccountId.translateSubToEth(subAddress); - } - - /** - * Encode key to substrate address - * @param key key for encoding address - * @param ss58Format prefix for encoding to the address of the corresponding network - * @returns encoded substrate address - */ - encodeSubstrateAddress(key: Uint8Array | string | bigint, ss58Format = 42): string { - const u8a: Uint8Array = typeof key === 'string' - ? hexToU8a(key) - : typeof key === 'bigint' - ? hexToU8a(key.toString(16)) - : key; - - if(ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) { - throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`); - } - - const allowedDecodedLengths = [1, 2, 4, 8, 32, 33]; - if(!allowedDecodedLengths.includes(u8a.length)) { - throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`); - } - - const u8aPrefix = ss58Format < 64 - ? new Uint8Array([ss58Format]) - : new Uint8Array([ - ((ss58Format & 0xfc) >> 2) | 0x40, - (ss58Format >> 8) | ((ss58Format & 0x03) << 6), - ]); - - const input = u8aConcat(u8aPrefix, u8a); - - return base58Encode(u8aConcat( - input, - blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1), - )); - } - - /** - * Restore substrate address from bigint representation - * @param number decimal representation of substrate address - * @returns substrate address - */ - restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount { - if(this.helper.api === null) { - throw 'Not connected'; - } - const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON(); - if(res === undefined || res === null) { - throw 'Restore address error'; - } - return res.toString(); - } - - /** - * Convert etherium cross account id to substrate cross account id - * @param ethCrossAccount etherium cross account - * @returns substrate cross account id - */ - convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId { - if(ethCrossAccount.sub === '0') { - return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()}; - } - - const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub)); - return {Substrate: ss58}; - } - - paraSiblingSovereignAccount(paraid: number) { - // We are getting a *sibling* parachain sovereign account, - // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c - const siblingPrefix = '0x7369626c'; - - const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2); - const suffix = '000000000000000000000000000000000000000000000000'; - - return siblingPrefix + encodedParaId + suffix; - } -} - - -class StakingGroup extends HelperGroup { - /** - * Stake tokens for App Promotion - * @param signer keyring of signer - * @param amountToStake amount of tokens to stake - * @param label extra label for log - * @returns - */ - async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise { - if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`; - await this.helper.executeExtrinsic( - signer, 'api.tx.appPromotion.stake', - [amountToStake], true, - ); - // TODO extract info from stakeResult - return true; - } - - /** - * Unstake all staked tokens - * @param signer keyring of signer - * @param amountToUnstake amount of tokens to unstake - * @param label extra label for log - * @returns block hash where unstake happened - */ - async unstakeAll(signer: TSigner, label?: string): Promise { - if(typeof label === 'undefined') label = `${signer.address}`; - const unstakeResult = await this.helper.executeExtrinsic( - signer, 'api.tx.appPromotion.unstakeAll', - [], true, - ); - return unstakeResult.blockHash; - } - - /** - * Unstake the part of a staked tokens - * @param signer keyring of signer - * @param amount amount of tokens to unstake - * @param label extra label for log - * @returns block hash where unstake happened - */ - async unstakePartial(signer: TSigner, amount: bigint, label?: string): Promise { - if(typeof label === 'undefined') label = `${signer.address}`; - const unstakeResult = await this.helper.executeExtrinsic( - signer, 'api.tx.appPromotion.unstakePartial', - [amount], true, - ); - return unstakeResult.blockHash; - } - - /** - * Get total number of active stakes - * @param address substrate address - * @returns {number} - */ - async getStakesNumber(address: ICrossAccountId): Promise { - if('Ethereum' in address) throw Error('only substrate address'); - return (await this.helper.callRpc('api.query.appPromotion.stakesPerAccount', [address.Substrate])).toNumber(); - } - - /** - * Get total staked amount for address - * @param address substrate or ethereum address - * @returns total staked amount - */ - async getTotalStaked(address?: ICrossAccountId): Promise { - if(address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt(); - return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt(); - } - - /** - * Get total staked per block - * @param address substrate or ethereum address - * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block - */ - async getTotalStakedPerBlock(address: ICrossAccountId): Promise { - const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]); - return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => ({ - block: block.toBigInt(), - amount: amount.toBigInt(), - })); - } - - /** - * Get total pending unstake amount for address - * @param address substrate or ethereum address - * @returns total pending unstake amount - */ - async getPendingUnstake(address: ICrossAccountId): Promise { - return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt(); - } - - /** - * Get pending unstake amount per block for address - * @param address substrate or ethereum address - * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block - */ - async getPendingUnstakePerBlock(address: ICrossAccountId): Promise { - const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]); - const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => ({ - block: block.toBigInt(), - amount: amount.toBigInt(), - })); - return result; - } -} - - -class PreimageGroup extends HelperGroup { - async getPreimageInfo(h256: string) { - return (await this.helper.callRpc('api.query.preimage.statusFor', [h256])).toJSON(); - } - - /** - * Create a preimage from an API call. - * @param signer keyring of the signer. - * @param call an extrinsic call - * @example await notePreimageFromCall(preimageMaker, - * helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]) - * ); - * @returns promise of extrinsic execution. - */ - notePreimageFromCall(signer: TSigner, call: any, returnPreimageHash = false) { - return this.notePreimage(signer, call.method.toHex(), returnPreimageHash); - } - - /** - * Create a preimage with a hex or a byte array. - * @param signer keyring of the signer. - * @param bytes preimage encoded in hex or a byte array, e.g. an extrinsic call. - * @example await notePreimage(preimageMaker, - * helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]).method.toHex() - * ); - * @returns promise of extrinsic execution. - */ - async notePreimage(signer: TSigner, bytes: string | Uint8Array, returnPreimageHash = false) { - const promise = this.helper.executeExtrinsic(signer, 'api.tx.preimage.notePreimage', [bytes]); - if(returnPreimageHash) { - const result = await promise; - const events = result.result.events.filter((x: any) => x.event.method === 'Noted' && x.event.section === 'preimage'); - const preimageHash = events[0].event.data[0].toHuman(); - return preimageHash; - } - return promise; - } - - /** - * Delete an existing preimage and return the deposit. - * @param signer keyring of the signer - either the owner or the preimage manager (sudo). - * @param h256 hash of the preimage. - * @returns promise of extrinsic execution. - */ - unnotePreimage(signer: TSigner, h256: string) { - return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unnotePreimage', [h256]); - } - - /** - * Request a preimage be uploaded to the chain without paying any fees or deposits. - * @param signer keyring of the signer - either the owner or the preimage manager (sudo). - * @param h256 hash of the preimage. - * @returns promise of extrinsic execution. - */ - requestPreimage(signer: TSigner, h256: string) { - return this.helper.executeExtrinsic(signer, 'api.tx.preimage.requestPreimage', [h256]); - } - - /** - * Clear a previously made request for a preimage. - * @param signer keyring of the signer - either the owner or the preimage manager (sudo). - * @param h256 hash of the preimage. - * @returns promise of extrinsic execution. - */ - unrequestPreimage(signer: TSigner, h256: string) { - return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unrequestPreimage', [h256]); - } -} - -class UtilityGroup extends HelperGroup { - async batch(signer: TSigner, txs: any[]) { - return await this.helper.executeExtrinsic(signer, 'api.tx.utility.batch', [txs]); - } - - async batchAll(signer: TSigner, txs: any[]) { - return await this.helper.executeExtrinsic(signer, 'api.tx.utility.batchAll', [txs]); - } - - batchAllCall(txs: any[]) { - return this.helper.constructApiCall('api.tx.utility.batchAll', [txs]); - } -} - -export type ChainHelperBaseConstructor = new (...args: any[]) => ChainHelperBase; -export type UniqueHelperConstructor = new (...args: any[]) => UniqueHelper; - -export class UniqueHelper extends ChainHelperBase { - balance: BalanceGroup; - collection: CollectionGroup; - nft: NFTGroup; - rft: RFTGroup; - ft: FTGroup; - staking: StakingGroup; - preimage: PreimageGroup; - utility: UtilityGroup; - - constructor(logger?: ILogger, options: { [key: string]: any } = {}) { - super(logger, options.helperBase ?? UniqueHelper); - - this.balance = new BalanceGroup(this); - this.collection = new CollectionGroup(this); - this.nft = new NFTGroup(this); - this.rft = new RFTGroup(this); - this.ft = new FTGroup(this); - this.staking = new StakingGroup(this); - this.preimage = new PreimageGroup(this); - this.utility = new UtilityGroup(this); - } -} - -export class UniqueBaseCollection { - helper: UniqueHelper; - collectionId: number; - - constructor(collectionId: number, uniqueHelper: UniqueHelper) { - this.collectionId = collectionId; - this.helper = uniqueHelper; - } - - async getData() { - return await this.helper.collection.getData(this.collectionId); - } - - async getLastTokenId() { - return await this.helper.collection.getLastTokenId(this.collectionId); - } - - async doesTokenExist(tokenId: number) { - return await this.helper.collection.doesTokenExist(this.collectionId, tokenId); - } - - async getAdmins() { - return await this.helper.collection.getAdmins(this.collectionId); - } - - async getAllowList() { - return await this.helper.collection.getAllowList(this.collectionId); - } - - async getEffectiveLimits() { - return await this.helper.collection.getEffectiveLimits(this.collectionId); - } - - async getProperties(propertyKeys?: string[] | null) { - return await this.helper.collection.getProperties(this.collectionId, propertyKeys); - } - - async getPropertiesConsumedSpace() { - return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId); - } - - async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) { - return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj); - } - - async getOptions() { - return await this.helper.collection.getCollectionOptions(this.collectionId); - } - - async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) { - return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress); - } - - async confirmSponsorship(signer: TSigner) { - return await this.helper.collection.confirmSponsorship(signer, this.collectionId); - } - - async removeSponsor(signer: TSigner) { - return await this.helper.collection.removeSponsor(signer, this.collectionId); - } - - async setLimits(signer: TSigner, limits: ICollectionLimits) { - return await this.helper.collection.setLimits(signer, this.collectionId, limits); - } - - async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) { - return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress); - } - - async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) { - return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj); - } - - async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) { - return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj); - } - - async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) { - return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj); - } - - async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) { - return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj); - } - - async setProperties(signer: TSigner, properties: IProperty[]) { - return await this.helper.collection.setProperties(signer, this.collectionId, properties); - } - - async deleteProperties(signer: TSigner, propertyKeys: string[]) { - return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys); - } - - async setPermissions(signer: TSigner, permissions: ICollectionPermissions) { - return await this.helper.collection.setPermissions(signer, this.collectionId, permissions); - } - - async enableNesting(signer: TSigner, permissions: INestingPermissions) { - return await this.helper.collection.enableNesting(signer, this.collectionId, permissions); - } - - async disableNesting(signer: TSigner) { - return await this.helper.collection.disableNesting(signer, this.collectionId); - } - - async burn(signer: TSigner) { - return await this.helper.collection.burn(signer, this.collectionId); - } -} - -export class UniqueNFTCollection extends UniqueBaseCollection { - getTokenObject(tokenId: number) { - return new UniqueNFToken(tokenId, this); - } - - async getTokensByAddress(addressObj: ICrossAccountId) { - return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj); - } - - async getToken(tokenId: number, blockHashAt?: string) { - return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt); - } - - async getTokenOwner(tokenId: number, blockHashAt?: string) { - return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt); - } - - async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) { - return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt); - } - - async getTokenChildren(tokenId: number, blockHashAt?: string) { - return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt); - } - - async getPropertyPermissions(propertyKeys: string[] | null = null) { - return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys); - } - - async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) { - return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys); - } - - async getTokenPropertiesConsumedSpace(tokenId: number): Promise { - const api = this.helper.getApi(); - const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON() as any; - - return props?.consumedSpace ?? 0; - } - - async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) { - return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj); - } - - async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { - return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj); - } - - async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) { - return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj); - } - - async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) { - return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj); - } - - async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) { - return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties}); - } - - async mintMultipleTokens(signer: TSigner, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]) { - return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens); - } - - async burnToken(signer: TSigner, tokenId: number) { - return await this.helper.nft.burnToken(signer, this.collectionId, tokenId); - } - - async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) { - return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj); - } - - async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) { - return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties); - } - - async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) { - return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys); - } - - async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) { - return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions); - } - - async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) { - return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj); - } - - async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) { - return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj); - } -} - -export class UniqueRFTCollection extends UniqueBaseCollection { - getTokenObject(tokenId: number) { - return new UniqueRFToken(tokenId, this); - } - - async getToken(tokenId: number, blockHashAt?: string) { - return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt); - } - - async getTokenOwner(tokenId: number, blockHashAt?: string) { - return await this.helper.rft.getTokenOwner(this.collectionId, tokenId, blockHashAt); - } - - async getTokensByAddress(addressObj: ICrossAccountId) { - return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj); - } - - async getTop10TokenOwners(tokenId: number) { - return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId); - } - - async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) { - return await this.helper.rft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt); - } - - async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) { - return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj); - } - - async getTokenTotalPieces(tokenId: number) { - return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId); - } - - async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { - return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj); - } - - async getPropertyPermissions(propertyKeys: string[] | null = null) { - return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys); - } - - async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) { - return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys); - } - - async getTokenPropertiesConsumedSpace(tokenId: number): Promise { - const api = this.helper.getApi(); - const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON() as any; - - return props?.consumedSpace ?? 0; - } - - async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount = 1n) { - return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount); - } - - async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { - return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount); - } - - async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) { - return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount); - } - - async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) { - return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount); - } - - async mintToken(signer: TSigner, pieces = 1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) { - return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties}); - } - - async mintMultipleTokens(signer: TSigner, tokens: { pieces: bigint, owner: ICrossAccountId, properties?: IProperty[] }[]) { - return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens); - } - - async burnToken(signer: TSigner, tokenId: number, amount = 1n) { - return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount); - } - - async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n) { - return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount); - } - - async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) { - return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties); - } - - async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) { - return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys); - } - - async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) { - return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions); - } - - async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) { - return await this.helper.rft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj); - } - - async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) { - return await this.helper.rft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj); - } -} - -export class UniqueFTCollection extends UniqueBaseCollection { - async getBalance(addressObj: ICrossAccountId) { - return await this.helper.ft.getBalance(this.collectionId, addressObj); - } - - async getTotalPieces() { - return await this.helper.ft.getTotalPieces(this.collectionId); - } - - async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { - return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj); - } - - async getTop10Owners() { - return await this.helper.ft.getTop10Owners(this.collectionId); - } - - async mint(signer: TSigner, amount = 1n, owner: ICrossAccountId = {Substrate: signer.address}) { - return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner); - } - - async mintWithOneOwner(signer: TSigner, tokens: { value: bigint }[], owner: ICrossAccountId = {Substrate: signer.address}) { - return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner); - } - - async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) { - return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount); - } - - async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { - return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount); - } - - async burnTokens(signer: TSigner, amount = 1n) { - return await this.helper.ft.burnTokens(signer, this.collectionId, amount); - } - - async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) { - return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount); - } - - async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) { - return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount); - } -} - -export class UniqueBaseToken { - collection: UniqueNFTCollection | UniqueRFTCollection; - collectionId: number; - tokenId: number; - - constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) { - this.collection = collection; - this.collectionId = collection.collectionId; - this.tokenId = tokenId; - } - - async getNextSponsored(addressObj: ICrossAccountId) { - return await this.collection.getTokenNextSponsored(this.tokenId, addressObj); - } - - async getProperties(propertyKeys?: string[] | null) { - return await this.collection.getTokenProperties(this.tokenId, propertyKeys); - } - - async getTokenPropertiesConsumedSpace() { - return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId); - } - - async setProperties(signer: TSigner, properties: IProperty[]) { - return await this.collection.setTokenProperties(signer, this.tokenId, properties); - } - - async deleteProperties(signer: TSigner, propertyKeys: string[]) { - return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys); - } - - async doesExist() { - return await this.collection.doesTokenExist(this.tokenId); - } - - nestingAccount(): ICrossAccountId { - return this.collection.helper.util.getTokenAccount(this); - } -} - -export class UniqueNFToken extends UniqueBaseToken { - declare collection: UniqueNFTCollection; - - constructor(tokenId: number, collection: UniqueNFTCollection) { - super(tokenId, collection); - this.collection = collection; - } - - async getData(blockHashAt?: string) { - return await this.collection.getToken(this.tokenId, blockHashAt); - } - - async getOwner(blockHashAt?: string) { - return await this.collection.getTokenOwner(this.tokenId, blockHashAt); - } - - async getTopmostOwner(blockHashAt?: string) { - return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt); - } - - async getChildren(blockHashAt?: string) { - return await this.collection.getTokenChildren(this.tokenId, blockHashAt); - } - - async nest(signer: TSigner, toTokenObj: IToken) { - return await this.collection.nestToken(signer, this.tokenId, toTokenObj); - } - - async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) { - return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj); - } - - async transfer(signer: TSigner, addressObj: ICrossAccountId) { - return await this.collection.transferToken(signer, this.tokenId, addressObj); - } - - async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { - return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj); - } - - async approve(signer: TSigner, toAddressObj: ICrossAccountId) { - return await this.collection.approveToken(signer, this.tokenId, toAddressObj); - } - - async isApproved(toAddressObj: ICrossAccountId) { - return await this.collection.isTokenApproved(this.tokenId, toAddressObj); - } - - async burn(signer: TSigner) { - return await this.collection.burnToken(signer, this.tokenId); - } - - async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) { - return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj); - } -} - -export class UniqueRFToken extends UniqueBaseToken { - declare collection: UniqueRFTCollection; - - constructor(tokenId: number, collection: UniqueRFTCollection) { - super(tokenId, collection); - this.collection = collection; - } - - async getData(blockHashAt?: string) { - return await this.collection.getToken(this.tokenId, blockHashAt); - } - - async getOwner(blockHashAt?: string) { - return await this.collection.getTokenOwner(this.tokenId, blockHashAt); - } - - async getTop10Owners() { - return await this.collection.getTop10TokenOwners(this.tokenId); - } - - async getTopmostOwner(blockHashAt?: string) { - return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt); - } - - async nest(signer: TSigner, toTokenObj: IToken) { - return await this.collection.nestToken(signer, this.tokenId, toTokenObj); - } - - async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) { - return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj); - } - - async getBalance(addressObj: ICrossAccountId) { - return await this.collection.getTokenBalance(this.tokenId, addressObj); - } - - async getTotalPieces() { - return await this.collection.getTokenTotalPieces(this.tokenId); - } - - async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) { - return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj); - } - - async transfer(signer: TSigner, addressObj: ICrossAccountId, amount = 1n) { - return await this.collection.transferToken(signer, this.tokenId, addressObj, amount); - } - - async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { - return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount); - } - - async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) { - return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount); - } - - async repartition(signer: TSigner, amount: bigint) { - return await this.collection.repartitionToken(signer, this.tokenId, amount); - } - - async burn(signer: TSigner, amount = 1n) { - return await this.collection.burnToken(signer, this.tokenId, amount); - } - - async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) { - return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount); - } -} --- a/js-packages/playgrounds/src/unique.xcm.ts +++ /dev/null @@ -1,375 +0,0 @@ -import {ApiPromise, WsProvider} from '@polkadot/api'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, UniqueHelper} from './unique.js'; -import type {ILogger, TSigner, TSubstrateAccount} from './types.js'; -import type {AcalaAssetMetadata, DemocracyStandardAccountVote, IForeignAssetMetadata, MoonbeamAssetInfo} from './types.xcm.js'; - - -export class XcmChainHelper extends ChainHelperBase { - override async connect(wsEndpoint: string, _listeners?: any): Promise { - 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 { - async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) { - await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true); - } -} - -class MoonbeamAssetManagerGroup extends HelperGroup { - 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 { - 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 { - 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 extends HelperGroup { - async whitelistToken(signer: TSigner, assetId: any) { - await this.helper.executeExtrinsic(signer, 'api.tx.xcmHelper.whitelistToken', [assetId], true); - } -} - -export class ForeignAssetsGroup extends HelperGroup { - 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 extends HelperGroup { - 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 extends HelperGroup { - 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 extends HelperGroup { - 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 extends HelperGroup { - async create(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint) { - await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true); - } - - async forceCreate(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint, isSufficient = true) { - await this.helper.executeExtrinsic(signer, 'api.tx.assets.forceCreate', [assetId, admin, isSufficient, minimalBalance], true); - } - - async setMetadata(signer: TSigner, assetId: number | bigint, 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 | bigint, beneficiary: string, amount: bigint) { - await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true); - } - - async account(assetId: string | number | bigint, 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; - xcm: XcmGroup; - - 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; - xcm: XcmGroup; - assets: AssetsGroup; - xTokens: XTokensGroup; - - 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; - assetManager: MoonbeamAssetManagerGroup; - assets: AssetsGroup; - xTokens: XTokensGroup; - 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; - assets: AssetsGroup; - xcm: XcmGroup; - - 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; - assetRegistry: AcalaAssetRegistryGroup; - xTokens: XTokensGroup; - tokens: TokensGroup; - xcm: XcmGroup; - - 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; - balance: SubstrateBalanceGroup; - xTokens: XTokensGroup; - xcm: XcmGroup; - xcmHelper: PolkadexXcmHelperGroup; - - 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); - } -} - --- a/js-packages/playgrounds/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../tsconfig.packages.json", - "compilerOptions": { - "rootDir": "./src", - "outDir": "dist" - }, - "references": [ - { "path": "../types/tsconfig.json" }, - ] -} \ No newline at end of file --- /dev/null +++ b/js-packages/playgrounds/types.ts @@ -0,0 +1,232 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +import type {IKeyringPair} from '@polkadot/types/types'; + +export const NON_EXISTENT_COLLECTION_ID = 4_294_967_295; + +export const MILLISECS_PER_BLOCK = 12000; +export const MINUTES = 60_000 / MILLISECS_PER_BLOCK; +export const HOURS = MINUTES * 60; +export const DAYS = HOURS * 24; + +export interface IEvent { + section: string; + method: string; + index: [number, number] | string; + data: any[]; + phase: {applyExtrinsic: number} | 'Initialization', +} + +export interface IPhasicEvent { + phase: any, // {ApplyExtrinsic: number} | 'Initialization', + event: IEvent; +} + +export interface ITransactionResult { + status: 'Fail' | 'Success'; + result: { + dispatchError: any, + events: IPhasicEvent[]; + }, + blockHash: string, + moduleError?: string | object; +} + +export interface ISubscribeBlockEventsData { + number: number; + hash: string; + timestamp: number; + events: IEvent[]; +} + +export interface ILogger { + log: (msg: any, level?: string) => void; + level: { + ERROR: 'ERROR'; + WARNING: 'WARNING'; + INFO: 'INFO'; + [key: string]: string; + } +} + +export interface IUniqueHelperLog { + executedAt: number; + executionTime: number; + type: 'extrinsic' | 'rpc'; + status: 'Fail' | 'Success'; + call: string; + params: any[]; + moduleError?: string; + dispatchError?: any; + events?: any; +} + +export interface IApiListeners { + connected?: (...args: any[]) => any; + disconnected?: (...args: any[]) => any; + error?: (...args: any[]) => any; + ready?: (...args: any[]) => any; + decorated?: (...args: any[]) => any; +} + +export type ICrossAccountId = { + Substrate: TSubstrateAccount; +} | { + Ethereum: TEthereumAccount; +} + +export type ICrossAccountIdLower = { + substrate: TSubstrateAccount; +} | { + ethereum: TEthereumAccount; +}; + +export interface IEthCrossAccountId { + 0: TEthereumAccount; + 1: TSubstrateAccount; + eth: TEthereumAccount; + sub: TSubstrateAccount; +} + +export interface ICollectionLimits { + accountTokenOwnershipLimit?: number | null; + sponsoredDataSize?: number | null; + sponsoredDataRateLimit?: {blocks: number} | {sponsoringDisabled: null} | null; + tokenLimit?: number | null; + sponsorTransferTimeout?: number | null; + sponsorApproveTimeout?: number | null; + ownerCanTransfer?: boolean | null; + ownerCanDestroy?: boolean | null; + transfersEnabled?: boolean | null; +} + +export interface INestingPermissions { + tokenOwner?: boolean; + collectionAdmin?: boolean; + restricted?: number[] | null; +} + +export interface ICollectionPermissions { + access?: 'Normal' | 'AllowList'; + mintMode?: boolean; + nesting?: INestingPermissions; +} + +export interface IProperty { + key: string; + value?: string; +} + +export interface ITokenPropertyPermission { + key: string; + permission: { + mutable?: boolean; + tokenOwner?: boolean; + collectionAdmin?: boolean; + } +} + +export interface IToken { + collectionId: number; + tokenId: number; +} + +export interface IBlock { + extrinsics: IExtrinsic[] + header: { + parentHash: string, + number: number, + }; +} + +export interface IExtrinsic { + isSigned: boolean, + method: { + method: string, + section: string, + args: any[] + } +} + +export interface ICollectionFlags { + foreign: boolean, + erc721metadata: boolean, +} + +export enum CollectionFlag { + None = 0, + /// External collections can't be managed using `unique` api + External = 1, + /// Supports ERC721Metadata + Erc721metadata = 64, + /// Tokens in foreign collections can be transferred, but not burnt + Foreign = 128, +} + +export interface ICollectionCreationOptions { + name?: string | number[]; + description?: string | number[]; + tokenPrefix?: string | number[]; + mode?: { + nft?: null; + refungible?: null; + fungible?: number; + } + permissions?: ICollectionPermissions; + properties?: IProperty[]; + tokenPropertyPermissions?: ITokenPropertyPermission[]; + limits?: ICollectionLimits; + pendingSponsor?: ICrossAccountId; + adminList?: ICrossAccountId[]; + flags?: number[] | CollectionFlag[] , +} + +export interface IChainProperties { + ss58Format: number; + tokenDecimals: number[]; + tokenSymbol: string[] +} + +export interface ISubstrateBalance { + free: bigint, + reserved: bigint, + frozen: bigint, +} + +export interface IStakingInfo { + block: bigint, + amount: bigint, +} + +export interface IPovInfo { + proofSize: number, + compactProofSize: number, + compressedProofSize: number, + results: any[], + kv: any, +} + +export interface ISchedulerOptions { + scheduledId?: string, + priority?: number, + periodic?: { + period: number, + repetitions: number, + }, +} + +export interface DemocracySplitAccount { + aye: bigint, + nay: bigint, +} + +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' +export type TCollectionMode = 'nft' | 'rft' | 'ft'; --- /dev/null +++ b/js-packages/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 --- /dev/null +++ b/js-packages/playgrounds/unique.dev.ts @@ -0,0 +1,1547 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +import '@unique/opal-types/augment-api.js'; +import '@unique/opal-types/augment-types.js'; +import '@unique/opal-types/types-lookup.js'; + +import {stringToU8a} from '@polkadot/util'; +import {blake2AsHex, encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto'; +import type {ChainHelperBaseConstructor, UniqueHelperConstructor} from './unique.js'; +import {UniqueHelper, ChainHelperBase, HelperGroup} from './unique.js'; +import {ApiPromise, Keyring, WsProvider} from '@polkadot/api'; +import * as defs from '@unique/opal-types/definitions.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import type {EventRecord} from '@polkadot/types/interfaces'; +import type {ICrossAccountId, ILogger, IPovInfo, ISchedulerOptions, ITransactionResult, TSigner} from './types.js'; +import type {FrameSystemEventRecord, StagingXcmV2TraitsError, StagingXcmV3TraitsOutcome} from '@polkadot/types/lookup'; +import type {SignerOptions, VoidFn} from '@polkadot/api/types'; +import {spawnSync} from 'child_process'; +import {AcalaHelper, AstarHelper, MoonbeamHelper, PolkadexHelper, RelayHelper, WestmintHelper, ForeignAssetsGroup, XcmGroup, XTokensGroup, TokensGroup} from './unique.xcm.js'; +import {CollectiveGroup, CollectiveMembershipGroup, DemocracyGroup, RankedCollectiveGroup, ReferendaGroup} from './unique.governance.js'; +import type {ICollectiveGroup, IFellowshipGroup} from './unique.governance.js'; + +export class SilentLogger { + log(_msg: any, _level: any): void { } + level = { + ERROR: 'ERROR' as const, + WARNING: 'WARNING' as const, + INFO: 'INFO' as const, + }; +} + +export class SilentConsole { + // TODO: Remove, this is temporary: Filter unneeded API output + // (Jaco promised it will be removed in the next version) + consoleErr: any; + consoleLog: any; + consoleWarn: any; + + constructor() { + this.consoleErr = console.error; + this.consoleLog = console.log; + this.consoleWarn = console.warn; + } + + enable() { + const outFn = (printer: any) => (...args: any[]) => { + for(const arg of args) { + if(typeof arg !== 'string') + continue; + const skippedWarnings = ['1000:: Normal connection closure', 'Not decorating unknown runtime apis:', 'RPC methods not decorated:', 'Not decorating runtime apis', 'Bad input data provided to validate_transaction', 'account balance too low', '1006:: Abnormal Closure']; + const needToSkip = skippedWarnings.reduce((a, b) => a || arg.includes(b), false); + if(needToSkip || arg === 'Normal connection closure') + return; + } + printer(...args); + }; + + console.error = outFn(this.consoleErr.bind(console)); + console.log = outFn(this.consoleLog.bind(console)); + console.warn = outFn(this.consoleWarn.bind(console)); + } + + disable() { + console.error = this.consoleErr; + console.log = this.consoleLog; + console.warn = this.consoleWarn; + } +} + +export interface IEventHelper { + section(): string; + + method(): string; + + wrapEvent(data: any[]): any; +} + +// eslint-disable-next-line @typescript-eslint/naming-convention +function EventHelper(section: string, method: string, wrapEvent: (data: any[]) => any) { + const helperClass = class implements IEventHelper { + wrapEvent: (data: any[]) => any; + _section: string; + _method: string; + + constructor() { + this.wrapEvent = wrapEvent; + this._section = section; + this._method = method; + } + + section(): string { + return this._section; + } + + method(): string { + return this._method; + } + + filter(txres: ITransactionResult) { + return txres.result.events.filter(e => e.event.section === section && e.event.method === method) + .map(e => this.wrapEvent(e.event.data)); + } + + find(txres: ITransactionResult) { + const e = txres.result.events.find(e => e.event.section === section && e.event.method === method); + return e ? this.wrapEvent(e.event.data) : null; + } + + expect(txres: ITransactionResult) { + const e = this.find(txres); + if(e) { + return e; + } else { + throw Error(`Expected event ${section}.${method}`); + } + } + }; + + return helperClass; +} + +function eventJsonData(data: any[], index: number) { + return data[index].toJSON() as T; +} + +function eventHumanData(data: any[], index: number) { + return data[index].toHuman(); +} + +function eventData(data: any[], index: number) { + return data[index] as T; +} + +// eslint-disable-next-line @typescript-eslint/naming-convention +function EventSection(section: string) { + return class Section { + static section = section; + + static Method(name: string, wrapEvent: (data: any[]) => any = () => {}) { + const helperClass = EventHelper(Section.section, name, wrapEvent); + return new helperClass(); + } + }; +} + +function schedulerSection(schedulerInstance: string) { + return class extends EventSection(schedulerInstance) { + static Dispatched = this.Method('Dispatched', data => ({ + task: eventJsonData(data, 0), + id: eventHumanData(data, 1), + result: data[2], + })); + + static PriorityChanged = this.Method('PriorityChanged', data => ({ + task: eventJsonData(data, 0), + priority: eventJsonData(data, 1), + })); + }; +} + +export class Event { + static Democracy = class extends EventSection('democracy') { + static Proposed = this.Method('Proposed', data => ({ + proposalIndex: eventJsonData(data, 0), + })); + + static ExternalTabled = this.Method('ExternalTabled'); + + static Started = this.Method('Started', data => ({ + referendumIndex: eventJsonData(data, 0), + threshold: eventHumanData(data, 1), + })); + + static Voted = this.Method('Voted', data => ({ + voter: eventJsonData(data, 0), + referendumIndex: eventJsonData(data, 1), + vote: eventJsonData(data, 2), + })); + + static Passed = this.Method('Passed', data => ({ + referendumIndex: eventJsonData(data, 0), + })); + + static ProposalCanceled = this.Method('ProposalCanceled', data => ({ + propIndex: eventJsonData(data, 0), + })); + + static Cancelled = this.Method('Cancelled', data => ({ + propIndex: eventJsonData(data, 0), + })); + + static Vetoed = this.Method('Vetoed', data => ({ + who: eventHumanData(data, 0), + proposalHash: eventHumanData(data, 1), + until: eventJsonData(data, 1), + })); + }; + + static Council = class extends EventSection('council') { + static Proposed = this.Method('Proposed', data => ({ + account: eventHumanData(data, 0), + proposalIndex: eventJsonData(data, 1), + proposalHash: eventHumanData(data, 2), + threshold: eventJsonData(data, 3), + })); + static Closed = this.Method('Closed', data => ({ + proposalHash: eventHumanData(data, 0), + yes: eventJsonData(data, 1), + no: eventJsonData(data, 2), + })); + static Executed = this.Method('Executed', data => ({ + proposalHash: eventHumanData(data, 0), + })); + }; + + static TechnicalCommittee = class extends EventSection('technicalCommittee') { + static Proposed = this.Method('Proposed', data => ({ + account: eventHumanData(data, 0), + proposalIndex: eventJsonData(data, 1), + proposalHash: eventHumanData(data, 2), + threshold: eventJsonData(data, 3), + })); + static Closed = this.Method('Closed', data => ({ + proposalHash: eventHumanData(data, 0), + yes: eventJsonData(data, 1), + no: eventJsonData(data, 2), + })); + static Approved = this.Method('Approved', data => ({ + proposalHash: eventHumanData(data, 0), + })); + static Executed = this.Method('Executed', data => ({ + proposalHash: eventHumanData(data, 0), + result: eventHumanData(data, 1), + })); + }; + + static FellowshipReferenda = class extends EventSection('fellowshipReferenda') { + static Submitted = this.Method('Submitted', data => ({ + referendumIndex: eventJsonData(data, 0), + trackId: eventJsonData(data, 1), + proposal: eventJsonData(data, 2), + })); + + static Cancelled = this.Method('Cancelled', data => ({ + index: eventJsonData(data, 0), + tally: eventJsonData(data, 1), + })); + }; + + static UniqueScheduler = schedulerSection('uniqueScheduler'); + static Scheduler = schedulerSection('scheduler'); + + static XcmpQueue = class extends EventSection('xcmpQueue') { + static XcmpMessageSent = this.Method('XcmpMessageSent', data => ({ + messageHash: eventJsonData(data, 0), + })); + + static Success = this.Method('Success', data => ({ + messageHash: eventJsonData(data, 0), + })); + + static Fail = this.Method('Fail', data => ({ + messageHash: eventJsonData(data, 0), + outcome: eventData(data, 2), + })); + }; + + static DmpQueue = class extends EventSection('dmpQueue') { + static ExecutedDownward = this.Method('ExecutedDownward', data => ({ + outcome: eventData(data, 2), + })); + }; +} + +// eslint-disable-next-line @typescript-eslint/naming-convention +export function SudoHelper(Base: T) { + return class extends Base { + constructor(...args: any[]) { + super(...args); + } + + override async executeExtrinsic( + sender: IKeyringPair, + extrinsic: string, + params: any[], + expectSuccess?: boolean, + options: Partial | null = null, + ): Promise { + 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; + } + override async executeExtrinsicUncheckedWeight( + sender: IKeyringPair, + extrinsic: string, + params: any[], + expectSuccess?: boolean, + options: Partial | null = null, + ): Promise { + 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 { + 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( + executionBlockNumber: number, + options: ISchedulerOptions = {}, + ) { + return this.schedule('schedule', executionBlockNumber, options); + } + + scheduleAfter( + blocksBeforeExecution: number, + options: ISchedulerOptions = {}, + ) { + return this.schedule('scheduleAfter', blocksBeforeExecution, options); + } + + schedule( + 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 { + //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 { + 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 { + 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 { + 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 { + 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 { + return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman()); + } +} + +export class DevUniqueHelper extends UniqueHelper { + /** + * Arrange methods for tests + */ + arrange: ArrangeGroup; + wait: WaitGroup; + admin: AdminGroup; + session: SessionGroup; + testUtils: TestUtilGroup; + foreignAssets: ForeignAssetsGroup; + xcm: XcmGroup; + xTokens: XTokensGroup; + tokens: TokensGroup; + 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; + + super(logger, options); + this.arrange = new ArrangeGroup(this); + this.wait = new WaitGroup(this); + 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); + } + + override async connect(wsEndpoint: string, _listeners?: any): Promise { + if(!wsEndpoint) throw new Error('wsEndpoint was not set'); + const wsProvider = new WsProvider(wsEndpoint); + this.api = new ApiPromise({ + provider: wsProvider, + signedExtensions: { + ContractHelpers: { + extrinsic: {}, + payload: {}, + }, + CheckMaintenance: { + extrinsic: {}, + payload: {}, + }, + DisableIdentityCalls: { + extrinsic: {}, + payload: {}, + }, + FakeTransactionFinalizer: { + extrinsic: {}, + payload: {}, + }, + }, + rpc: { + unique: defs.unique.rpc, + appPromotion: defs.appPromotion.rpc, + povinfo: defs.povinfo.rpc, + eth: { + feeHistory: { + description: 'Dummy', + params: [], + type: 'u8', + }, + maxPriorityFeePerGas: { + description: 'Dummy', + params: [], + type: 'u8', + }, + }, + }, + }); + await this.api.isReadyOrError; + this.network = await UniqueHelper.detectNetwork(this.api); + this.wsEndpoint = wsEndpoint; + } + getSudo() { + // eslint-disable-next-line @typescript-eslint/naming-convention + const SudoHelperType = SudoHelper(this.helperBase); + return this.clone(SudoHelperType) as T; + } +} + +export class DevRelayHelper extends RelayHelper { + wait: WaitGroup; + + constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { + options.helperBase = options.helperBase ?? DevRelayHelper; + + super(logger, options); + this.wait = new WaitGroup(this); + } + + getSudo() { + // eslint-disable-next-line @typescript-eslint/naming-convention + const SudoHelperType = SudoHelper(this.helperBase); + return this.clone(SudoHelperType) as DevRelayHelper; + } +} + +export class DevWestmintHelper extends WestmintHelper { + wait: WaitGroup; + + constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { + options.helperBase = options.helperBase ?? DevWestmintHelper; + + super(logger, options); + this.wait = new WaitGroup(this); + } +} + +export class DevStatemineHelper extends DevWestmintHelper {} + +export class DevStatemintHelper extends DevWestmintHelper {} + +export class DevMoonbeamHelper extends MoonbeamHelper { + account: MoonbeamAccountGroup; + wait: WaitGroup; + fastDemocracy: MoonbeamFastDemocracyGroup; + + constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { + options.helperBase = options.helperBase ?? DevMoonbeamHelper; + options.notePreimagePallet = options.notePreimagePallet ?? 'preimage'; + + super(logger, options); + this.account = new MoonbeamAccountGroup(this); + this.wait = new WaitGroup(this); + this.fastDemocracy = new MoonbeamFastDemocracyGroup(this); + } +} + +export class DevMoonriverHelper extends DevMoonbeamHelper { + constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { + options.notePreimagePallet = options.notePreimagePallet ?? 'preimage'; + super(logger, options); + } +} + +export class DevAstarHelper extends AstarHelper { + wait: WaitGroup; + + constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { + options.helperBase = options.helperBase ?? DevAstarHelper; + + super(logger, options); + this.wait = new WaitGroup(this); + } + + getSudo() { + // 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; + + 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); + } + getSudo() { + // eslint-disable-next-line @typescript-eslint/naming-convention + const SudoHelperType = SudoHelper(this.helperBase); + return this.clone(SudoHelperType) as DevAcalaHelper; + } +} + +export class DevPolkadexHelper extends PolkadexHelper { + wait: WaitGroup; + constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) { + options.helperBase = options.helperBase ?? PolkadexHelper; + + super(logger, options); + this.wait = new WaitGroup(this); + } + + getSudo() { + // eslint-disable-next-line @typescript-eslint/naming-convention + const SudoHelperType = SudoHelper(this.helperBase); + return this.clone(SudoHelperType) as DevPolkadexHelper; + } +} + +export class DevKaruraHelper extends DevAcalaHelper {} + +export class ArrangeGroup { + helper: DevUniqueHelper; + + scheduledIdSlider = 0; + + constructor(helper: DevUniqueHelper) { + this.helper = helper; + } + + /** + * Generates accounts with the specified UNQ token balance + * @param balances balances for generated accounts. Each balance will be multiplied by the token nominal. + * @param donor donor account for balances + * @returns array of newly created accounts + * @example const [acc1, acc2, acc3] = await createAccounts([0n, 10n, 20n], donor); + */ + createAccounts = async (balances: bigint[], donor: IKeyringPair): Promise => { + let nonce = await this.helper.chain.getNonce(donor.address); + const wait = new WaitGroup(this.helper); + const ss58Format = this.helper.chain.getChainProperties().ss58Format; + const tokenNominal = this.helper.balance.getOneTokenNominal(); + const transactions = []; + const accounts: IKeyringPair[] = []; + for(const balance of balances) { + const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format); + accounts.push(recipient); + if(balance !== 0n) { + const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recipient.address}, balance * tokenNominal]); + transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation')); + nonce++; + } + } + + await Promise.all(transactions).catch(_e => {}); + + //#region TODO remove this region, when nonce problem will be solved + const checkBalances = async () => { + let isSuccess = true; + for(let i = 0; i < balances.length; i++) { + const balance = await this.helper.balance.getSubstrate(accounts[i].address); + if(balance !== balances[i] * tokenNominal) { + isSuccess = false; + break; + } + } + return isSuccess; + }; + + let accountsCreated = false; + const maxBlocksChecked = await this.helper.arrange.isDevNode() ? 50 : 5; + // checkBalances retry up to 5-50 blocks + for(let index = 0; index < maxBlocksChecked; index++) { + accountsCreated = await checkBalances(); + if(accountsCreated) break; + await wait.newBlocks(1); + } + + if(!accountsCreated) throw Error('Accounts generation failed'); + //#endregion + + return accounts; + }; + + // TODO combine this method and createAccounts into one + createCrowd = async (accountsToCreate: number, withBalance: bigint, donor: IKeyringPair): Promise => { + const createAsManyAsCan = async () => { + let transactions: any = []; + const accounts: IKeyringPair[] = []; + let nonce = await this.helper.chain.getNonce(donor.address); + const tokenNominal = this.helper.balance.getOneTokenNominal(); + const ss58Format = this.helper.chain.getChainProperties().ss58Format; + for(let i = 0; i < accountsToCreate; i++) { + if(i === 500) { // if there are too many accounts to create + await Promise.allSettled(transactions); // wait while first 500 (should be 100 for devnode) tx will be settled + transactions = []; // + nonce = await this.helper.chain.getNonce(donor.address); // update nonce + } + const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format); + accounts.push(recipient); + if(withBalance !== 0n) { + const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recipient.address}, withBalance * tokenNominal]); + transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation')); + nonce++; + } + } + + const fullfilledAccounts = []; + await Promise.allSettled(transactions); + for(const account of accounts) { + const accountBalance = await this.helper.balance.getSubstrate(account.address); + if(accountBalance === withBalance * tokenNominal) { + fullfilledAccounts.push(account); + } + } + return fullfilledAccounts; + }; + + + const crowd: IKeyringPair[] = []; + // do up to 5 retries + for(let index = 0; index < 5 && accountsToCreate !== 0; index++) { + const asManyAsCan = await createAsManyAsCan(); + crowd.push(...asManyAsCan); + accountsToCreate -= asManyAsCan.length; + } + + if(accountsToCreate !== 0) throw Error(`Crowd generation failed: ${accountsToCreate} accounts left`); + + return crowd; + }; + + /** + * Generates one account with zero balance + * @returns the newly generated account + * @example const account = await helper.arrange.createEmptyAccount(); + */ + createEmptyAccount = (): IKeyringPair => { + const ss58Format = this.helper.chain.getChainProperties().ss58Format; + return this.helper.util.fromSeed(mnemonicGenerate(), ss58Format); + }; + + isDevNode = async () => { + let blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON(); + if(blockNumber == 0) { + await this.helper.wait.newBlocks(1); + blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON(); + } + const block2 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])]); + const block1 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber - 1])]); + const findCreationDate = (block: any) => { + const humanBlock = block.toHuman(); + let date; + humanBlock.block.extrinsics.forEach((ext: any) => { + if(ext.method.section === 'timestamp') { + date = Number(ext.method.args.now.replaceAll(',', '')); + } + }); + return date; + }; + const block1date = await findCreationDate(block1); + const block2date = await findCreationDate(block2); + if(block2date! - block1date! < 9000) return true; + return false; + }; + + async calculcateFee(payer: ICrossAccountId, promise: () => Promise): Promise { + const address = 'Substrate' in payer ? payer.Substrate : this.helper.address.ethToSubstrate(payer.Ethereum); + let balance = await this.helper.balance.getSubstrate(address); + + await promise(); + + balance -= await this.helper.balance.getSubstrate(address); + + return balance; + } + + async calculatePoVInfo(txs: any[]): Promise { + const rawPovInfo = await this.helper.callRpc('api.rpc.povinfo.estimateExtrinsicPoV', [txs]); + + const kvJson: {[key: string]: string} = {}; + + for(const kv of rawPovInfo.keyValues) { + kvJson[kv.key.toHex()] = kv.value.toHex(); + } + + const kvStr = JSON.stringify(kvJson); + + const chainql = spawnSync( + 'chainql', + [ + `--tla-code=data=${kvStr}`, + '-e', `function(data) cql.dump(cql.chain("${this.helper.getEndpoint()}").latest._meta, data, {omit_empty:true})`, + ], + ); + + if(!chainql.stdout) { + throw Error('unable to get an output from the `chainql`'); + } + + return { + proofSize: rawPovInfo.proofSize.toNumber(), + compactProofSize: rawPovInfo.compactProofSize.toNumber(), + compressedProofSize: rawPovInfo.compressedProofSize.toNumber(), + results: rawPovInfo.results, + kv: JSON.parse(chainql.stdout.toString()), + }; + } + + calculatePalletAddress(palletId: any) { + const address = stringToU8a(('modl' + palletId).padEnd(32, '\0')); + return encodeAddress(address, this.helper.chain.getChainProperties().ss58Format); + } + + makeScheduledIds(num: number): string[] { + function makeId(slider: number) { + const scheduledIdSize = 64; + const hexId = slider.toString(16); + const prefixSize = scheduledIdSize - hexId.length; + + const scheduledId = '0x' + '0'.repeat(prefixSize) + hexId; + + return scheduledId; + } + + const ids = []; + for(let i = 0; i < num; i++) { + ids.push(makeId(this.scheduledIdSlider)); + this.scheduledIdSlider += 1; + } + + return ids; + } + + makeScheduledId(): string { + return (this.makeScheduledIds(1))[0]; + } + + async captureEvents(eventSection: string, eventMethod: string): Promise { + const capture = new EventCapture(this.helper, eventSection, eventMethod); + await capture.startCapture(); + + return capture; + } + + makeXcmProgramWithdrawDeposit(beneficiary: Uint8Array, id: any, amount: bigint) { + return { + V2: [ + { + WithdrawAsset: [ + { + id, + fun: { + Fungible: amount, + }, + }, + ], + }, + { + BuyExecution: { + fees: { + id, + fun: { + Fungible: amount, + }, + }, + weightLimit: 'Unlimited', + }, + }, + { + DepositAsset: { + assets: { + Wild: 'All', + }, + maxAssets: 1, + beneficiary: { + parents: 0, + interior: { + X1: { + AccountId32: { + network: 'Any', + id: beneficiary, + }, + }, + }, + }, + }, + }, + ], + }; + } + + makeXcmProgramReserveAssetDeposited(beneficiary: Uint8Array, id: any, amount: bigint) { + return { + V2: [ + { + ReserveAssetDeposited: [ + { + id, + fun: { + Fungible: amount, + }, + }, + ], + }, + { + BuyExecution: { + fees: { + id, + fun: { + Fungible: amount, + }, + }, + weightLimit: 'Unlimited', + }, + }, + { + DepositAsset: { + assets: { + Wild: 'All', + }, + maxAssets: 1, + beneficiary: { + parents: 0, + interior: { + X1: { + AccountId32: { + network: 'Any', + id: beneficiary, + }, + }, + }, + }, + }, + }, + ], + }; + } + + makeUnpaidSudoTransactProgram(info: {weightMultiplier: number, call: string}) { + return { + V3: [ + { + UnpaidExecution: { + weightLimit: 'Unlimited', + checkOrigin: null, + }, + }, + { + Transact: { + originKind: 'Superuser', + requireWeightAtMost: { + refTime: info.weightMultiplier * 200000000, + proofSize: info.weightMultiplier * 3000, + }, + call: { + encoded: info.call, + }, + }, + }, + ], + }; + } +} + +class MoonbeamAccountGroup { + helper: MoonbeamHelper; + + keyring: Keyring; + _alithAccount: IKeyringPair; + _baltatharAccount: IKeyringPair; + _dorothyAccount: IKeyringPair; + + constructor(helper: MoonbeamHelper) { + this.helper = helper; + + this.keyring = new Keyring({type: 'ethereum'}); + const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133'; + const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b'; + const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68'; + + this._alithAccount = this.keyring.addFromUri(alithPrivateKey, undefined, 'ethereum'); + this._baltatharAccount = this.keyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum'); + this._dorothyAccount = this.keyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum'); + } + + alithAccount() { + return this._alithAccount; + } + + baltatharAccount() { + return this._baltatharAccount; + } + + dorothyAccount() { + return this._dorothyAccount; + } + + create() { + return this.keyring.addFromUri(mnemonicGenerate()); + } +} + +class MoonbeamFastDemocracyGroup { + helper: DevMoonbeamHelper; + + constructor(helper: DevMoonbeamHelper) { + this.helper = helper; + } + + async executeProposal(proposalDesciption: string, encodedProposal: string) { + const proposalHash = blake2AsHex(encodedProposal); + + const alithAccount = this.helper.account.alithAccount(); + const baltatharAccount = this.helper.account.baltatharAccount(); + const dorothyAccount = this.helper.account.dorothyAccount(); + + const councilVotingThreshold = 2; + const technicalCommitteeThreshold = 2; + const fastTrackVotingPeriod = 3; + const fastTrackDelayPeriod = 0; + + console.log(`[democracy] executing '${proposalDesciption}' proposal`); + + // >>> Propose external motion through council >>> + console.log('\t* Propose external motion through council.......'); + const externalMotion = this.helper.democracy.externalProposeMajority({Inline: encodedProposal}); + const encodedMotion = externalMotion?.method.toHex() || ''; + const motionHash = blake2AsHex(encodedMotion); + console.log('\t* Motion hash is %s', motionHash); + + await this.helper.collective.council.propose( + baltatharAccount, + councilVotingThreshold, + externalMotion, + externalMotion.encodedLength, + ); + + const councilProposalIdx = await this.helper.collective.council.proposalCount() - 1; + await this.helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true); + await this.helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true); + + await this.helper.collective.council.close( + dorothyAccount, + motionHash, + councilProposalIdx, + { + refTime: 1_000_000_000, + proofSize: 1_000_000, + }, + externalMotion.encodedLength, + ); + console.log('\t* Propose external motion through council.......DONE'); + // <<< Propose external motion through council <<< + + // >>> Fast track proposal through technical committee >>> + console.log('\t* Fast track proposal through technical committee.......'); + const fastTrack = this.helper.democracy.fastTrack(proposalHash, fastTrackVotingPeriod, fastTrackDelayPeriod); + const encodedFastTrack = fastTrack?.method.toHex() || ''; + const fastTrackHash = blake2AsHex(encodedFastTrack); + console.log('\t* FastTrack hash is %s', fastTrackHash); + + await this.helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength); + + const techProposalIdx = await this.helper.collective.techCommittee.proposalCount() - 1; + await this.helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true); + await this.helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true); + + await this.helper.collective.techCommittee.close( + baltatharAccount, + fastTrackHash, + techProposalIdx, + { + refTime: 1_000_000_000, + proofSize: 1_000_000, + }, + fastTrack.encodedLength, + ); + console.log('\t* Fast track proposal through technical committee.......DONE'); + // <<< Fast track proposal through technical committee <<< + + const democracyStarted = await this.helper.wait.expectEvent(3, Event.Democracy.Started); + const referendumIndex = democracyStarted.referendumIndex; + + // >>> Referendum voting >>> + console.log(`\t* Referendum #${referendumIndex} voting.......`); + await this.helper.democracy.referendumVote(dorothyAccount, referendumIndex, { + balance: 10_000_000_000_000_000_000n, + vote: {aye: true, conviction: 1}, + }); + console.log(`\t* Referendum #${referendumIndex} voting.......DONE`); + // <<< Referendum voting <<< + + // Wait the proposal to pass + await this.helper.wait.expectEvent(3, Event.Democracy.Passed, event => event.referendumIndex == referendumIndex); + + await this.helper.wait.newBlocks(1); + + console.log(`[democracy] executing '${proposalDesciption}' proposal.......DONE`); + } +} + +class WaitGroup { + helper: ChainHelperBase; + + constructor(helper: ChainHelperBase) { + this.helper = helper; + } + + sleep(milliseconds: number) { + return new Promise((resolve) => setTimeout(resolve, milliseconds)); + } + + private async waitWithTimeout(promise: Promise, timeout: number) { + let isBlock = false; + promise.then(() => isBlock = true).catch(() => isBlock = true); + let totalTime = 0; + const step = 100; + while(!isBlock) { + await this.sleep(step); + totalTime += step; + if(totalTime >= timeout) throw Error('Blocks production failed'); + } + return promise; + } + + /** + * Launch some async operation, or throw an error after some time. Note that it will still continue executing after the timeout. + * @param promise async operation to race against the timeout + * @param timeoutMS time after which to time out + * @param timeoutError error message to throw + * @returns promise of the same type the operation had + */ + withTimeout( + promise: Promise, + timeoutMS = 30000, + timeoutError = 'The operation has timed out!', + ): Promise { + const timeout = new Promise((_, reject) => { + setTimeout(() => { + reject(new Error(timeoutError)); + }, timeoutMS); + }); + + return Promise.race([promise, timeout]).catch(e => {throw new Error(e);}); + } + + /** + * Wait for specified number of blocks + * @param blocksCount number of blocks to wait + * @returns + */ + async newBlocks(blocksCount = 1, timeout?: number): Promise { + timeout = timeout ?? blocksCount * 60_000; + // eslint-disable-next-line no-async-promise-executor + const promise = new Promise(async (resolve) => { + const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(() => { + if(blocksCount > 0) { + blocksCount--; + } else { + unsubscribe(); + resolve(); + } + }); + }); + await this.waitWithTimeout(promise, timeout); + return promise; + } + + /** + * Wait for the specified number of sessions to pass. + * Only applicable if the Session pallet is turned on. + * @param sessionCount number of sessions to wait + * @param blockTimeout time in ms until panicking that the chain has stopped producing blocks + * @returns + */ + async newSessions(sessionCount = 1, blockTimeout = 60000): Promise { + console.log(`Waiting for ${sessionCount} new session${sessionCount > 1 ? 's' : ''}.` + + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.'); + + const expectedSessionIndex = await (this.helper as DevUniqueHelper).session.getIndex() + sessionCount; + let currentSessionIndex = -1; + + while(currentSessionIndex < expectedSessionIndex) { + // eslint-disable-next-line no-async-promise-executor + currentSessionIndex = await this.withTimeout(new Promise(async (resolve) => { + await this.newBlocks(1); + const res = await (this.helper as DevUniqueHelper).session.getIndex(); + resolve(res); + }), blockTimeout, 'The chain has stopped producing blocks!'); + } + } + + async forParachainBlockNumber(blockNumber: bigint | number, timeout?: number) { + timeout = timeout ?? 30 * 60 * 1000; + // eslint-disable-next-line no-async-promise-executor + const promise = new Promise(async (resolve) => { + const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => { + if(data.number.toNumber() >= blockNumber) { + unsubscribe(); + resolve(); + } + }); + }); + await this.waitWithTimeout(promise, timeout); + return promise; + } + + async forRelayBlockNumber(blockNumber: bigint | number, timeout?: number) { + timeout = timeout ?? 30 * 60 * 1000; + // eslint-disable-next-line no-async-promise-executor + const promise = new Promise(async (resolve) => { + const unsubscribe = await this.helper.getApi().query.parachainSystem.validationData((data: any) => { + if(data.value.relayParentNumber.toNumber() >= blockNumber) { + // @ts-ignore + unsubscribe(); + resolve(); + } + }); + }); + await this.waitWithTimeout(promise, timeout); + return promise; + } + + noScheduledTasks() { + const api = this.helper.getApi(); + + // eslint-disable-next-line no-async-promise-executor + const promise = new Promise(async resolve => { + const unsubscribe = await api.rpc.chain.subscribeNewHeads(async () => { + const areThereScheduledTasks = await api.query.scheduler.lookup.entries(); + + if(areThereScheduledTasks.length == 0) { + unsubscribe(); + resolve(); + } + }); + }); + + return promise; + } + + parachainBlockMultiplesOf(val: bigint) { + // eslint-disable-next-line no-async-promise-executor + const promise = new Promise(async resolve => { + const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => { + if(data.number.toBigInt() % val == 0n) { + console.log(`from waiter: ${data.number.toBigInt()}`); + unsubscribe(); + resolve(); + } + }); + }); + return promise; + } + + event( + maxBlocksToWait: number, + eventHelper: T, + filter: (_: any) => boolean = () => true, + ): any { + // eslint-disable-next-line no-async-promise-executor + const promise = new Promise(async (resolve) => { + const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(async header => { + const blockNumber = header.number.toJSON(); + const blockHash = header.hash; + const eventIdStr = `${eventHelper.section()}.${eventHelper.method()}`; + const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`; + + this.helper.logger.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`); + + const apiAt = await this.helper.getApi().at(blockHash); + const eventRecords = (await apiAt.query.system.events()) as any; + + const neededEvent = eventRecords.toArray() + .filter((r: FrameSystemEventRecord) => r.event.section == eventHelper.section() && r.event.method == eventHelper.method()) + .map((r: FrameSystemEventRecord) => eventHelper.wrapEvent(r.event.data)) + .find(filter); + + if(neededEvent) { + unsubscribe(); + resolve(neededEvent); + } else if(maxBlocksToWait > 0) { + maxBlocksToWait--; + } else { + this.helper.logger.log(`Eligible event \`${eventIdStr}\` is NOT found. + The wait lasted until block ${blockNumber} inclusive`); + unsubscribe(); + resolve(null); + } + }); + }); + return promise; + } + + async expectEvent( + maxBlocksToWait: number, + eventHelper: T, + filter: (e: any) => boolean = () => true, + ) { + const e = await this.event(maxBlocksToWait, eventHelper, filter); + if(e == null) { + throw Error(`The event '${eventHelper.section()}.${eventHelper.method()}' is expected`); + } else { + return e; + } + } +} + +class SessionGroup { + helper: ChainHelperBase; + + constructor(helper: ChainHelperBase) { + this.helper = helper; + } + + //todo:collator documentation + async getIndex(): Promise { + return (await this.helper.callRpc('api.query.session.currentIndex', [])).toNumber(); + } + + newSessions(sessionCount = 1, blockTimeout = 24000): Promise { + return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout); + } + + setOwnKeys(signer: TSigner, key: string) { + return this.helper.executeExtrinsic( + signer, + 'api.tx.session.setKeys', + [key, '0x0'], + true, + ); + } + + setOwnKeysFromAddress(signer: TSigner) { + return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex')); + } +} + +class TestUtilGroup { + helper: DevUniqueHelper; + + constructor(helper: DevUniqueHelper) { + this.helper = helper; + } + + async enable(testUtilsPalletName: string) { + if(this.helper.fetchMissingPalletNames([testUtilsPalletName]).length != 0) { + return; + } + + const signer = this.helper.util.fromSeed('//Alice'); + await this.helper.getSudo().executeExtrinsic(signer, 'api.tx.testUtils.enable', [], true); + } + + async setTestValue(signer: TSigner, testVal: number) { + await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValue', [testVal], true); + } + + async incTestValue(signer: TSigner) { + await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.incTestValue', [], true); + } + + async setTestValueAndRollback(signer: TSigner, testVal: number) { + await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValueAndRollback', [testVal], true); + } + + async testValue(blockIdx?: number) { + const api = blockIdx + ? await this.helper.getApi().at(await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockIdx])) + : this.helper.getApi(); + + return (await api.query.testUtils.testValue()).toJSON(); + } + + async justTakeFee(signer: TSigner) { + await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.justTakeFee', [], true); + } + + async selfCancelingInc(signer: TSigner, scheduledId: string, maxTestVal: number) { + await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.selfCancelingInc', [scheduledId, maxTestVal], true); + } +} + +class EventCapture { + helper: DevUniqueHelper; + eventSection: string; + eventMethod: string; + events: EventRecord[] = []; + unsubscribe: VoidFn | null = null; + + constructor( + helper: DevUniqueHelper, + eventSection: string, + eventMethod: string, + ) { + this.helper = helper; + this.eventSection = eventSection; + this.eventMethod = eventMethod; + } + + async startCapture() { + this.stopCapture(); + this.unsubscribe = (await this.helper.getApi().query.system.events((eventRecords: FrameSystemEventRecord[]) => { + const newEvents = eventRecords.filter(r => r.event.section == this.eventSection && r.event.method == this.eventMethod); + + this.events.push(...newEvents); + })) as any; + } + + stopCapture() { + if(this.unsubscribe !== null) { + this.unsubscribe(); + } + } + + extractCapturedEvents() { + return this.events; + } +} + +class AdminGroup { + helper: UniqueHelper; + + constructor(helper: UniqueHelper) { + this.helper = helper; + } + + async payoutStakers(signer: IKeyringPair, stakersToPayout: number): Promise<{staker: string, stake: bigint, payout: bigint}[]> { + const payoutResult = await this.helper.executeExtrinsic(signer, 'api.tx.appPromotion.payoutStakers', [stakersToPayout], true); + return payoutResult.result.events.filter(e => e.event.method === 'StakingRecalculation').map(e => ({ + staker: e.event.data[0].toString(), + stake: e.event.data[1].toBigInt(), + payout: e.event.data[2].toBigInt(), + })); + } +} + +// eslint-disable-next-line @typescript-eslint/naming-convention +function ScheduledUniqueHelper(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; + } + + override executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise { + 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, + ); + } + }; +} --- /dev/null +++ b/js-packages/playgrounds/unique.governance.ts @@ -0,0 +1,531 @@ +import {blake2AsHex} from '@polkadot/util-crypto'; +import type {PalletDemocracyConviction} from '@polkadot/types/lookup'; +import type {IPhasicEvent, TSigner} from './types.js'; +import {HelperGroup, UniqueHelper} from './unique.js'; + +export class CollectiveGroup extends HelperGroup { + /** + * 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]); + } +} + +export class CollectiveMembershipGroup extends HelperGroup { + /** + * 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`, []); + } +} + +export class RankedCollectiveGroup extends HelperGroup { + /** + * 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; + } +} + +export class ReferendaGroup extends HelperGroup { + /** + * 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; +} + +export class DemocracyGroup extends HelperGroup { + // 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); + }*/ +} --- /dev/null +++ b/js-packages/playgrounds/unique.ts @@ -0,0 +1,3498 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable function-call-argument-newline */ +/* eslint-disable no-prototype-builtins */ + +import {ApiPromise, WsProvider, Keyring} from '@polkadot/api'; +import type {SignerOptions} from '@polkadot/api/types'; +import type {AugmentedSubmittables} from '@polkadot/api-base/types/submittable'; +import type {ApiInterfaceEvents} from '@polkadot/api/types'; +import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {hexToU8a} from '@polkadot/util/hex'; +import {u8aConcat} from '@polkadot/util/u8a'; +import type { + IApiListeners, + IBlock, + IEvent, + IChainProperties, + ICollectionCreationOptions, + ICollectionLimits, + ICollectionPermissions, + ICrossAccountId, + ICrossAccountIdLower, + ILogger, + INestingPermissions, + IProperty, + IStakingInfo, + ISubstrateBalance, + IToken, + ITokenPropertyPermission, + ITransactionResult, + IUniqueHelperLog, + TApiAllowedListeners, + TEthereumAccount, + TSigner, + TSubstrateAccount, + TNetworks, + IEthCrossAccountId, +} from './types.js'; +import type {RuntimeDispatchInfo} from '@polkadot/types/interfaces'; + +export class CrossAccountId { + account: ICrossAccountId; + + constructor(account: ICrossAccountId) { + this.account = account; + } + + static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') { + switch (domain) { + case 'Substrate': return new CrossAccountId({Substrate: account.address}); + case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum(); + } + } + + static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId { + if('substrate' in address) return new CrossAccountId({Substrate: address.substrate}); + else return new CrossAccountId({Ethereum: address.ethereum}); + } + + static normalizeSubstrateAddress(address: {Substrate: TSubstrateAccount}, ss58Format = 42): TSubstrateAccount { + return encodeAddress(decodeAddress(address.Substrate), ss58Format); + } + + static withNormalizedSubstrate(address: ICrossAccountId, ss58Format = 42): ICrossAccountId { + if('Substrate' in address) return {Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)}; + return address; + } + + withNormalizedSubstrate(ss58Format = 42): CrossAccountId { + if('Substrate' in this.account) this.account = CrossAccountId.withNormalizedSubstrate(this.account, ss58Format); + return this; + } + + static translateSubToEth(address: TSubstrateAccount): TEthereumAccount { + return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join('')); + } + + toEthereum(): CrossAccountId { + this.account = CrossAccountId.toEthereum(this.account); + return this; + } + + static toEthereum(account: ICrossAccountId): ICrossAccountId { + if('Substrate' in account) return {Ethereum: CrossAccountId.translateSubToEth(account.Substrate)}; + return account; + } + + static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount { + return evmToAddress(address, ss58Format); + } + + toSubstrate(ss58Format?: number): CrossAccountId { + this.account = CrossAccountId.toSubstrate(this.account, ss58Format); + return this; + } + + static toSubstrate(account: ICrossAccountId, ss58Format?: number): ICrossAccountId { + if('Ethereum' in account) return {Substrate: CrossAccountId.translateEthToSub(account.Ethereum, ss58Format)}; + return account; + } + + toLowerCase(): CrossAccountId { + this.account = CrossAccountId.toLowerCase(this.account); + return this; + } + + static toLowerCase(account: ICrossAccountId) { + if('Substrate' in account) return {Substrate: account.Substrate.toLowerCase()}; + if('Ethereum' in account) return {Ethereum: account.Ethereum.toLowerCase()}; + return account; + } + + toICrossAccountId(): ICrossAccountId { + return this.account; + } +} + +const nesting = { + toChecksumAddress(address: string): string { + if(typeof address === 'undefined') return ''; + + if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`); + + address = address.toLowerCase().replace(/^0x/i, ''); + const addressHash = keccakAsHex(address).replace(/^0x/i, ''); + const checksumAddress = ['0x']; + + for(let i = 0; i < address.length; i++) { + // If ith character is 8 to f then make it uppercase + if(parseInt(addressHash[i], 16) > 7) { + checksumAddress.push(address[i].toUpperCase()); + } else { + checksumAddress.push(address[i]); + } + } + return checksumAddress.join(''); + }, + tokenIdToAddress(collectionId: number, tokenId: number) { + return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`); + }, +}; + +class UniqueUtil { + static transactionStatus = { + NOT_READY: 'NotReady', + FAIL: 'Fail', + SUCCESS: 'Success', + }; + + static chainLogType = { + EXTRINSIC: 'extrinsic', + RPC: 'rpc', + }; + + static getTokenAccount(token: IToken): ICrossAccountId { + return {Ethereum: this.getTokenAddress(token)}; + } + + static getTokenAddress(token: IToken): string { + return nesting.tokenIdToAddress(token.collectionId, token.tokenId); + } + + static getDefaultLogger(): ILogger { + return { + log(msg: any, level = 'INFO') { + console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg])); + }, + level: { + ERROR: 'ERROR', + WARNING: 'WARNING', + INFO: 'INFO', + }, + }; + } + + static vec2str(arr: string[] | number[]) { + return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join(''); + } + + static str2vec(string: string) { + if(typeof string !== 'string') return string; + return Array.from(string).map(x => x.charCodeAt(0)); + } + + static fromSeed(seed: string, ss58Format = 42) { + const keyring = new Keyring({type: 'sr25519', ss58Format}); + return keyring.addFromUri(seed); + } + + static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number { + if(creationResult.status !== this.transactionStatus.SUCCESS) { + throw Error('Unable to create collection!'); + } + + let collectionId = null; + creationResult.result.events.forEach(({event: {data, method, section}}) => { + if((section === 'common') && (method === 'CollectionCreated')) { + collectionId = parseInt(data[0].toString(), 10); + } + }); + + if(collectionId === null) { + throw Error('No CollectionCreated event was found!'); + } + + return collectionId; + } + + static extractTokensFromCreationResult(creationResult: ITransactionResult): { + success: boolean, + tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[], + } { + if(creationResult.status !== this.transactionStatus.SUCCESS) { + throw Error('Unable to create tokens!'); + } + let success = false; + const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[]; + creationResult.result.events.forEach(({event: {data, method, section}}) => { + if(method === 'ExtrinsicSuccess') { + success = true; + } else if((section === 'common') && (method === 'ItemCreated')) { + tokens.push({ + collectionId: parseInt(data[0].toString(), 10), + tokenId: parseInt(data[1].toString(), 10), + owner: data[2].toHuman(), + amount: data[3].toBigInt(), + }); + } + }); + return {success, tokens}; + } + + static extractTokensFromBurnResult(burnResult: ITransactionResult): { + success: boolean, + tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[], + } { + if(burnResult.status !== this.transactionStatus.SUCCESS) { + throw Error('Unable to burn tokens!'); + } + let success = false; + const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[]; + burnResult.result.events.forEach(({event: {data, method, section}}: any) => { + if(method === 'ExtrinsicSuccess') { + success = true; + } else if((section === 'common') && (method === 'ItemDestroyed')) { + tokens.push({ + collectionId: parseInt(data[0].toString(), 10), + tokenId: parseInt(data[1].toString(), 10), + owner: data[2].toHuman(), + amount: data[3].toBigInt(), + }); + } + }); + return {success, tokens}; + } + + static findCollectionInEvents(events: { event: IEvent }[], collectionId: number, expectedSection: string, expectedMethod: string): boolean { + let eventId = null; + events.forEach(({event: {data, method, section}}) => { + if((section === expectedSection) && (method === expectedMethod)) { + eventId = parseInt(data[0].toString(), 10); + } + }); + + if(eventId === null) { + throw Error(`No ${expectedMethod} event was found!`); + } + return eventId === collectionId; + } + + static isTokenTransferSuccess(events: { event: IEvent }[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { + const normalizeAddress = (address: string | ICrossAccountId) => { + if(typeof address === 'string') return address; + if('Substrate' in address) return CrossAccountId.withNormalizedSubstrate(address); + if('Ethereum' in address) return CrossAccountId.toLowerCase(address); + return address; + }; + let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any; + events.forEach(({event: {data, method, section}}) => { + if((section === 'common') && (method === 'Transfer')) { + transfer = { + collectionId: data[0].toJSON(), + tokenId: data[1].toJSON(), + from: normalizeAddress(data[2].toHuman()), + to: normalizeAddress(data[3].toHuman()), + amount: BigInt(data[4].toJSON()), + }; + } + }); + let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId; + isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from); + isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to); + isSuccess = isSuccess && amount === transfer.amount; + return isSuccess; + } + + static bigIntToDecimals(number: bigint, decimals = 18) { + const numberStr = number.toString(); + const dotPos = numberStr.length - decimals; + + if(dotPos <= 0) { + return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr; + } else { + const intPart = numberStr.substring(0, dotPos); + const fractPart = numberStr.substring(dotPos); + return intPart + '.' + fractPart; + } + } +} + +class UniqueEventHelper { + static extractIndex(index: any): [number, number] | string { + if(index.toRawType() === '[u8;2]') return [index[0], index[1]]; + return index.toJSON(); + } + + static extractSub(data: any, subTypes: any): { [key: string]: any } { + let obj: any = {}; + let index = 0; + + if(data.entries) { + for(const [key, value] of data.entries()) { + obj[key] = this.extractData(value, subTypes[index]); + index++; + } + } else obj = data.toJSON(); + + return obj; + } + + static toHuman(data: any) { + return data && data.toHuman ? data.toHuman() : `${data}`; + } + + static extractData(data: any, type: any): any { + if(!type) return this.toHuman(data); + if(['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber(); + if(['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt(); + if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub); + return this.toHuman(data); + } + + public static extractEvents(events: { event: any, phase: any }[]): IEvent[] { + const parsedEvents: IEvent[] = []; + + events.forEach((record) => { + const {event, phase} = record; + const types = event.typeDef; + + const eventData: IEvent = { + section: event.section.toString(), + method: event.method.toString(), + index: this.extractIndex(event.index), + data: [], + phase: phase.toJSON(), + }; + + event.data.forEach((val: any, index: number) => { + eventData.data.push(this.extractData(val, types[index])); + }); + + parsedEvents.push(eventData); + }); + + return parsedEvents; + } +} +const InvalidTypeSymbol = Symbol('Invalid type'); +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export type Invalid = + | (( + invalidType: typeof InvalidTypeSymbol, + ..._: typeof InvalidTypeSymbol[] + ) => typeof InvalidTypeSymbol) + | null + | undefined; +// Has slightly better error messages than Get +type Get2 = + P extends `${infer Key}.${infer Key2}` ? Key extends keyof T ? Key2 extends keyof T[Key] ? T[Key][Key2] : E : E : E; +type ForceFunction = T extends (...args: any) => any ? T : (...args: any) => Invalid; + +export class ChainHelperBase { + helperBase: any; + + transactionStatus = UniqueUtil.transactionStatus; + chainLogType = UniqueUtil.chainLogType; + util: typeof UniqueUtil; + eventHelper: typeof UniqueEventHelper; + logger: ILogger; + api: ApiPromise | null; + forcedNetwork: TNetworks | null; + network: TNetworks | null; + wsEndpoint: string | null; + chainLog: IUniqueHelperLog[]; + children: ChainHelperBase[]; + address: AddressGroup; + chain: ChainGroup; + + constructor(logger?: ILogger, helperBase?: any) { + this.helperBase = helperBase; + + this.util = UniqueUtil; + this.eventHelper = UniqueEventHelper; + if(typeof logger == 'undefined') logger = this.util.getDefaultLogger(); + this.logger = logger; + this.api = null; + this.forcedNetwork = null; + this.network = null; + this.wsEndpoint = null; + this.chainLog = []; + this.children = []; + this.address = new AddressGroup(this); + this.chain = new ChainGroup(this); + } + + clone(helperCls: ChainHelperBaseConstructor, options: { [key: string]: any } = {}) { + Object.setPrototypeOf(helperCls.prototype, this); + const newHelper = new helperCls(this.logger, options); + + newHelper.api = this.api; + newHelper.network = this.network; + newHelper.forceNetwork = this.forceNetwork; + + this.children.push(newHelper); + + return newHelper; + } + + getEndpoint(): string { + if(this.wsEndpoint === null) throw Error('No connection was established'); + return this.wsEndpoint; + } + + getApi(): ApiPromise { + if(this.api === null) throw Error('API not initialized'); + return this.api; + } + + async subscribeEvents(expectedEvents: { section: string, names: string[] }[]) { + const collectedEvents: IEvent[] = []; + const unsubscribe = await this.getApi().query.system.events((events: any) => { + const ievents = this.eventHelper.extractEvents(events); + ievents.forEach((event) => { + expectedEvents.forEach((e => { + if(event.section === e.section && e.names.includes(event.method)) { + collectedEvents.push(event); + } + })); + }); + }); + return {unsubscribe: unsubscribe as any, collectedEvents}; + } + + clearChainLog(): void { + this.chainLog = []; + } + + forceNetwork(value: TNetworks): void { + this.forcedNetwork = value; + } + + async connect(wsEndpoint: string, listeners?: IApiListeners) { + if(this.api !== null) throw Error('Already connected'); + const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork); + this.wsEndpoint = wsEndpoint; + this.api = api; + this.network = network; + } + + async disconnect() { + for(const child of this.children) { + child.clearApi(); + } + + if(this.api === null) return; + await this.api.disconnect(); + this.clearApi(); + } + + clearApi() { + this.api = null; + this.network = null; + } + + static async detectNetwork(api: ApiPromise): Promise { + const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any; + const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver']; + + if(xcmChains.indexOf(spec.specName) > -1) return spec.specName; + + if(['quartz', 'unique', 'sapphire'].indexOf(spec.specName) > -1) return spec.specName; + return 'opal'; + } + + static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise { + if(!wsEndpoint) throw new Error('wsEndpoint was not set'); + const api = new ApiPromise({provider: new WsProvider(wsEndpoint)}); + await api.isReady; + + const network = await this.detectNetwork(api); + + await api.disconnect(); + + return network; + } + + static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{ + api: ApiPromise; + network: TNetworks; + }> { + if(typeof network === 'undefined' || network === null) network = 'opal'; + if(!wsEndpoint) throw new Error('wsEndpoint was not set'); + const supportedRPC = { + opal: { + unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc, + }, + quartz: { + unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc, + }, + unique: { + unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc, + }, + rococo: {}, + westend: {}, + moonbeam: {}, + moonriver: {}, + acala: {}, + karura: {}, + westmint: {}, + }; + if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint); + const rpc = supportedRPC[network] as any; + + // TODO: investigate how to replace rpc in runtime + // api._rpcCore.addUserInterfaces(rpc); + + const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc}); + + await api.isReadyOrError; + + if(typeof listeners === 'undefined') listeners = {}; + for(const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) { + if(!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue; + api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any); + } + + return {api, network}; + } + + getTransactionStatus(data: { events: { event: IEvent }[], status: any }) { + const {events, status} = data; + if(status.isReady) { + return this.transactionStatus.NOT_READY; + } + if(status.isBroadcast) { + return this.transactionStatus.NOT_READY; + } + if(status.isInBlock || status.isFinalized) { + const errors = events.filter(e => e.event.method === 'ExtrinsicFailed'); + if(errors.length > 0) { + return this.transactionStatus.FAIL; + } + if(events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) { + return this.transactionStatus.SUCCESS; + } + } + + return this.transactionStatus.FAIL; + } + + signTransaction(sender: TSigner, transaction: any, options: Partial | null = null, label = 'transaction') { + const sign = (callback: any) => { + if(options !== null) return transaction.signAndSend(sender, options, callback); + return transaction.signAndSend(sender, callback); + }; + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + try { + const unsub = await sign((result: any) => { + const status = this.getTransactionStatus(result); + + if(status === this.transactionStatus.SUCCESS) { + this.logger.log(`${label} successful`); + unsub(); + resolve({result, status, blockHash: result.status.asInBlock.toHuman()}); + } else if(status === this.transactionStatus.FAIL) { + let moduleError = null; + + if(result.hasOwnProperty('dispatchError')) { + const dispatchError = result['dispatchError']; + + if(dispatchError) { + if(dispatchError.isModule) { + const modErr = dispatchError.asModule; + const errorMeta = dispatchError.registry.findMetaError(modErr); + + moduleError = `${errorMeta.section}.${errorMeta.name}`; + } else if(dispatchError.isToken) { + moduleError = `Token: ${dispatchError.asToken}`; + } else { + // May be [object Object] in case of unhandled non-unit enum + moduleError = `Misc: ${dispatchError.toHuman()}`; + } + } else { + this.logger.log(result, this.logger.level.ERROR); + } + } + + this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR); + unsub(); + reject({status, moduleError, result}); + } + }); + } catch (e) { + this.logger.log(e, this.logger.level.ERROR); + reject(e); + } + }); + } + + async signTransactionWithoutSending(signer: TSigner, tx: any) { + const api = this.getApi(); + const signingInfo = await api.derive.tx.signingInfo(signer.address); + + tx.sign(signer, { + blockHash: api.genesisHash, + genesisHash: api.genesisHash, + runtimeVersion: api.runtimeVersion, + nonce: signingInfo.nonce, + }); + + return tx.toHex(); + } + + async getPaymentInfo(signer: TSigner, tx: any, len: number | null) { + const api = this.getApi(); + const signingInfo = await api.derive.tx.signingInfo(signer.address); + + // We need to sign the tx because + // unsigned transactions does not have an inclusion fee + tx.sign(signer, { + blockHash: api.genesisHash, + genesisHash: api.genesisHash, + runtimeVersion: api.runtimeVersion, + nonce: signingInfo.nonce, + }); + + if(len === null) { + return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo; + } else { + return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo; + } + } + + constructApiCall(apiCall: string, params: any[]) { + if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`); + let call = this.getApi() as any; + for(const part of apiCall.slice(4).split('.')) { + call = call[part]; + if(!call) { + const advice = part.includes('_') ? ' Looks like it needs to be converted to camel case.' : ''; + throw Error(`Function ${part} of api call ${apiCall} not found.${advice}`); + } + } + return call(...params); + } + + encodeApiCall(apiCall: string, params: any[]) { + return this.constructApiCall(apiCall, params).method.toHex(); + } + + async executeExtrinsic< + E extends string, + V extends ( + ...args: any) => any = ForceFunction< + Get2< + AugmentedSubmittables<'promise'>, + E, (...args: any) => Invalid + > + > + >( + sender: TSigner, + extrinsic: `api.tx.${E}`, + params: Parameters, + expectSuccess = true, + options: Partial | null = null,/*, failureMessage='expected success'*/ + ): Promise { + if(this.api === null) throw Error('API not initialized'); + + const startTime = (new Date()).getTime(); + let result: ITransactionResult; + let events: IEvent[] = []; + try { + result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult; + events = this.eventHelper.extractEvents(result.result.events); + const errorEvent = events.find((event) => event.method == 'ExecutedFailed' || event.method == 'CreatedFailed'); + if(errorEvent) + throw Error(errorEvent.method + ': ' + extrinsic); + } + catch (e) { + if(!(e as object).hasOwnProperty('status')) throw e; + result = e as ITransactionResult; + } + + const endTime = (new Date()).getTime(); + + const log = { + executedAt: endTime, + executionTime: endTime - startTime, + type: this.chainLogType.EXTRINSIC, + status: result.status, + call: extrinsic, + signer: this.getSignerAddress(sender), + params, + } as IUniqueHelperLog; + + let errorMessage = ''; + + if(result.status !== this.transactionStatus.SUCCESS) { + if(result.moduleError) { + errorMessage = typeof result.moduleError === 'string' + ? result.moduleError + : `${Object.keys(result.moduleError)[0]}: ${Object.values(result.moduleError)[0]}`; + log.moduleError = errorMessage; + } + else if(result.result.dispatchError) log.dispatchError = result.result.dispatchError; + } + if(events.length > 0) log.events = events; + + this.chainLog.push(log); + + if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) { + if(result.moduleError) throw Error(`${errorMessage}`); + else if(result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError)); + } + return result as any; + } + executeExtrinsicUncheckedWeight< + E extends string, + V extends ( + ...args: any) => any = ForceFunction< + Get2< + AugmentedSubmittables<'promise'>, + E, (...args: any) => Invalid + > + > + >( + _sender: TSigner, + _extrinsic: `api.tx.${E}`, + _params: Parameters, + _expectSuccess = true, + _options: Partial | null = null,/*, failureMessage='expected success'*/ + ): Promise { + throw new Error('executeExtrinsicUncheckedWeight only supported in sudo'); + } + + async callRpc + // TODO: make it strongly typed, or use api.query/api.rpc directly + // < + // K extends 'rpc' | 'query', + // E extends string, + // V extends (...args: any) => any = ForceFunction< + // Get2< + // K extends 'rpc' ? DecoratedRpc<'promise', RpcInterface> : QueryableStorage<'promise'>, + // E, (...args: any) => Invalid<'not found'> + // > + // >, + // P = Parameters, + // > + (rpc: string, params?: any[]): Promise { + + if(typeof params === 'undefined') params = [] as any; + if(this.api === null) throw Error('API not initialized'); + if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`); + + const startTime = (new Date()).getTime(); + let result; + let error = null; + const log = { + type: this.chainLogType.RPC, + call: rpc, + params, + } as any as IUniqueHelperLog; + + try { + result = await this.constructApiCall(rpc, params as any); + } + catch (e) { + error = e; + } + + const endTime = (new Date()).getTime(); + + log.executedAt = endTime; + log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success'; + log.executionTime = endTime - startTime; + + this.chainLog.push(log); + + if(error !== null) throw error; + + return result; + } + + getSignerAddress(signer: IKeyringPair | string): string { + if(typeof signer === 'string') return signer; + return signer.address; + } + + fetchAllPalletNames(): string[] { + if(this.api === null) throw Error('API not initialized'); + return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase()).sort(); + } + + fetchMissingPalletNames(requiredPallets: readonly string[]): string[] { + const palletNames = this.fetchAllPalletNames(); + return requiredPallets.filter(p => !palletNames.includes(p)); + } +} + + +export class HelperGroup { + helper: T; + + constructor(uniqueHelper: T) { + this.helper = uniqueHelper; + } +} + + +class CollectionGroup extends HelperGroup { + /** + * Get number of blocks when sponsored transaction is available. + * + * @param collectionId ID of collection + * @param tokenId ID of token + * @param addressObj address for which the sponsorship is checked + * @example await getTokenNextSponsored(1, 2, {Substrate: '5DfhbVfww7ThF8q6f3...'}); + * @returns number of blocks or null if sponsorship hasn't been set + */ + async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON(); + } + + /** + * Get the number of created collections. + * + * @returns number of created collections + */ + async getTotalCount(): Promise { + return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber(); + } + + /** + * Get information about the collection with additional data, + * including the number of tokens it contains, its administrators, + * the normalized address of the collection's owner, and decoded name and description. + * + * @param collectionId ID of collection + * @example await getData(2) + * @returns collection information object + */ + async getData(collectionId: number): Promise<{ + id: number; + name: string; + description: string; + tokensCount: number; + admins: CrossAccountId[]; + normalizedOwner: TSubstrateAccount; + raw: any + } | null> { + const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]); + const humanCollection = collection.toHuman(), collectionData = { + id: collectionId, name: null, description: null, tokensCount: 0, admins: [], + raw: humanCollection, + } as any, jsonCollection = collection.toJSON(); + if(humanCollection === null) return null; + collectionData.raw.limits = jsonCollection.limits; + collectionData.raw.permissions = jsonCollection.permissions; + collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner); + for(const key of ['name', 'description']) { + collectionData[key] = this.helper.util.vec2str(humanCollection[key]); + } + + collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode)) + ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId) + : 0; + collectionData.admins = await this.getAdmins(collectionId); + + return collectionData; + } + + /** + * Get the addresses of the collection's administrators, optionally normalized. + * + * @param collectionId ID of collection + * @param normalize whether to normalize the addresses to the default ss58 format + * @example await getAdmins(1) + * @returns array of administrators + */ + async getAdmins(collectionId: number, normalize = false): Promise { + const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman() as ICrossAccountId[]; + + return normalize + ? admins.map(address => CrossAccountId.withNormalizedSubstrate(address)) + : admins; + } + + /** + * Get the addresses added to the collection allow-list, optionally normalized. + * @param collectionId ID of collection + * @param normalize whether to normalize the addresses to the default ss58 format + * @example await getAllowList(1) + * @returns array of allow-listed addresses + */ + async getAllowList(collectionId: number, normalize = false): Promise { + const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman() as ICrossAccountId[]; + return normalize + ? allowListed.map(address => CrossAccountId.withNormalizedSubstrate(address)) + : allowListed; + } + + /** + * Get the effective limits of the collection instead of null for default values + * + * @param collectionId ID of collection + * @example await getEffectiveLimits(2) + * @returns object of collection limits + */ + async getEffectiveLimits(collectionId: number): Promise { + return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON(); + } + + /** + * Burns the collection if the signer has sufficient permissions and collection is empty. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @example await helper.collection.burn(aliceKeyring, 3); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async burn(signer: TSigner, collectionId: number): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.destroyCollection', [collectionId], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed'); + } + + /** + * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param sponsorAddress Sponsor substrate address + * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...") + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet'); + } + + /** + * Confirms consent to sponsor the collection on behalf of the signer. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @example confirmSponsorship(aliceKeyring, 10) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async confirmSponsorship(signer: TSigner, collectionId: number): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.confirmSponsorship', [collectionId], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed'); + } + + /** + * Removes the sponsor of a collection, regardless if it consented or not. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @example removeSponsor(aliceKeyring, 10) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async removeSponsor(signer: TSigner, collectionId: number): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.removeCollectionSponsor', [collectionId], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved'); + } + + /** + * Sets the limits of the collection. At least one limit must be specified for a correct call. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param limits collection limits object + * @example + * await setLimits( + * aliceKeyring, + * 10, + * { + * sponsorTransferTimeout: 0, + * ownerCanDestroy: false + * } + * ) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.setCollectionLimits', [collectionId, limits], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet'); + } + + /** + * Changes the owner of the collection to the new Substrate address. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param ownerAddress substrate address of new owner + * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...") + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnerChanged'); + } + + /** + * Adds a collection administrator. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param adminAddressObj Administrator address (substrate or ethereum) + * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded'); + } + + /** + * Removes a collection administrator. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param adminAddressObj Administrator address (substrate or ethereum) + * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved'); + } + + /** + * Check if user is in allow list. + * + * @param collectionId ID of collection + * @param user Account to check + * @example await getAdmins(1) + * @returns is user in allow list + */ + async allowed(collectionId: number, user: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON(); + } + + /** + * Adds an address to allow list + * @param signer keyring of signer + * @param collectionId ID of collection + * @param addressObj address to add to the allow list + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.addToAllowList', [collectionId, addressObj], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded'); + } + + /** + * Removes an address from allow list + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param addressObj address to remove from the allow list + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.removeFromAllowList', [collectionId, addressObj], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved'); + } + + /** + * Sets onchain permissions for selected collection. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param permissions collection permissions object + * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}}); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.setCollectionPermissions', [collectionId, permissions], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet'); + } + + /** + * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param permissions nesting permissions object + * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true}); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise { + return await this.setPermissions(signer, collectionId, {nesting: permissions}); + } + + /** + * Disables nesting for selected collection. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @example disableNesting(aliceKeyring, 10); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async disableNesting(signer: TSigner, collectionId: number): Promise { + return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}}); + } + + /** + * Sets onchain properties to the collection. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param properties array of property objects + * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.setCollectionProperties', [collectionId, properties], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet'); + } + + /** + * Get collection properties. + * + * @param collectionId ID of collection + * @param propertyKeys optionally filter the returned properties to only these keys + * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']); + * @returns array of key-value pairs + */ + async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise { + return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman(); + } + + async getPropertiesConsumedSpace(collectionId: number): Promise { + const api = this.helper.getApi(); + const props = (await api.query.common.collectionProperties(collectionId)).toJSON(); + + return (props! as any).consumedSpace; + } + + async getCollectionOptions(collectionId: number) { + return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman(); + } + + /** + * Deletes onchain properties from the collection. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param propertyKeys array of property keys to delete + * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted'); + } + + /** + * Changes the owner of the token. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param addressObj address of a new owner + * @param amount amount of tokens to be transfered. For NFT must be set to 1n + * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}) + * @returns true if the token success, otherwise false + */ + async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount], + true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`, + ); + + return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount); + } + + /** + * + * Change ownership of a token(s) on behalf of the owner. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param fromAddressObj address on behalf of which the token will be sent + * @param toAddressObj new token owner + * @param amount amount of tokens to be transfered. For NFT must be set to 1n + * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg"}, {Ethereum: "0x9F0583DbB85..."}) + * @returns true if the token success, otherwise false + */ + async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount], + true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`, + ); + return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount); + } + + /** + * + * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param amount amount of tokens to be burned. For NFT must be set to 1n + * @example burnToken(aliceKeyring, 10, 5); + * @returns ```true``` if the extrinsic is successful, otherwise ```false``` + */ + async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount = 1n): Promise { + const burnResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.burnItem', [collectionId, tokenId, amount], + true, // `Unable to burn token for ${label}`, + ); + const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult); + if(burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens'); + return burnedTokens.success; + } + + /** + * Destroys a concrete instance of NFT on behalf of the owner + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param fromAddressObj address on behalf of which the token will be burnt + * @param amount amount of tokens to be burned. For NFT must be set to 1n + * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."}) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise { + const burnResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount], + true, // `Unable to burn token from for ${label}`, + ); + const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult); + return burnedTokens.success && burnedTokens.tokens.length > 0; + } + + /** + * Set, change, or remove approved address to transfer the ownership of the NFT. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens + * @param amount amount of token to be approved. For NFT must be set to 1n + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) { + const approveResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount], + true, // `Unable to approve token for ${label}`, + ); + + return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved'); + } + + /** + * Set, change, or remove approved address to transfer the ownership of the NFT from eth mirror. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param fromAddressObj Signer's Ethereum address containing her tokens + * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens + * @param amount amount of token to be approved. For NFT must be set to 1n + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async approveTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { + const approveResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.approveFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount], + true, // `Unable to approve token for ${label}`, + ); + + return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved'); + } + + /** + * Set, change, or remove approved address to transfer the ownership of the NFT from eth mirror. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens + * @param amount amount of token to be approved. For NFT must be set to 1n + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async approveTokenFromEth(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) { + const ethMirror = CrossAccountId.fromKeyring(signer).toEthereum().toICrossAccountId(); + return await this.approveTokenFrom(signer, collectionId, tokenId, ethMirror, toAddressObj, amount); + } + + /** + * Get the amount of token pieces approved to transfer or burn. Normally 0. + * + * @param collectionId ID of collection + * @param tokenId ID of token + * @param toAccountObj address which is approved to use token pieces + * @param fromAccountObj address which may have allowed the use of its owned tokens + * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."}) + * @returns number of approved to transfer pieces + */ + async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt(); + } + + /** + * Get the last created token ID in a collection + * + * @param collectionId ID of collection + * @example getLastTokenId(10); + * @returns id of the last created token + */ + async getLastTokenId(collectionId: number): Promise { + return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber(); + } + + /** + * Check if token exists + * + * @param collectionId ID of collection + * @param tokenId ID of token + * @example doesTokenExist(10, 20); + * @returns true if the token exists, otherwise false + */ + async doesTokenExist(collectionId: number, tokenId: number): Promise { + return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON(); + } +} + +class NFTnRFT extends CollectionGroup { + /** + * Get tokens owned by account + * + * @param collectionId ID of collection + * @param addressObj tokens owner + * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."}) + * @returns array of token ids owned by account + */ + async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON(); + } + + /** + * Get token data + * + * @param collectionId ID of collection + * @param tokenId ID of token + * @param propertyKeys optionally filter the token properties to only these keys + * @param blockHashAt optionally query the data at some block with this hash + * @example getToken(10, 5); + * @returns human readable token data + */ + async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{ + properties: IProperty[]; + owner: ICrossAccountId; + normalizedOwner: ICrossAccountId; + } | null> { + let args; + if(typeof blockHashAt === 'undefined') { + args = [collectionId, tokenId]; + } + else { + if(propertyKeys.length == 0) { + const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman(); + if(!collection) return null; + propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key); + } + args = [collectionId, tokenId, propertyKeys, blockHashAt]; + } + const tokenData = (await this.helper.callRpc('api.rpc.unique.tokenData', args)).toHuman(); + if(tokenData === null || tokenData.owner === null) return null; + tokenData.normalizedOwner = CrossAccountId.withNormalizedSubstrate(tokenData.owner); + return tokenData; + } + + /** + * Get token's owner + * @param collectionId ID of collection + * @param tokenId ID of token + * @param blockHashAt optionally query the data at the block with this hash + * @example getTokenOwner(10, 5); + * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."} + */ + async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise { + let owner; + if(typeof blockHashAt === 'undefined') { + owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]); + } else { + owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]); + } + return CrossAccountId.fromLowerCaseKeys(owner.toJSON()).toICrossAccountId(); + } + + /** + * Recursively find the address that owns the token + * @param collectionId ID of collection + * @param tokenId ID of token + * @param blockHashAt + * @example getTokenTopmostOwner(10, 5); + * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."} + */ + async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise { + let owner; + if(typeof blockHashAt === 'undefined') { + owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]); + } else { + owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]); + } + + if(owner === null) return null; + + return owner.toHuman(); + } + + /** + * Nest one token into another + * @param signer keyring of signer + * @param tokenObj token to be nested + * @param rootTokenObj token to be parent + * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise { + const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj); + const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress); + if(!result) { + throw Error('Unable to nest token!'); + } + return result; + } + + /** + * Remove token from nested state + * @param signer keyring of signer + * @param tokenObj token to unnest + * @param rootTokenObj parent of a token + * @param toAddressObj address of a new token owner + * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."}); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise { + const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj); + const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj); + if(!result) { + throw Error('Unable to unnest token!'); + } + return result; + } + + /** + * Set permissions to change token properties + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param permissions permissions to change a property by the collection admin or token owner + * @example setTokenPropertyPermissions( + * aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}] + * ) + * @returns true if extrinsic success otherwise false + */ + async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet'); + } + + /** + * Get token property permissions. + * + * @param collectionId ID of collection + * @param propertyKeys optionally filter the returned property permissions to only these keys + * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']); + * @returns array of key-permission pairs + */ + async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise { + return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman(); + } + + /** + * Set token properties + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection + * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}]) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet'); + } + + /** + * Get properties, metadata assigned to a token. + * + * @param collectionId ID of collection + * @param tokenId ID of token + * @param propertyKeys optionally filter the returned properties to only these keys + * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']); + * @returns array of key-value pairs + */ + async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise { + return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman(); + } + + /** + * Delete the provided properties of a token + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param propertyKeys property keys to be deleted + * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"]) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys], + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted'); + } + + /** + * Mint new collection + * + * @param signer keyring of signer + * @param collectionOptions basic collection options and properties + * @param mode NFT or RFT type of a collection + * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT") + * @returns object of the created collection + */ + async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise { + collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object + collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null}; + for(const key of ['name', 'description', 'tokenPrefix']) { + if(typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string); + } + + let flags = 0; + // convert CollectionFlags to number and join them in one number + if(collectionOptions.flags) { + for(let i = 0; i < collectionOptions.flags.length; i++){ + const flag = collectionOptions.flags[i]; + flags = flags | flag; + } + } + collectionOptions.flags = [flags]; + + const creationResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.createCollectionEx', [collectionOptions], + true, // errorLabel, + ); + return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult)); + } + + getCollectionObject(_collectionId: number): any { + return null; + } + + getTokenObject(_collectionId: number, _tokenId: number): any { + return null; + } + + /** + * Tells whether the given `owner` approves the `operator`. + * @param collectionId ID of collection + * @param owner owner address + * @param operator operator addrees + * @returns true if operator is enabled + */ + async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON(); + } + + /** Sets or unsets the approval of a given operator. + * The `operator` is allowed to transfer all tokens of the `caller` on their behalf. + * @param operator Operator + * @param approved Should operator status be granted or revoked? + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved], + true, + ); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll'); + } +} + + +class NFTGroup extends NFTnRFT { + /** + * Get collection object + * @param collectionId ID of collection + * @example getCollectionObject(2); + * @returns instance of UniqueNFTCollection + */ + override getCollectionObject(collectionId: number): UniqueNFTCollection { + return new UniqueNFTCollection(collectionId, this.helper); + } + + /** + * Get token object + * @param collectionId ID of collection + * @param tokenId ID of token + * @example getTokenObject(10, 5); + * @returns instance of UniqueNFTToken + */ + override getTokenObject(collectionId: number, tokenId: number): UniqueNFToken { + return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId)); + } + + /** + * Is token approved to transfer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param toAccountObj address to be approved + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise { + return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, (await this.getTokenOwner(collectionId, tokenId)))) === 1n; + } + + /** + * Changes the owner of the token. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param addressObj address of a new owner + * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + override async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise { + return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n); + } + + /** + * + * Change ownership of a NFT on behalf of the owner. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param fromAddressObj address on behalf of which the token will be sent + * @param toAddressObj new token owner + * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Ethereum: "0x9F0583DbB85..."}) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + override async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise { + return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n); + } + + /** + * Get tokens nested in the provided token + * @param collectionId ID of collection + * @param tokenId ID of token + * @param blockHashAt optionally query the data at the block with this hash + * @example getTokenChildren(10, 5); + * @returns tokens whose depth of nesting is <= 5 + */ + async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise { + let children; + if(typeof blockHashAt === 'undefined') { + children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]); + } else { + children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]); + } + + return children.toJSON().map((x: any) => ({collectionId: x.collection, tokenId: x.token})); + } + + /** + * Mint new collection + * @param signer keyring of signer + * @param collectionOptions Collection options + * @example + * mintCollection(aliceKeyring, { + * name: 'New', + * description: 'New collection', + * tokenPrefix: 'NEW', + * }) + * @returns object of the created collection + */ + override async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise { + return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection; + } + + /** + * Mint new token + * @param signer keyring of signer + * @param data token data + * @returns created token object + */ + async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise { + const creationResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, { + NFT: { + properties: data.properties, + }, + }], + true, + ); + const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult); + if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens'); + if(createdTokens.tokens.length < 1) throw Error('No tokens minted'); + return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId); + } + + /** + * Mint multiple NFT tokens + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokens array of tokens with owner and properties + * @example + * mintMultipleTokens(aliceKeyring, 10, [{ + * owner: {Substrate: "5DyN4Y92vZCjv38fg..."}, + * properties: [{key: "gender", value: "male"},{key: "age", value: "45"}], + * },{ + * owner: {Ethereum: "0x9F0583DbB855d..."}, + * properties: [{key: "gender", value: "female"},{key: "age", value: "22"}], + * }]); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]): Promise { + const creationResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}], + true, + ); + const collection = this.getCollectionObject(collectionId); + return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); + } + + /** + * Mint multiple NFT tokens with one owner + * @param signer keyring of signer + * @param collectionId ID of collection + * @param owner tokens owner + * @param tokens array of tokens with owner and properties + * @example + * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{ + * properties: [{ + * key: "gender", + * value: "female", + * },{ + * key: "age", + * value: "33", + * }], + * }]); + * @returns array of newly created tokens + */ + async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { properties?: IProperty[] }[]): Promise { + const rawTokens = []; + for(const token of tokens) { + const raw = {NFT: {properties: token.properties}}; + rawTokens.push(raw); + } + const creationResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens], + true, + ); + const collection = this.getCollectionObject(collectionId); + return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); + } + + /** + * Set, change, or remove approved address to transfer the ownership of the NFT. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param toAddressObj address to approve + * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + override approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) { + return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount); + } +} + + +class RFTGroup extends NFTnRFT { + /** + * Get collection object + * @param collectionId ID of collection + * @example getCollectionObject(2); + * @returns instance of UniqueRFTCollection + */ + override getCollectionObject(collectionId: number): UniqueRFTCollection { + return new UniqueRFTCollection(collectionId, this.helper); + } + + /** + * Get token object + * @param collectionId ID of collection + * @param tokenId ID of token + * @example getTokenObject(10, 5); + * @returns instance of UniqueNFTToken + */ + override getTokenObject(collectionId: number, tokenId: number): UniqueRFToken { + return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId)); + } + + /** + * Get top 10 token owners with the largest number of pieces + * @param collectionId ID of collection + * @param tokenId ID of token + * @example getTokenTop10Owners(10, 5); + * @returns array of top 10 owners + */ + async getTokenTop10Owners(collectionId: number, tokenId: number): Promise { + return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys).map((a: CrossAccountId) => a.toICrossAccountId()); + } + + /** + * Get number of pieces owned by address + * @param collectionId ID of collection + * @param tokenId ID of token + * @param addressObj address token owner + * @example getTokenBalance(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}); + * @returns number of pieces ownerd by address + */ + async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt(); + } + + /** + * Transfer pieces of token to another address + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param addressObj address of a new owner + * @param amount number of pieces to be transfered + * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + override async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise { + return await super.transferToken(signer, collectionId, tokenId, addressObj, amount); + } + + /** + * Change ownership of some pieces of RFT on behalf of the owner. + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param fromAddressObj address on behalf of which the token will be sent + * @param toAddressObj new token owner + * @param amount number of pieces to be transfered + * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + override async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise { + return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount); + } + + /** + * Mint new collection + * @param signer keyring of signer + * @param collectionOptions Collection options + * @example + * mintCollection(aliceKeyring, { + * name: 'New', + * description: 'New collection', + * tokenPrefix: 'NEW', + * }) + * @returns object of the created collection + */ + override async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise { + return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection; + } + + /** + * Mint new token + * @param signer keyring of signer + * @param data token data + * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n}); + * @returns created token object + */ + async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise { + const creationResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, { + ReFungible: { + pieces: data.pieces, + properties: data.properties, + }, + }], + true, + ); + const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult); + if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens'); + if(createdTokens.tokens.length < 1) throw Error('No tokens minted'); + return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId); + } + + mintMultipleTokens(_signer: TSigner, _collectionId: number, _tokens: { owner: ICrossAccountId, pieces: bigint, properties?: IProperty[] }[]): Promise { + throw Error('Not implemented'); + // const creationResult = await this.helper.executeExtrinsic( + // signer, + // 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}], + // true, // `Unable to mint RFT tokens for ${label}`, + // ); + // const collection = this.getCollectionObject(collectionId); + // return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); + } + + /** + * Mint multiple RFT tokens with one owner + * @param signer keyring of signer + * @param collectionId ID of collection + * @param owner tokens owner + * @param tokens array of tokens with properties and pieces + * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]); + * @returns array of newly created RFT tokens + */ + async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { pieces: bigint, properties?: IProperty[] }[]): Promise { + const rawTokens = []; + for(const token of tokens) { + const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}}; + rawTokens.push(raw); + } + const creationResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens], + true, + ); + const collection = this.getCollectionObject(collectionId); + return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); + } + + /** + * Destroys a concrete instance of RFT. + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param amount number of pieces to be burnt + * @example burnToken(aliceKeyring, 10, 5); + * @returns ```true``` if the extrinsic is successful, otherwise ```false``` + */ + override async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount = 1n): Promise { + return await super.burnToken(signer, collectionId, tokenId, amount); + } + + /** + * Destroys a concrete instance of RFT on behalf of the owner. + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param fromAddressObj address on behalf of which the token will be burnt + * @param amount number of pieces to be burnt + * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + override async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise { + return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount); + } + + /** + * Set, change, or remove approved address to transfer the ownership of the RFT. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param toAddressObj address to approve + * @param amount number of pieces to be approved + * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n); + * @returns true if the token success, otherwise false + */ + override approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) { + return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount); + } + + /** + * Get total number of pieces + * @param collectionId ID of collection + * @param tokenId ID of token + * @example getTokenTotalPieces(10, 5); + * @returns number of pieces + */ + async getTokenTotalPieces(collectionId: number, tokenId: number): Promise { + return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt(); + } + + /** + * Change number of token pieces. Signer must be the owner of all token pieces. + * @param signer keyring of signer + * @param collectionId ID of collection + * @param tokenId ID of token + * @param amount new number of pieces + * @example repartitionToken(aliceKeyring, 10, 5, 12345n); + * @returns true if the repartion was success, otherwise false + */ + async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise { + const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId); + const repartitionResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.repartition', [collectionId, tokenId, amount], + true, + ); + if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated'); + return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed'); + } +} + + +class FTGroup extends CollectionGroup { + /** + * Get collection object + * @param collectionId ID of collection + * @example getCollectionObject(2); + * @returns instance of UniqueFTCollection + */ + getCollectionObject(collectionId: number): UniqueFTCollection { + return new UniqueFTCollection(collectionId, this.helper); + } + + /** + * Mint new fungible collection + * @param signer keyring of signer + * @param collectionOptions Collection options + * @param decimalPoints number of token decimals + * @example + * mintCollection(aliceKeyring, { + * name: 'New', + * description: 'New collection', + * tokenPrefix: 'NEW', + * }, 18) + * @returns newly created fungible collection + */ + async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise { + collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object + if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions'); + collectionOptions.mode = {fungible: decimalPoints}; + for(const key of ['name', 'description', 'tokenPrefix']) { + if(typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string); + } + const creationResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.createCollectionEx', [collectionOptions], + true, + ); + return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult)); + } + + /** + * Mint tokens + * @param signer keyring of signer + * @param collectionId ID of collection + * @param owner address owner of new tokens + * @param amount amount of tokens to be meanted + * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise { + const creationResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, { + Fungible: { + value: amount, + }, + }], + true, // `Unable to mint fungible tokens for ${label}`, + ); + return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated'); + } + + /** + * Mint multiple Fungible tokens with one owner + * @param signer keyring of signer + * @param collectionId ID of collection + * @param owner tokens owner + * @param tokens array of tokens with properties and pieces + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: { value: bigint }[], owner: ICrossAccountId): Promise { + const rawTokens = []; + for(const token of tokens) { + const raw = {Fungible: {Value: token.value}}; + rawTokens.push(raw); + } + const creationResult = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens], + true, + ); + return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated'); + } + + /** + * Get the top 10 owners with the largest balance for the Fungible collection + * @param collectionId ID of collection + * @example getTop10Owners(10); + * @returns array of ```ICrossAccountId``` + */ + async getTop10Owners(collectionId: number): Promise { + return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys).map((a: CrossAccountId) => a.toICrossAccountId()); + } + + /** + * Get account balance + * @param collectionId ID of collection + * @param addressObj address of owner + * @example getBalance(10, {Substrate: "5GHoZe9c73RYbVzq..."}) + * @returns amount of fungible tokens owned by address + */ + async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt(); + } + + /** + * Transfer tokens to address + * @param signer keyring of signer + * @param collectionId ID of collection + * @param toAddressObj address recipient + * @param amount amount of tokens to be sent + * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) { + return await super.transferToken(signer, collectionId, 0, toAddressObj, amount); + } + + /** + * Transfer some tokens on behalf of the owner. + * @param signer keyring of signer + * @param collectionId ID of collection + * @param fromAddressObj address on behalf of which tokens will be sent + * @param toAddressObj address where token to be sent + * @param amount number of tokens to be sent + * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { + return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount); + } + + /** + * Destroy some amount of tokens + * @param signer keyring of signer + * @param collectionId ID of collection + * @param amount amount of tokens to be destroyed + * @example burnTokens(aliceKeyring, 10, 1000n); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async burnTokens(signer: IKeyringPair, collectionId: number, amount = 1n): Promise { + return await super.burnToken(signer, collectionId, 0, amount); + } + + /** + * Burn some tokens on behalf of the owner. + * @param signer keyring of signer + * @param collectionId ID of collection + * @param fromAddressObj address on behalf of which tokens will be burnt + * @param amount amount of tokens to be burnt + * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise { + return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount); + } + + /** + * Get total collection supply + * @param collectionId + * @returns + */ + async getTotalPieces(collectionId: number): Promise { + return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt(); + } + + /** + * Set, change, or remove approved address to transfer tokens. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @param toAddressObj address to be approved + * @param amount amount of tokens to be approved + * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) { + return super.approveToken(signer, collectionId, 0, toAddressObj, amount); + } + + /** + * Get amount of fungible tokens approved to transfer + * @param collectionId ID of collection + * @param fromAddressObj owner of tokens + * @param toAddressObj the address approved for the transfer of tokens on behalf of the owner + * @returns number of tokens approved for the transfer + */ + getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { + return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj); + } +} + + +class ChainGroup extends HelperGroup { + /** + * Get system properties of a chain + * @example getChainProperties(); + * @returns ss58Format, token decimals, and token symbol + */ + getChainProperties(): IChainProperties { + const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON(); + return { + ss58Format: properties.ss58Format.toJSON(), + tokenDecimals: properties.tokenDecimals.toJSON(), + tokenSymbol: properties.tokenSymbol.toJSON(), + }; + } + + /** + * Get chain header + * @example getLatestBlockNumber(); + * @returns the number of the last block + */ + async getLatestBlockNumber(): Promise { + return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber(); + } + + /** + * Get block hash by block number + * @param blockNumber number of block + * @example getBlockHashByNumber(12345); + * @returns hash of a block + */ + async getBlockHashByNumber(blockNumber: number): Promise { + const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON(); + if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null; + return blockHash; + } + + // TODO add docs + async getBlock(blockHashOrNumber: string | number): Promise { + const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber); + if(!blockHash) return null; + return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block; + } + + /** + * Get latest relay block + * @returns {number} relay block + */ + async getRelayBlockNumber(): Promise { + const blockNumber = (await this.helper.callRpc('api.query.parachainSystem.validationData')).toJSON().relayParentNumber; + return BigInt(blockNumber); + } + + /** + * Get account nonce + * @param address substrate address + * @example getNonce("5GrwvaEF5zXb26Fz..."); + * @returns number, account's nonce + */ + async getNonce(address: TSubstrateAccount): Promise { + return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber(); + } +} + +export class SubstrateBalanceGroup extends HelperGroup { + /** + * Get substrate address balance + * @param address substrate address + * @example getSubstrate("5GrwvaEF5zXb26Fz...") + * @returns amount of tokens on address + */ + async getSubstrate(address: TSubstrateAccount): Promise { + return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt(); + } + + /** + * Transfer tokens to substrate address + * @param signer keyring of signer + * @param address substrate address of a recipient + * @param amount amount of tokens to be transfered + * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise { + 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}`*/); + + let transfer = {from: null, to: null, amount: 0n} as any; + result.result.events.forEach(({event: {data, method, section}}: any) => { + if((section === 'balances') && (method === 'Transfer')) { + transfer = { + from: this.helper.address.normalizeSubstrate(data[0]), + to: this.helper.address.normalizeSubstrate(data[1]), + amount: BigInt(data[2]), + }; + } + }); + const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from + && this.helper.address.normalizeSubstrate(address) === transfer.to + && BigInt(amount) === transfer.amount; + return isSuccess; + } + + /** + * Get full substrate balance including free, frozen, and reserved + * @param address substrate address + * @returns + */ + async getSubstrateFull(address: TSubstrateAccount): Promise { + const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data; + return {free: accountInfo.free.toBigInt(), frozen: accountInfo.frozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()}; + } + + /** + * Get total issuance + * @returns + */ + async getTotalIssuance(): Promise { + const total = (await this.helper.callRpc('api.query.balances.totalIssuance', [])); + return total.toBigInt(); + } + + async getLocked(address: TSubstrateAccount): Promise<{ id: string, amount: bigint, reason: string }[]> { + const locks = (await this.helper.callRpc('api.query.balances.locks', [address])).toHuman(); + return locks.map((lock: any) => ({id: lock.id, amount: BigInt(lock.amount.replace(/,/g, '')), reasons: lock.reasons})); + } + async getFrozen(address: TSubstrateAccount): Promise<{ id: string, amount: bigint }[]> { + const locks = (await this.helper.api!.query.balances.freezes(address)) as unknown as Array; + return locks.map(lock => ({id: lock.id.toUtf8(), amount: lock.amount.toBigInt()})); + } +} + +export class EthereumBalanceGroup extends HelperGroup { + /** + * Get ethereum address balance + * @param address ethereum address + * @example getEthereum("0x9F0583DbB855d...") + * @returns amount of tokens on address + */ + async getEthereum(address: TEthereumAccount): Promise { + return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt(); + } + + /** + * Transfer tokens to address + * @param signer keyring of signer + * @param address Ethereum address of a recipient + * @param amount amount of tokens to be transfered + * @example transferToEthereum(alithKeyring, "0x9F0583DbB855d...", 100_000_000_000n); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise { + const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true); + + let transfer = {from: null, to: null, amount: 0n} as any; + result.result.events.forEach(({event: {data, method, section}}: any) => { + if((section === 'balances') && (method === 'Transfer')) { + transfer = { + from: data[0].toString(), + to: data[1].toString(), + amount: BigInt(data[2]), + }; + } + }); + const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from + && address === transfer.to + && BigInt(amount) === transfer.amount; + return isSuccess; + } +} + +class BalanceGroup extends HelperGroup { + subBalanceGroup: SubstrateBalanceGroup; + ethBalanceGroup: EthereumBalanceGroup; + + constructor(helper: T) { + super(helper); + this.subBalanceGroup = new SubstrateBalanceGroup(helper); + this.ethBalanceGroup = new EthereumBalanceGroup(helper); + } + + getCollectionCreationPrice(): bigint { + return 2n * this.getOneTokenNominal(); + } + /** + * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ). + * @example getOneTokenNominal() + * @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ. + */ + getOneTokenNominal(): bigint { + const chainProperties = this.helper.chain.getChainProperties(); + return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]); + } + + /** + * Get substrate address balance + * @param address substrate address + * @example getSubstrate("5GrwvaEF5zXb26Fz...") + * @returns amount of tokens on address + */ + getSubstrate(address: TSubstrateAccount): Promise { + return this.subBalanceGroup.getSubstrate(address); + } + + /** + * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved + * @param address substrate address + * @returns + */ + getSubstrateFull(address: TSubstrateAccount): Promise { + return this.subBalanceGroup.getSubstrateFull(address); + } + + /** + * Get total issuance + * @returns + */ + getTotalIssuance(): Promise { + return this.subBalanceGroup.getTotalIssuance(); + } + + /** + * Get locked balances + * @param address substrate address + * @returns locked balances with reason via api.query.balances.locks + * @deprecated all the methods should switch to getFrozen + */ + getLocked(address: TSubstrateAccount) { + return this.subBalanceGroup.getLocked(address); + } + + /** + * Get frozen balances + * @param address substrate address + * @returns frozen balances with id via api.query.balances.freezes + */ + getFrozen(address: TSubstrateAccount) { + return this.subBalanceGroup.getFrozen(address); + } + + /** + * Get ethereum address balance + * @param address ethereum address + * @example getEthereum("0x9F0583DbB855d...") + * @returns amount of tokens on address + */ + getEthereum(address: TEthereumAccount): Promise { + return this.ethBalanceGroup.getEthereum(address); + } + + async setBalanceSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint) { + await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceSetBalance', [address, amount], true); + } + + /** + * Transfer tokens to substrate address + * @param signer keyring of signer + * @param address substrate address of a recipient + * @param amount amount of tokens to be transfered + * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n); + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise { + return this.subBalanceGroup.transferToSubstrate(signer, address, amount); + } + + async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise { + const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true); + + let transfer = {from: null, to: null, amount: 0n} as any; + result.result.events.forEach(({event: {data, method, section}}: any) => { + if((section === 'balances') && (method === 'Transfer')) { + transfer = { + from: this.helper.address.normalizeSubstrate(data[0]), + to: this.helper.address.normalizeSubstrate(data[1]), + amount: BigInt(data[2]), + }; + } + }); + let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from; + isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to; + isSuccess = isSuccess && BigInt(amount) === transfer.amount; + return isSuccess; + } + + /** + * Transfer tokens with the unlock period + * @param signer signers Keyring + * @param address Substrate address of recipient + * @param schedule Schedule params + * @example vestedTransfer(signer, recepient.address, 20000, 100, 10, 50 * nominal); // total amount of vested tokens will be 100 * 50 = 5000 + */ + async vestedTransfer(signer: TSigner, address: TSubstrateAccount, schedule: { start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }): Promise { + const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.vestedTransfer', [address, schedule]); + const event = result.result.events + .find((e: any) => e.event.section === 'vesting' && + e.event.method === 'VestingScheduleAdded' && + e.event.data[0].toHuman() === signer.address); + if(!event) throw Error('Cannot find transfer in events'); + } + + /** + * Get schedule for recepient of vested transfer + * @param address Substrate address of recipient + * @returns + */ + async getVestingSchedules(address: TSubstrateAccount): Promise<{ start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }[]> { + const schedule = (await this.helper.callRpc('api.query.vesting.vestingSchedules', [address])).toJSON(); + return schedule.map((schedule: any) => ({ + start: BigInt(schedule.start), + period: BigInt(schedule.period), + periodCount: BigInt(schedule.periodCount), + perPeriod: BigInt(schedule.perPeriod), + })); + } + + /** + * Claim vested tokens + * @param signer signers Keyring + */ + async claim(signer: TSigner) { + const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.claim', []); + const event = result.result.events + .find((e: any) => e.event.section === 'vesting' && + e.event.method === 'Claimed' && + e.event.data[0].toHuman() === signer.address); + if(!event) throw Error('Cannot find claim in events'); + } +} + +class AddressGroup extends HelperGroup { + /** + * Normalizes the address to the specified ss58 format, by default ```42```. + * @param address substrate address + * @param ss58Format format for address conversion, by default ```42``` + * @example normalizeSubstrate("unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx") // returns 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY + * @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation + */ + normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount { + return CrossAccountId.normalizeSubstrateAddress({Substrate: address}, ss58Format); + } + + /** + * Get address in the connected chain format + * @param address substrate address + * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network + * @returns address in chain format + */ + normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount { + return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format); + } + + /** + * Get substrate mirror of an ethereum address + * @param ethAddress ethereum address + * @param toChainFormat false for normalized account + * @example ethToSubstrate('0x9F0583DbB855d...') + * @returns substrate mirror of a provided ethereum address + */ + ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat = false): TSubstrateAccount { + return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined); + } + + /** + * Get ethereum mirror of a substrate address + * @param subAddress substrate account + * @example substrateToEth("5DnSF6RRjwteE3BrC...") + * @returns ethereum mirror of a provided substrate address + */ + substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount { + return CrossAccountId.translateSubToEth(subAddress); + } + + /** + * Encode key to substrate address + * @param key key for encoding address + * @param ss58Format prefix for encoding to the address of the corresponding network + * @returns encoded substrate address + */ + encodeSubstrateAddress(key: Uint8Array | string | bigint, ss58Format = 42): string { + const u8a: Uint8Array = typeof key === 'string' + ? hexToU8a(key) + : typeof key === 'bigint' + ? hexToU8a(key.toString(16)) + : key; + + if(ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) { + throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`); + } + + const allowedDecodedLengths = [1, 2, 4, 8, 32, 33]; + if(!allowedDecodedLengths.includes(u8a.length)) { + throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`); + } + + const u8aPrefix = ss58Format < 64 + ? new Uint8Array([ss58Format]) + : new Uint8Array([ + ((ss58Format & 0xfc) >> 2) | 0x40, + (ss58Format >> 8) | ((ss58Format & 0x03) << 6), + ]); + + const input = u8aConcat(u8aPrefix, u8a); + + return base58Encode(u8aConcat( + input, + blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1), + )); + } + + /** + * Restore substrate address from bigint representation + * @param number decimal representation of substrate address + * @returns substrate address + */ + restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount { + if(this.helper.api === null) { + throw 'Not connected'; + } + const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON(); + if(res === undefined || res === null) { + throw 'Restore address error'; + } + return res.toString(); + } + + /** + * Convert etherium cross account id to substrate cross account id + * @param ethCrossAccount etherium cross account + * @returns substrate cross account id + */ + convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId { + if(ethCrossAccount.sub === '0') { + return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()}; + } + + const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub)); + return {Substrate: ss58}; + } + + paraSiblingSovereignAccount(paraid: number) { + // We are getting a *sibling* parachain sovereign account, + // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c + const siblingPrefix = '0x7369626c'; + + const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2); + const suffix = '000000000000000000000000000000000000000000000000'; + + return siblingPrefix + encodedParaId + suffix; + } +} + + +class StakingGroup extends HelperGroup { + /** + * Stake tokens for App Promotion + * @param signer keyring of signer + * @param amountToStake amount of tokens to stake + * @param label extra label for log + * @returns + */ + async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise { + if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`; + await this.helper.executeExtrinsic( + signer, 'api.tx.appPromotion.stake', + [amountToStake], true, + ); + // TODO extract info from stakeResult + return true; + } + + /** + * Unstake all staked tokens + * @param signer keyring of signer + * @param amountToUnstake amount of tokens to unstake + * @param label extra label for log + * @returns block hash where unstake happened + */ + async unstakeAll(signer: TSigner, label?: string): Promise { + if(typeof label === 'undefined') label = `${signer.address}`; + const unstakeResult = await this.helper.executeExtrinsic( + signer, 'api.tx.appPromotion.unstakeAll', + [], true, + ); + return unstakeResult.blockHash; + } + + /** + * Unstake the part of a staked tokens + * @param signer keyring of signer + * @param amount amount of tokens to unstake + * @param label extra label for log + * @returns block hash where unstake happened + */ + async unstakePartial(signer: TSigner, amount: bigint, label?: string): Promise { + if(typeof label === 'undefined') label = `${signer.address}`; + const unstakeResult = await this.helper.executeExtrinsic( + signer, 'api.tx.appPromotion.unstakePartial', + [amount], true, + ); + return unstakeResult.blockHash; + } + + /** + * Get total number of active stakes + * @param address substrate address + * @returns {number} + */ + async getStakesNumber(address: ICrossAccountId): Promise { + if('Ethereum' in address) throw Error('only substrate address'); + return (await this.helper.callRpc('api.query.appPromotion.stakesPerAccount', [address.Substrate])).toNumber(); + } + + /** + * Get total staked amount for address + * @param address substrate or ethereum address + * @returns total staked amount + */ + async getTotalStaked(address?: ICrossAccountId): Promise { + if(address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt(); + return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt(); + } + + /** + * Get total staked per block + * @param address substrate or ethereum address + * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block + */ + async getTotalStakedPerBlock(address: ICrossAccountId): Promise { + const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]); + return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => ({ + block: block.toBigInt(), + amount: amount.toBigInt(), + })); + } + + /** + * Get total pending unstake amount for address + * @param address substrate or ethereum address + * @returns total pending unstake amount + */ + async getPendingUnstake(address: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt(); + } + + /** + * Get pending unstake amount per block for address + * @param address substrate or ethereum address + * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block + */ + async getPendingUnstakePerBlock(address: ICrossAccountId): Promise { + const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]); + const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => ({ + block: block.toBigInt(), + amount: amount.toBigInt(), + })); + return result; + } +} + + +class PreimageGroup extends HelperGroup { + async getPreimageInfo(h256: string) { + return (await this.helper.callRpc('api.query.preimage.statusFor', [h256])).toJSON(); + } + + /** + * Create a preimage from an API call. + * @param signer keyring of the signer. + * @param call an extrinsic call + * @example await notePreimageFromCall(preimageMaker, + * helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]) + * ); + * @returns promise of extrinsic execution. + */ + notePreimageFromCall(signer: TSigner, call: any, returnPreimageHash = false) { + return this.notePreimage(signer, call.method.toHex(), returnPreimageHash); + } + + /** + * Create a preimage with a hex or a byte array. + * @param signer keyring of the signer. + * @param bytes preimage encoded in hex or a byte array, e.g. an extrinsic call. + * @example await notePreimage(preimageMaker, + * helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]).method.toHex() + * ); + * @returns promise of extrinsic execution. + */ + async notePreimage(signer: TSigner, bytes: string | Uint8Array, returnPreimageHash = false) { + const promise = this.helper.executeExtrinsic(signer, 'api.tx.preimage.notePreimage', [bytes]); + if(returnPreimageHash) { + const result = await promise; + const events = result.result.events.filter((x: any) => x.event.method === 'Noted' && x.event.section === 'preimage'); + const preimageHash = events[0].event.data[0].toHuman(); + return preimageHash; + } + return promise; + } + + /** + * Delete an existing preimage and return the deposit. + * @param signer keyring of the signer - either the owner or the preimage manager (sudo). + * @param h256 hash of the preimage. + * @returns promise of extrinsic execution. + */ + unnotePreimage(signer: TSigner, h256: string) { + return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unnotePreimage', [h256]); + } + + /** + * Request a preimage be uploaded to the chain without paying any fees or deposits. + * @param signer keyring of the signer - either the owner or the preimage manager (sudo). + * @param h256 hash of the preimage. + * @returns promise of extrinsic execution. + */ + requestPreimage(signer: TSigner, h256: string) { + return this.helper.executeExtrinsic(signer, 'api.tx.preimage.requestPreimage', [h256]); + } + + /** + * Clear a previously made request for a preimage. + * @param signer keyring of the signer - either the owner or the preimage manager (sudo). + * @param h256 hash of the preimage. + * @returns promise of extrinsic execution. + */ + unrequestPreimage(signer: TSigner, h256: string) { + return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unrequestPreimage', [h256]); + } +} + +class UtilityGroup extends HelperGroup { + async batch(signer: TSigner, txs: any[]) { + return await this.helper.executeExtrinsic(signer, 'api.tx.utility.batch', [txs]); + } + + async batchAll(signer: TSigner, txs: any[]) { + return await this.helper.executeExtrinsic(signer, 'api.tx.utility.batchAll', [txs]); + } + + batchAllCall(txs: any[]) { + return this.helper.constructApiCall('api.tx.utility.batchAll', [txs]); + } +} + +export type ChainHelperBaseConstructor = new (...args: any[]) => ChainHelperBase; +export type UniqueHelperConstructor = new (...args: any[]) => UniqueHelper; + +export class UniqueHelper extends ChainHelperBase { + balance: BalanceGroup; + collection: CollectionGroup; + nft: NFTGroup; + rft: RFTGroup; + ft: FTGroup; + staking: StakingGroup; + preimage: PreimageGroup; + utility: UtilityGroup; + + constructor(logger?: ILogger, options: { [key: string]: any } = {}) { + super(logger, options.helperBase ?? UniqueHelper); + + this.balance = new BalanceGroup(this); + this.collection = new CollectionGroup(this); + this.nft = new NFTGroup(this); + this.rft = new RFTGroup(this); + this.ft = new FTGroup(this); + this.staking = new StakingGroup(this); + this.preimage = new PreimageGroup(this); + this.utility = new UtilityGroup(this); + } +} + +export class UniqueBaseCollection { + helper: UniqueHelper; + collectionId: number; + + constructor(collectionId: number, uniqueHelper: UniqueHelper) { + this.collectionId = collectionId; + this.helper = uniqueHelper; + } + + async getData() { + return await this.helper.collection.getData(this.collectionId); + } + + async getLastTokenId() { + return await this.helper.collection.getLastTokenId(this.collectionId); + } + + async doesTokenExist(tokenId: number) { + return await this.helper.collection.doesTokenExist(this.collectionId, tokenId); + } + + async getAdmins() { + return await this.helper.collection.getAdmins(this.collectionId); + } + + async getAllowList() { + return await this.helper.collection.getAllowList(this.collectionId); + } + + async getEffectiveLimits() { + return await this.helper.collection.getEffectiveLimits(this.collectionId); + } + + async getProperties(propertyKeys?: string[] | null) { + return await this.helper.collection.getProperties(this.collectionId, propertyKeys); + } + + async getPropertiesConsumedSpace() { + return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId); + } + + async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) { + return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj); + } + + async getOptions() { + return await this.helper.collection.getCollectionOptions(this.collectionId); + } + + async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) { + return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress); + } + + async confirmSponsorship(signer: TSigner) { + return await this.helper.collection.confirmSponsorship(signer, this.collectionId); + } + + async removeSponsor(signer: TSigner) { + return await this.helper.collection.removeSponsor(signer, this.collectionId); + } + + async setLimits(signer: TSigner, limits: ICollectionLimits) { + return await this.helper.collection.setLimits(signer, this.collectionId, limits); + } + + async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) { + return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress); + } + + async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) { + return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj); + } + + async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) { + return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj); + } + + async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) { + return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj); + } + + async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) { + return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj); + } + + async setProperties(signer: TSigner, properties: IProperty[]) { + return await this.helper.collection.setProperties(signer, this.collectionId, properties); + } + + async deleteProperties(signer: TSigner, propertyKeys: string[]) { + return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys); + } + + async setPermissions(signer: TSigner, permissions: ICollectionPermissions) { + return await this.helper.collection.setPermissions(signer, this.collectionId, permissions); + } + + async enableNesting(signer: TSigner, permissions: INestingPermissions) { + return await this.helper.collection.enableNesting(signer, this.collectionId, permissions); + } + + async disableNesting(signer: TSigner) { + return await this.helper.collection.disableNesting(signer, this.collectionId); + } + + async burn(signer: TSigner) { + return await this.helper.collection.burn(signer, this.collectionId); + } +} + +export class UniqueNFTCollection extends UniqueBaseCollection { + getTokenObject(tokenId: number) { + return new UniqueNFToken(tokenId, this); + } + + async getTokensByAddress(addressObj: ICrossAccountId) { + return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj); + } + + async getToken(tokenId: number, blockHashAt?: string) { + return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt); + } + + async getTokenOwner(tokenId: number, blockHashAt?: string) { + return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt); + } + + async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) { + return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt); + } + + async getTokenChildren(tokenId: number, blockHashAt?: string) { + return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt); + } + + async getPropertyPermissions(propertyKeys: string[] | null = null) { + return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys); + } + + async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) { + return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys); + } + + async getTokenPropertiesConsumedSpace(tokenId: number): Promise { + const api = this.helper.getApi(); + const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON() as any; + + return props?.consumedSpace ?? 0; + } + + async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) { + return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj); + } + + async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { + return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj); + } + + async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) { + return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj); + } + + async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) { + return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj); + } + + async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) { + return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties}); + } + + async mintMultipleTokens(signer: TSigner, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]) { + return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens); + } + + async burnToken(signer: TSigner, tokenId: number) { + return await this.helper.nft.burnToken(signer, this.collectionId, tokenId); + } + + async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) { + return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj); + } + + async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) { + return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties); + } + + async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) { + return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys); + } + + async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) { + return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions); + } + + async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) { + return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj); + } + + async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) { + return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj); + } +} + +export class UniqueRFTCollection extends UniqueBaseCollection { + getTokenObject(tokenId: number) { + return new UniqueRFToken(tokenId, this); + } + + async getToken(tokenId: number, blockHashAt?: string) { + return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt); + } + + async getTokenOwner(tokenId: number, blockHashAt?: string) { + return await this.helper.rft.getTokenOwner(this.collectionId, tokenId, blockHashAt); + } + + async getTokensByAddress(addressObj: ICrossAccountId) { + return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj); + } + + async getTop10TokenOwners(tokenId: number) { + return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId); + } + + async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) { + return await this.helper.rft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt); + } + + async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) { + return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj); + } + + async getTokenTotalPieces(tokenId: number) { + return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId); + } + + async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { + return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj); + } + + async getPropertyPermissions(propertyKeys: string[] | null = null) { + return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys); + } + + async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) { + return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys); + } + + async getTokenPropertiesConsumedSpace(tokenId: number): Promise { + const api = this.helper.getApi(); + const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON() as any; + + return props?.consumedSpace ?? 0; + } + + async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount = 1n) { + return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount); + } + + async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { + return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount); + } + + async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) { + return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount); + } + + async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) { + return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount); + } + + async mintToken(signer: TSigner, pieces = 1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) { + return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties}); + } + + async mintMultipleTokens(signer: TSigner, tokens: { pieces: bigint, owner: ICrossAccountId, properties?: IProperty[] }[]) { + return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens); + } + + async burnToken(signer: TSigner, tokenId: number, amount = 1n) { + return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount); + } + + async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n) { + return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount); + } + + async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) { + return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties); + } + + async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) { + return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys); + } + + async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) { + return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions); + } + + async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) { + return await this.helper.rft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj); + } + + async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) { + return await this.helper.rft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj); + } +} + +export class UniqueFTCollection extends UniqueBaseCollection { + async getBalance(addressObj: ICrossAccountId) { + return await this.helper.ft.getBalance(this.collectionId, addressObj); + } + + async getTotalPieces() { + return await this.helper.ft.getTotalPieces(this.collectionId); + } + + async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { + return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj); + } + + async getTop10Owners() { + return await this.helper.ft.getTop10Owners(this.collectionId); + } + + async mint(signer: TSigner, amount = 1n, owner: ICrossAccountId = {Substrate: signer.address}) { + return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner); + } + + async mintWithOneOwner(signer: TSigner, tokens: { value: bigint }[], owner: ICrossAccountId = {Substrate: signer.address}) { + return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner); + } + + async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) { + return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount); + } + + async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { + return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount); + } + + async burnTokens(signer: TSigner, amount = 1n) { + return await this.helper.ft.burnTokens(signer, this.collectionId, amount); + } + + async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) { + return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount); + } + + async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) { + return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount); + } +} + +export class UniqueBaseToken { + collection: UniqueNFTCollection | UniqueRFTCollection; + collectionId: number; + tokenId: number; + + constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) { + this.collection = collection; + this.collectionId = collection.collectionId; + this.tokenId = tokenId; + } + + async getNextSponsored(addressObj: ICrossAccountId) { + return await this.collection.getTokenNextSponsored(this.tokenId, addressObj); + } + + async getProperties(propertyKeys?: string[] | null) { + return await this.collection.getTokenProperties(this.tokenId, propertyKeys); + } + + async getTokenPropertiesConsumedSpace() { + return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId); + } + + async setProperties(signer: TSigner, properties: IProperty[]) { + return await this.collection.setTokenProperties(signer, this.tokenId, properties); + } + + async deleteProperties(signer: TSigner, propertyKeys: string[]) { + return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys); + } + + async doesExist() { + return await this.collection.doesTokenExist(this.tokenId); + } + + nestingAccount(): ICrossAccountId { + return this.collection.helper.util.getTokenAccount(this); + } +} + +export class UniqueNFToken extends UniqueBaseToken { + declare collection: UniqueNFTCollection; + + constructor(tokenId: number, collection: UniqueNFTCollection) { + super(tokenId, collection); + this.collection = collection; + } + + async getData(blockHashAt?: string) { + return await this.collection.getToken(this.tokenId, blockHashAt); + } + + async getOwner(blockHashAt?: string) { + return await this.collection.getTokenOwner(this.tokenId, blockHashAt); + } + + async getTopmostOwner(blockHashAt?: string) { + return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt); + } + + async getChildren(blockHashAt?: string) { + return await this.collection.getTokenChildren(this.tokenId, blockHashAt); + } + + async nest(signer: TSigner, toTokenObj: IToken) { + return await this.collection.nestToken(signer, this.tokenId, toTokenObj); + } + + async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) { + return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj); + } + + async transfer(signer: TSigner, addressObj: ICrossAccountId) { + return await this.collection.transferToken(signer, this.tokenId, addressObj); + } + + async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { + return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj); + } + + async approve(signer: TSigner, toAddressObj: ICrossAccountId) { + return await this.collection.approveToken(signer, this.tokenId, toAddressObj); + } + + async isApproved(toAddressObj: ICrossAccountId) { + return await this.collection.isTokenApproved(this.tokenId, toAddressObj); + } + + async burn(signer: TSigner) { + return await this.collection.burnToken(signer, this.tokenId); + } + + async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) { + return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj); + } +} + +export class UniqueRFToken extends UniqueBaseToken { + declare collection: UniqueRFTCollection; + + constructor(tokenId: number, collection: UniqueRFTCollection) { + super(tokenId, collection); + this.collection = collection; + } + + async getData(blockHashAt?: string) { + return await this.collection.getToken(this.tokenId, blockHashAt); + } + + async getOwner(blockHashAt?: string) { + return await this.collection.getTokenOwner(this.tokenId, blockHashAt); + } + + async getTop10Owners() { + return await this.collection.getTop10TokenOwners(this.tokenId); + } + + async getTopmostOwner(blockHashAt?: string) { + return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt); + } + + async nest(signer: TSigner, toTokenObj: IToken) { + return await this.collection.nestToken(signer, this.tokenId, toTokenObj); + } + + async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) { + return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj); + } + + async getBalance(addressObj: ICrossAccountId) { + return await this.collection.getTokenBalance(this.tokenId, addressObj); + } + + async getTotalPieces() { + return await this.collection.getTokenTotalPieces(this.tokenId); + } + + async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) { + return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj); + } + + async transfer(signer: TSigner, addressObj: ICrossAccountId, amount = 1n) { + return await this.collection.transferToken(signer, this.tokenId, addressObj, amount); + } + + async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) { + return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount); + } + + async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) { + return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount); + } + + async repartition(signer: TSigner, amount: bigint) { + return await this.collection.repartitionToken(signer, this.tokenId, amount); + } + + async burn(signer: TSigner, amount = 1n) { + return await this.collection.burnToken(signer, this.tokenId, amount); + } + + async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) { + return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount); + } +} --- /dev/null +++ b/js-packages/playgrounds/unique.xcm.ts @@ -0,0 +1,375 @@ +import {ApiPromise, WsProvider} from '@polkadot/api'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, UniqueHelper} from './unique.js'; +import type {ILogger, TSigner, TSubstrateAccount} from './types.js'; +import type {AcalaAssetMetadata, DemocracyStandardAccountVote, IForeignAssetMetadata, MoonbeamAssetInfo} from './types.xcm.js'; + + +export class XcmChainHelper extends ChainHelperBase { + override async connect(wsEndpoint: string, _listeners?: any): Promise { + 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 { + async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) { + await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true); + } +} + +class MoonbeamAssetManagerGroup extends HelperGroup { + 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 { + 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 { + 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 extends HelperGroup { + async whitelistToken(signer: TSigner, assetId: any) { + await this.helper.executeExtrinsic(signer, 'api.tx.xcmHelper.whitelistToken', [assetId], true); + } +} + +export class ForeignAssetsGroup extends HelperGroup { + 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 extends HelperGroup { + 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 extends HelperGroup { + 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 extends HelperGroup { + 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 extends HelperGroup { + async create(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint) { + await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true); + } + + async forceCreate(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint, isSufficient = true) { + await this.helper.executeExtrinsic(signer, 'api.tx.assets.forceCreate', [assetId, admin, isSufficient, minimalBalance], true); + } + + async setMetadata(signer: TSigner, assetId: number | bigint, 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 | bigint, beneficiary: string, amount: bigint) { + await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true); + } + + async account(assetId: string | number | bigint, 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; + xcm: XcmGroup; + + 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; + xcm: XcmGroup; + assets: AssetsGroup; + xTokens: XTokensGroup; + + 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; + assetManager: MoonbeamAssetManagerGroup; + assets: AssetsGroup; + xTokens: XTokensGroup; + 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; + assets: AssetsGroup; + xcm: XcmGroup; + + 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; + assetRegistry: AcalaAssetRegistryGroup; + xTokens: XTokensGroup; + tokens: TokensGroup; + xcm: XcmGroup; + + 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; + balance: SubstrateBalanceGroup; + xTokens: XTokensGroup; + xcm: XcmGroup; + xcmHelper: PolkadexXcmHelperGroup; + + 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); + } +} + --- /dev/null +++ b/js-packages/scripts/benchmarks/mintFee/index.ts @@ -0,0 +1,399 @@ +import {usingEthPlaygrounds} from '@unique/tests/eth/util/index.js'; +import {EthUniqueHelper} from '@unique/tests/eth/util/playgrounds/unique.dev.js'; +import {readFile} from 'fs/promises'; +import type {ICrossAccountId} from '@unique/playgrounds/types.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {UniqueNFTCollection} from '@unique/playgrounds/unique.js'; +import {Contract} from 'web3-eth-contract'; +import {createObjectCsvWriter} from 'csv-writer'; +import {convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES} from '../utils/common.js'; +import {makeNames} from '@unique/tests/util/index.js'; +import type {ContractImports} from '@unique/tests/eth/util/playgrounds/types.js'; + +const {dirname} = makeNames(import.meta.url); + +export const CONTRACT_IMPORT: ContractImports[] = [ + { + fsPath: `${dirname}/../../../../tests/eth/api/CollectionHelpers.sol`, + solPath: 'eth/api/CollectionHelpers.sol', + }, + { + fsPath: `${dirname}/../../../../tests/eth/api/ContractHelpers.sol`, + solPath: 'eth/api/ContractHelpers.sol', + }, + { + fsPath: `${dirname}/../../../../tests/eth/api/UniqueRefungibleToken.sol`, + solPath: 'eth/api/UniqueRefungibleToken.sol', + }, + { + fsPath: `${dirname}/../../../../tests/eth/api/UniqueRefungible.sol`, + solPath: 'eth/api/UniqueRefungible.sol', + }, + { + fsPath: `${dirname}/../../../../tests/eth/api/UniqueNFT.sol`, + solPath: 'eth/api/UniqueNFT.sol', + }, +]; + +interface IBenchmarkResultForProp { + propertiesNumber: number; + substrateFee: number; + ethFee: number; + ethBulkFee: number; + ethMintCrossFee: number; + evmProxyContractFee: number; + evmProxyContractBulkFee: number; +} + +const main = async () => { + const benchmarks = [ + 'substrateFee', + 'ethFee', + 'ethBulkFee', + 'ethMintCrossFee', + 'evmProxyContractFee', + 'evmProxyContractBulkFee', + ]; + const headers = [ + 'propertiesNumber', + ...benchmarks, + ]; + + + const csvWriter = createObjectCsvWriter({ + path: 'properties.csv', + header: headers, + }); + + await usingEthPlaygrounds(async (helper, privateKey) => { + const CONTRACT_SOURCE = ( + await readFile(`${dirname}/proxyContract.sol`) + ).toString(); + + const donor = await privateKey('//Alice'); // Seed from account with balance on this network + const ethSigner = await helper.eth.createAccountWithBalance(donor); + + const contract = await helper.ethContract.deployByCode( + ethSigner, + 'ProxyMint', + CONTRACT_SOURCE, + CONTRACT_IMPORT, + ); + + const fees = await benchMintFee(helper, privateKey, contract); + console.log('Minting without properties'); + console.table(fees); + + const result: IBenchmarkResultForProp[] = []; + const csvResult: IBenchmarkResultForProp[] = []; + + for(let i = 1; i <= 20; i++) { + const benchResult = await benchMintWithProperties(helper, privateKey, contract, { + propertiesNumber: i, + }) as any; + + csvResult.push(benchResult); + + const minFee = Math.min(...(benchmarks.map(x => benchResult[x]))); + for(const key of benchmarks) { + const keyPercent = Math.round((benchResult[key] / minFee) * 100); + benchResult[key] = `${benchResult[key]} (${keyPercent}%)`; + } + + result.push(benchResult); + } + + await csvWriter.writeRecords(csvResult); + + console.log('Minting with properties'); + console.table(result, headers); + }); +}; + +main() + .then(() => process.exit(0)) + .catch((e) => { + console.log(e); + process.exit(1); + }); + + + +async function benchMintFee( + helper: EthUniqueHelper, + privateKey: (seed: string) => Promise, + proxyContract: Contract, +): Promise<{ + substrateFee: number; + ethFee: number; + evmProxyContractFee: number; +}> { + const donor = await privateKey('//Alice'); + const substrateReceiver = await privateKey('//Bob'); + const ethSigner = await helper.eth.createAccountWithBalance(donor); + + const nominal = helper.balance.getOneTokenNominal(); + + await helper.eth.transferBalanceFromSubstrate( + donor, + proxyContract.options.address, + 100n, + ); + + const collection = (await createCollectionForBenchmarks( + 'nft', + helper, + privateKey, + ethSigner, + proxyContract.options.address, + PERMISSIONS, + )) as UniqueNFTCollection; + + const substrateFee = await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.mintToken(donor, {Substrate: substrateReceiver.address}), + ); + + const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionContract = await helper.ethNativeContract.collection( + collectionEthAddress, + 'nft', + ); + + const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address); + + const encodedCall = collectionContract.methods + .mint(receiverEthAddress) + .encodeABI(); + + const ethFee = await helper.arrange.calculcateFee( + {Substrate: donor.address}, + async () => { + await helper.eth.sendEVM( + donor, + collectionContract.options.address, + encodedCall, + '0', + ); + }, + ); + + const evmProxyContractFee = await helper.arrange.calculcateFee( + {Ethereum: ethSigner}, + async () => { + await proxyContract.methods + .mintToSubstrate( + helper.ethAddress.fromCollectionId(collection.collectionId), + substrateReceiver.addressRaw, + ) + .send({from: ethSigner}); + }, + ); + + return { + substrateFee: convertToTokens(substrateFee, nominal), + ethFee: convertToTokens(ethFee, nominal), + evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal), + }; +} + +async function benchMintWithProperties( + helper: EthUniqueHelper, + privateKey: (seed: string) => Promise, + proxyContract: Contract, + setup: { propertiesNumber: number }, +): Promise { + const donor = await privateKey('//Alice'); // Seed from account with balance on this network + const ethSigner = await helper.eth.createAccountWithBalance(donor); + + const susbstrateReceiver = await privateKey('//Bob'); + const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address); + + const nominal = helper.balance.getOneTokenNominal(); + + const substrateFee = await calculateFeeNftMintWithProperties( + helper, + privateKey, + {Substrate: donor.address}, + ethSigner, + proxyContract.options.address, + async (collection) => { + await collection.mintToken( + donor, + {Substrate: susbstrateReceiver.address}, + PROPERTIES.slice(0, setup.propertiesNumber).map((p) => ({key: p.key, value: Buffer.from(p.value).toString()})), + ); + }, + ); + + const ethFee = await calculateFeeNftMintWithProperties( + helper, + privateKey, + {Substrate: donor.address}, + ethSigner, + proxyContract.options.address, + async (collection) => { + const evmContract = await helper.ethNativeContract.collection( + helper.ethAddress.fromCollectionId(collection.collectionId), + 'nft', + undefined, + true, + ); + + const subTokenId = await evmContract.methods.nextTokenId().call(); + + let encodedCall = evmContract.methods + .mint(receiverEthAddress) + .encodeABI(); + + await helper.eth.sendEVM( + donor, + evmContract.options.address, + encodedCall, + '0', + ); + + for(const val of PROPERTIES.slice(0, setup.propertiesNumber)) { + encodedCall = await evmContract.methods + .setProperty(subTokenId, val.key, Buffer.from(val.value)) + .encodeABI(); + + await helper.eth.sendEVM( + donor, + evmContract.options.address, + encodedCall, + '0', + ); + } + }, + ); + + const ethBulkFee = await calculateFeeNftMintWithProperties( + helper, + privateKey, + {Substrate: donor.address}, + ethSigner, + proxyContract.options.address, + async (collection) => { + const evmContract = await helper.ethNativeContract.collection( + helper.ethAddress.fromCollectionId(collection.collectionId), + 'nft', + ); + + const subTokenId = await evmContract.methods.nextTokenId().call(); + + let encodedCall = evmContract.methods + .mint(receiverEthAddress) + .encodeABI(); + + await helper.eth.sendEVM( + donor, + evmContract.options.address, + encodedCall, + '0', + ); + + encodedCall = await evmContract.methods + .setProperties( + subTokenId, + PROPERTIES.slice(0, setup.propertiesNumber), + ) + .encodeABI(); + + await helper.eth.sendEVM( + donor, + evmContract.options.address, + encodedCall, + '0', + ); + }, + ); + + const ethMintCrossFee = await calculateFeeNftMintWithProperties( + helper, + privateKey, + {Ethereum: ethSigner}, + ethSigner, + proxyContract.options.address, + async (collection) => { + const evmContract = await helper.ethNativeContract.collection( + helper.ethAddress.fromCollectionId(collection.collectionId), + 'nft', + ); + + await evmContract.methods.mintCross( + helper.ethCrossAccount.fromAddress(receiverEthAddress), + PROPERTIES.slice(0, setup.propertiesNumber), + ) + .send({from: ethSigner}); + }, + ); + + const proxyContractFee = await calculateFeeNftMintWithProperties( + helper, + privateKey, + {Ethereum: ethSigner}, + ethSigner, + proxyContract.options.address, + async (collection) => { + await proxyContract.methods + .mintToSubstrateWithProperty( + helper.ethAddress.fromCollectionId(collection.collectionId), + susbstrateReceiver.addressRaw, + PROPERTIES.slice(0, setup.propertiesNumber), + ) + .send({from: ethSigner}); + }, + ); + + const proxyContractBulkFee = await calculateFeeNftMintWithProperties( + helper, + privateKey, + {Ethereum: ethSigner}, + ethSigner, + proxyContract.options.address, + async (collection) => { + await proxyContract.methods + .mintToSubstrateBulkProperty( + helper.ethAddress.fromCollectionId(collection.collectionId), + susbstrateReceiver.addressRaw, + PROPERTIES.slice(0, setup.propertiesNumber), + ) + .send({from: ethSigner}); + }, + ); + + return { + propertiesNumber: setup.propertiesNumber, + substrateFee: convertToTokens(substrateFee, nominal), + ethFee: convertToTokens(ethFee, nominal), + ethBulkFee: convertToTokens(ethBulkFee, nominal), + ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal), + evmProxyContractFee: convertToTokens(proxyContractFee, nominal), + evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal), + }; +} + +async function calculateFeeNftMintWithProperties( + helper: EthUniqueHelper, + privateKey: (seed: string) => Promise, + payer: ICrossAccountId, + ethSigner: string, + proxyContractAddress: string, + calculatedCall: (collection: UniqueNFTCollection) => Promise, +): Promise { + const collection = (await createCollectionForBenchmarks( + 'nft', + helper, + privateKey, + ethSigner, + proxyContractAddress, + PERMISSIONS, + )) as UniqueNFTCollection; + return helper.arrange.calculcateFee(payer, async () => { + await calculatedCall(collection); + }); +} + + + --- /dev/null +++ b/js-packages/scripts/benchmarks/mintFee/proxyContract.sol @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: Apache License +pragma solidity >=0.8.0; +import {CollectionHelpers} from "eth/api/CollectionHelpers.sol"; +import {ContractHelpers} from "eth/api/ContractHelpers.sol"; +import {UniqueRefungibleToken} from "eth/api/UniqueRefungibleToken.sol"; +import {UniqueRefungible, Collection, CrossAddress as RftCrossAccountId, Property as RftProperty} from "eth/api/UniqueRefungible.sol"; +import {UniqueNFT, CrossAddress as NftCrossAccountId, Property as NftProperty} from "eth/api/UniqueNFT.sol"; + +struct Property { + string key; + bytes value; +} + +interface SoftDeprecatedMethods { + /// @notice Set token property value. + /// @dev Throws error if `msg.sender` has no permission to edit the property. + /// @param tokenId ID of the token. + /// @param key Property key. + /// @param value Property value. + /// @dev EVM selector for this function is: 0x1752d67b, + /// or in textual repr: setProperty(uint256,string,bytes) + function setProperty( + uint256 tokenId, + string memory key, + bytes memory value + ) external; +} + +interface BenchUniqueRefungible is UniqueRefungible, SoftDeprecatedMethods {} +interface BenchUniqueNFT is UniqueNFT, SoftDeprecatedMethods {} + + + +contract ProxyMint { + bytes32 constant REFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("ReFungible")); + bytes32 constant NONFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("NFT")); + + modifier checkRestrictions(address _collection) { + Collection commonContract = Collection(_collection); + require( + commonContract.isOwnerOrAdminCross(RftCrossAccountId(msg.sender, 0)), + "Only collection admin/owner can call this method" + ); + _; + } + + /// @dev This emits when a mint to a substrate address has been made. + event MintToSub(address _toEth, uint256 _toSub, address _collection, uint256 _tokenId); + + function mintToSubstrate(address _collection, uint256 _substrateReceiver) external checkRestrictions(_collection) { + Collection commonContract = Collection(_collection); + bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType())); + uint256 tokenId; + + if (collectionType == REFUNGIBLE_COLLECTION_TYPE) { + UniqueRefungible rftCollection = UniqueRefungible(_collection); + + tokenId = rftCollection.mint(address(this)); + + rftCollection.transferFromCross( + RftCrossAccountId(address(this), 0), + RftCrossAccountId(address(0), _substrateReceiver), + tokenId + ); + } else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) { + UniqueNFT nftCollection = UniqueNFT(_collection); + tokenId = nftCollection.mint(address(this)); + + nftCollection.transferFromCross( + NftCrossAccountId(address(this), 0), + NftCrossAccountId(address(0), _substrateReceiver), + tokenId + ); + } else { + revert("Wrong collection type. Works only with NFT or RFT"); + } + + emit MintToSub(address(0), _substrateReceiver, _collection, tokenId); + } + + function mintToSubstrateWithProperty( + address _collection, + uint256 _substrateReceiver, + Property[] calldata _properties + ) external checkRestrictions(_collection) { + uint256 propertiesLength = _properties.length; + require(propertiesLength > 0, "Properies is empty"); + + Collection commonContract = Collection(_collection); + bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType())); + uint256 tokenId; + + if (collectionType == REFUNGIBLE_COLLECTION_TYPE) { + BenchUniqueRefungible rftCollection = BenchUniqueRefungible(_collection); + tokenId = rftCollection.nextTokenId(); + rftCollection.mint(address(this)); + + for (uint256 i = 0; i < propertiesLength; ++i) { + rftCollection.setProperty(tokenId, _properties[i].key, _properties[i].value); + } + rftCollection.transferFromCross( + RftCrossAccountId(address(this), 0), + RftCrossAccountId(address(0), _substrateReceiver), + tokenId + ); + } else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) { + BenchUniqueNFT nftCollection = BenchUniqueNFT(_collection); + tokenId = nftCollection.mint(address(this)); + for (uint256 i = 0; i < propertiesLength; ++i) { + nftCollection.setProperty(tokenId, _properties[i].key, _properties[i].value); + } + nftCollection.transferFromCross( + NftCrossAccountId(address(this), 0), + NftCrossAccountId(address(0), _substrateReceiver), + tokenId + ); + } else { + revert("Wrong collection type. Works only with NFT or RFT"); + } + + emit MintToSub(address(0), _substrateReceiver, _collection, tokenId); + } + + function mintToSubstrateBulkProperty( + address _collection, + uint256 _substrateReceiver, + NftProperty[] calldata _properties + ) external checkRestrictions(_collection) { + uint256 propertiesLength = _properties.length; + require(propertiesLength > 0, "Properies is empty"); + + Collection commonContract = Collection(_collection); + bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType())); + uint256 tokenId; + + if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) { + UniqueNFT nftCollection = UniqueNFT(_collection); + tokenId = nftCollection.mint(address(this)); + + nftCollection.setProperties(tokenId, _properties); + + nftCollection.transferFromCross( + NftCrossAccountId(address(this), 0), + NftCrossAccountId(address(0), _substrateReceiver), + tokenId + ); + } else { + revert("Wrong collection type. Works only with NFT or RFT"); + } + + emit MintToSub(address(0), _substrateReceiver, _collection, tokenId); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/nesting/ABIGEN/RMRKNestableMintable.ts @@ -0,0 +1,354 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import type BN from "bn.js"; +import type { ContractOptions } from "web3-eth-contract"; +import type { EventLog } from "web3-core"; +import type { EventEmitter } from "events"; +import type { + Callback, + NonPayableTransactionObject, + BlockType, + ContractEventLog, + BaseContract, +} from "./types.js"; + +export interface EventOptions { + filter?: object; + fromBlock?: BlockType; + topics?: string[]; +} + +export type AllChildrenRejected = ContractEventLog<{ + tokenId: string; + 0: string; +}>; +export type Approval = ContractEventLog<{ + owner: string; + approved: string; + tokenId: string; + 0: string; + 1: string; + 2: string; +}>; +export type ApprovalForAll = ContractEventLog<{ + owner: string; + operator: string; + approved: boolean; + 0: string; + 1: string; + 2: boolean; +}>; +export type ChildAccepted = ContractEventLog<{ + tokenId: string; + childIndex: string; + childAddress: string; + childId: string; + 0: string; + 1: string; + 2: string; + 3: string; +}>; +export type ChildProposed = ContractEventLog<{ + tokenId: string; + childIndex: string; + childAddress: string; + childId: string; + 0: string; + 1: string; + 2: string; + 3: string; +}>; +export type ChildTransferred = ContractEventLog<{ + tokenId: string; + childIndex: string; + childAddress: string; + childId: string; + fromPending: boolean; + toZero: boolean; + 0: string; + 1: string; + 2: string; + 3: string; + 4: boolean; + 5: boolean; +}>; +export type NestTransfer = ContractEventLog<{ + from: string; + to: string; + fromTokenId: string; + toTokenId: string; + tokenId: string; + 0: string; + 1: string; + 2: string; + 3: string; + 4: string; +}>; +export type Transfer = ContractEventLog<{ + from: string; + to: string; + tokenId: string; + 0: string; + 1: string; + 2: string; +}>; + +export interface RMRKNestableMintable extends BaseContract { + constructor( + jsonInterface: any[], + address?: string, + options?: ContractOptions + ): RMRKNestableMintable; + clone(): RMRKNestableMintable; + methods: { + RMRK_INTERFACE(): NonPayableTransactionObject; + + VERSION(): NonPayableTransactionObject; + + acceptChild( + parentId: number | string | BN, + childIndex: number | string | BN, + childAddress: string, + childId: number | string | BN + ): NonPayableTransactionObject; + + addChild( + parentId: number | string | BN, + childId: number | string | BN, + data: string | number[] + ): NonPayableTransactionObject; + + approve( + to: string, + tokenId: number | string | BN + ): NonPayableTransactionObject; + + balanceOf(owner: string): NonPayableTransactionObject; + + "burn(uint256)"( + tokenId: number | string | BN + ): NonPayableTransactionObject; + + "burn(uint256,uint256)"( + tokenId: number | string | BN, + maxChildrenBurns: number | string | BN + ): NonPayableTransactionObject; + + childOf( + parentId: number | string | BN, + index: number | string | BN + ): NonPayableTransactionObject<[string, string]>; + + childrenOf( + parentId: number | string | BN + ): NonPayableTransactionObject<[string, string][]>; + + directOwnerOf(tokenId: number | string | BN): NonPayableTransactionObject<{ + 0: string; + 1: string; + 2: boolean; + }>; + + getApproved( + tokenId: number | string | BN + ): NonPayableTransactionObject; + + isApprovedForAll( + owner: string, + operator: string + ): NonPayableTransactionObject; + + mint( + to: string, + tokenId: number | string | BN + ): NonPayableTransactionObject; + + name(): NonPayableTransactionObject; + + nestMint( + to: string, + tokenId: number | string | BN, + destinationId: number | string | BN + ): NonPayableTransactionObject; + + nestTransfer( + to: string, + tokenId: number | string | BN, + destinationId: number | string | BN + ): NonPayableTransactionObject; + + nestTransferFrom( + from: string, + to: string, + tokenId: number | string | BN, + destinationId: number | string | BN, + data: string | number[] + ): NonPayableTransactionObject; + + onERC721Received( + _operator: string, + _from: string, + _tokenId: number | string | BN, + _data: string | number[] + ): NonPayableTransactionObject; + + ownerOf(tokenId: number | string | BN): NonPayableTransactionObject; + + pendingChildOf( + parentId: number | string | BN, + index: number | string | BN + ): NonPayableTransactionObject<[string, string]>; + + pendingChildrenOf( + parentId: number | string | BN + ): NonPayableTransactionObject<[string, string][]>; + + rejectAllChildren( + tokenId: number | string | BN, + maxRejections: number | string | BN + ): NonPayableTransactionObject; + + safeMint(to: string): NonPayableTransactionObject; + + "safeTransferFrom(address,address,uint256)"( + from: string, + to: string, + tokenId: number | string | BN + ): NonPayableTransactionObject; + + "safeTransferFrom(address,address,uint256,bytes)"( + from: string, + to: string, + tokenId: number | string | BN, + data: string | number[] + ): NonPayableTransactionObject; + + setApprovalForAll( + operator: string, + approved: boolean + ): NonPayableTransactionObject; + + supportsInterface( + interfaceId: string | number[] + ): NonPayableTransactionObject; + + symbol(): NonPayableTransactionObject; + + transfer( + to: string, + tokenId: number | string | BN + ): NonPayableTransactionObject; + + transferChild( + tokenId: number | string | BN, + to: string, + destinationId: number | string | BN, + childIndex: number | string | BN, + childAddress: string, + childId: number | string | BN, + isPending: boolean, + data: string | number[] + ): NonPayableTransactionObject; + + transferFrom( + from: string, + to: string, + tokenId: number | string | BN + ): NonPayableTransactionObject; + }; + events: { + AllChildrenRejected(cb?: Callback): EventEmitter; + AllChildrenRejected( + options?: EventOptions, + cb?: Callback + ): EventEmitter; + + Approval(cb?: Callback): EventEmitter; + Approval(options?: EventOptions, cb?: Callback): EventEmitter; + + ApprovalForAll(cb?: Callback): EventEmitter; + ApprovalForAll( + options?: EventOptions, + cb?: Callback + ): EventEmitter; + + ChildAccepted(cb?: Callback): EventEmitter; + ChildAccepted( + options?: EventOptions, + cb?: Callback + ): EventEmitter; + + ChildProposed(cb?: Callback): EventEmitter; + ChildProposed( + options?: EventOptions, + cb?: Callback + ): EventEmitter; + + ChildTransferred(cb?: Callback): EventEmitter; + ChildTransferred( + options?: EventOptions, + cb?: Callback + ): EventEmitter; + + NestTransfer(cb?: Callback): EventEmitter; + NestTransfer( + options?: EventOptions, + cb?: Callback + ): EventEmitter; + + Transfer(cb?: Callback): EventEmitter; + Transfer(options?: EventOptions, cb?: Callback): EventEmitter; + + allEvents(options?: EventOptions, cb?: Callback): EventEmitter; + }; + + once(event: "AllChildrenRejected", cb: Callback): void; + once( + event: "AllChildrenRejected", + options: EventOptions, + cb: Callback + ): void; + + once(event: "Approval", cb: Callback): void; + once(event: "Approval", options: EventOptions, cb: Callback): void; + + once(event: "ApprovalForAll", cb: Callback): void; + once( + event: "ApprovalForAll", + options: EventOptions, + cb: Callback + ): void; + + once(event: "ChildAccepted", cb: Callback): void; + once( + event: "ChildAccepted", + options: EventOptions, + cb: Callback + ): void; + + once(event: "ChildProposed", cb: Callback): void; + once( + event: "ChildProposed", + options: EventOptions, + cb: Callback + ): void; + + once(event: "ChildTransferred", cb: Callback): void; + once( + event: "ChildTransferred", + options: EventOptions, + cb: Callback + ): void; + + once(event: "NestTransfer", cb: Callback): void; + once( + event: "NestTransfer", + options: EventOptions, + cb: Callback + ): void; + + once(event: "Transfer", cb: Callback): void; + once(event: "Transfer", options: EventOptions, cb: Callback): void; +} --- /dev/null +++ b/js-packages/scripts/benchmarks/nesting/ABIGEN/index.ts @@ -0,0 +1,4 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +export type { RMRKNestableMintable } from "./RMRKNestableMintable.js"; --- /dev/null +++ b/js-packages/scripts/benchmarks/nesting/ABIGEN/types.ts @@ -0,0 +1,73 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type BN from "bn.js"; +import type { EventEmitter } from "events"; +import type { EventLog, PromiEvent, TransactionReceipt } from "web3-core"; +import type { Contract } from "web3-eth-contract"; + +export interface EstimateGasOptions { + from?: string; + gas?: number; + value?: number | string | BN; +} + +export interface EventOptions { + filter?: object; + fromBlock?: BlockType; + topics?: string[]; +} + +export type Callback = (error: Error, result: T) => void; +export interface ContractEventLog extends EventLog { + returnValues: T; +} +export interface ContractEventEmitter extends EventEmitter { + on(event: "connected", listener: (subscriptionId: string) => void): this; + on( + event: "data" | "changed", + listener: (event: ContractEventLog) => void + ): this; + on(event: "error", listener: (error: Error) => void): this; +} + +export interface NonPayableTx { + nonce?: string | number | BN; + chainId?: string | number | BN; + from?: string; + to?: string; + data?: string; + gas?: string | number | BN; + maxPriorityFeePerGas?: string | number | BN; + maxFeePerGas?: string | number | BN; + gasPrice?: string | number | BN; +} + +export interface PayableTx extends NonPayableTx { + value?: string | number | BN; +} + +export interface NonPayableTransactionObject { + arguments: any[]; + call(tx?: NonPayableTx, block?: BlockType): Promise; + send(tx?: NonPayableTx): PromiEvent; + estimateGas(tx?: NonPayableTx): Promise; + encodeABI(): string; +} + +export interface PayableTransactionObject { + arguments: any[]; + call(tx?: PayableTx, block?: BlockType): Promise; + send(tx?: PayableTx): PromiEvent; + estimateGas(tx?: PayableTx): Promise; + encodeABI(): string; +} + +export type BlockType = + | "latest" + | "pending" + | "genesis" + | "earliest" + | number + | BN; +export type BaseContract = Omit; --- /dev/null +++ b/js-packages/scripts/benchmarks/nesting/RMRKNestableMintable.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +import "@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; + +contract RMRKNestableMintable is RMRKNestable, IERC721Receiver { + uint256 private _counter; + + constructor() RMRKNestable("RMRK", "nesting") { + _counter = 1; + } + + function safeMint(address to) external { + _safeMint(to, _counter, ""); + _counter++; + } + + function mint(address to, uint256 tokenId) external { + _mint(to, tokenId, ""); + } + + function nestMint(address to, uint256 tokenId, uint256 destinationId) external { + _nestMint(to, tokenId, destinationId, ""); + } + + function nestTransfer(address to, uint256 tokenId, uint256 destinationId) public virtual { + nestTransferFrom(_msgSender(), to, tokenId, destinationId, ""); + } + + function transfer(address to, uint256 tokenId) public virtual { + transferFrom(_msgSender(), to, tokenId); + } + + function onERC721Received( + address _operator, + address _from, + uint256 _tokenId, + bytes calldata _data + ) external returns (bytes4) { + return IERC721Receiver.onERC721Received.selector; + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/nesting/abigen.ts @@ -0,0 +1,20 @@ +import {runTypeChain, glob, DEFAULT_FLAGS} from 'typechain'; + + +async function main() { + const cwd = process.cwd(); + + // find all files matching the glob + const allFiles = glob(cwd, ['./ABI/*.abi']); + + await runTypeChain({ + cwd, + filesToProcess: allFiles, + allFiles, + outDir: 'ABIGEN', + target: 'web3-v1', + flags: {...DEFAULT_FLAGS, alwaysGenerateOverloads: true, tsNocheck: false}, + }); +} + +main().catch(console.error); \ No newline at end of file --- /dev/null +++ b/js-packages/scripts/benchmarks/nesting/index.ts @@ -0,0 +1,222 @@ +import {usingEthPlaygrounds} from '@unique/tests/eth/util/index.js'; +import {EthUniqueHelper} from '@unique/tests/eth/util/playgrounds/unique.dev.js'; +import {readFile} from 'fs/promises'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {Contract} from 'web3-eth-contract'; +import {convertToTokens} from '../utils/common.js'; +import {makeNames} from '@unique/tests/util/index.js'; +import type {ContractImports} from '@unique/tests/eth/util/playgrounds/types.js'; +import type {RMRKNestableMintable} from './ABIGEN/index.js'; + +const {dirname} = makeNames(import.meta.url); + +export const CONTRACT_IMPORT: ContractImports[] = [ + { + fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol`, + solPath: '@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol', + }, + { + fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/nestable/IERC6059.sol`, + solPath: '@rmrk-team/evm-contracts/contracts/RMRK/nestable/IERC6059.sol', + }, + { + fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/core/RMRKCore.sol`, + solPath: '@rmrk-team/evm-contracts/contracts/RMRK/core/RMRKCore.sol', + }, + { + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol`, + solPath: '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol', + }, + { + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol`, + solPath: '@openzeppelin/contracts/token/ERC721/IERC721.sol', + }, + { + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol`, + solPath: '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol', + }, + { + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Address.sol`, + solPath: '@openzeppelin/contracts/utils/Address.sol', + }, + { + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Context.sol`, + solPath: '@openzeppelin/contracts/utils/Context.sol', + }, + { + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol`, + solPath: '@openzeppelin/contracts/utils/introspection/IERC165.sol', + }, + { + fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/library/RMRKErrors.sol`, + solPath: '@rmrk-team/evm-contracts/contracts/RMRK/library/RMRKErrors.sol', + }, + { + fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/core/IRMRKCore.sol`, + solPath: '@rmrk-team/evm-contracts/contracts/RMRK/core/IRMRKCore.sol', + }, + { + fsPath: `${dirname}/RMRKNestableMintable.sol`, + solPath: 'RMRKNestableMintable.sol', + }, +]; + + +const main = async () => { + + await usingEthPlaygrounds(async (helper, privateKey) => { + + const donor = await privateKey('//Alice'); // Seed from account with balance on this network + + const eth = await measureEth(helper, donor); + const sub = await measureSub(helper, donor); + const rmrk = await measureRMRK(helper, donor); + console.table({susbtrate: sub, eth: eth, rmrk: rmrk}); + }); +}; + +async function measureRMRK(helper: EthUniqueHelper, donor: IKeyringPair) { + const CONTRACT_SOURCE = ( + await readFile(`${dirname}/RMRKNestableMintable.sol`) + ).toString(); + const RELAYER_SOURCE = (await readFile(`${dirname}/relayer.sol`)).toString(); + + const ethSigner = await helper.eth.createAccountWithBalance(donor); + + const contract = await helper.ethContract.deployByCode( + ethSigner, + 'RMRKNestableMintable', + CONTRACT_SOURCE, + CONTRACT_IMPORT, + 5000000, + ); + + const relayer = await helper.ethContract.deployByCode( + ethSigner, + 'Relayer', + RELAYER_SOURCE, + CONTRACT_IMPORT, + 5000000, + [contract.options.address], + ); + + const relayerAddress = relayer.options.address; + + const rmrk = contract as any as RMRKNestableMintable; + const createTokenFor = async (receiver: string) => { + const tokenReceipt = await rmrk.methods.safeMint(receiver).send({from: ethSigner}); + return tokenReceipt.events!['Transfer'].returnValues.tokenId as number; + }; + + + const nestId = await createTokenFor(ethSigner); + const outerCollectionNestedId = 10; + const contractOwnedNestId = await createTokenFor(relayerAddress); + const nextTokenId = contractOwnedNestId + 1; + + const nestTransfer = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => { + const addChildData = rmrk.methods.addChild(nestId, outerCollectionNestedId, []).encodeABI(); + await relayer.methods.relay(addChildData).send({from: ethSigner}); + await rmrk.methods.acceptChild(nestId, 0, relayerAddress, outerCollectionNestedId).send({from: ethSigner}); + }); + + + const nestMint = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => { + const nestMintData = rmrk.methods.nestMint(rmrk.options.address, nextTokenId, contractOwnedNestId).encodeABI(); + await relayer.methods.relay(nestMintData).send({from: ethSigner}); + const acceptNestedToken = rmrk.methods.acceptChild(contractOwnedNestId, 0, rmrk.options.address, nextTokenId).encodeABI(); + await relayer.methods.relay(acceptNestedToken).send({from: ethSigner}); + }); + + + const unnestToken = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => { + const unnestData = rmrk.methods.transferChild(contractOwnedNestId, ethSigner, 0, 0, rmrk.options.address, nextTokenId, false, []).encodeABI(); + await relayer.methods.relay(unnestData).send({from: ethSigner}); + }); + + return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)}; +} + +async function measureEth(helper: EthUniqueHelper, donor: IKeyringPair) { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId, contract} = await createNestingCollection(helper, owner); + + // Create a token to be nested to + const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner}); + const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId; + const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId); + + // Create a nested token + const nestMint = await helper.arrange.calculcateFee({Ethereum: owner}, async () => { + await contract.methods.mint(targetNftTokenAddress).send({from: owner}); + }); + + // Create a token to be nested and nest + const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner}); + const nestedTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId; + + const nestTransfer = await helper.arrange.calculcateFee({Ethereum: owner}, async () => { + await contract.methods.transfer(targetNftTokenAddress, nestedTokenId).send({from: owner}); + }); + + const unnestToken = await helper.arrange.calculcateFee({Ethereum: owner}, async () => { + await contract.methods.transferFrom(targetNftTokenAddress, owner, nestedTokenId).send({from: owner}); + }); + return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)}; +} + +async function measureSub(helper: EthUniqueHelper, donor: IKeyringPair) { + + const [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + + const targetNFTCollection = await helper.nft.mintCollection(alice); + const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address}); + + + const collectionForNesting = await helper.nft.mintCollection(bob); + // permissions should be set: + await targetNFTCollection.setPermissions(alice, { + nesting: {tokenOwner: true}, + }); + + const nestMint = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => { + await (collectionForNesting.mintToken(bob, targetTokenBob.nestingAccount())); + }); + + const nestedToken2 = await collectionForNesting.mintToken(bob); + const nestTransfer = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => { + await nestedToken2.nest(bob, targetTokenBob); + }); + + const unnestToken = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => { + await nestedToken2.transferFrom(bob, targetTokenBob.nestingAccount(), {Substrate: bob.address}); + }); + + return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)}; +} + + +const createNestingCollection = async ( + helper: EthUniqueHelper, + owner: string, +): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => { + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + await contract.methods.setCollectionNesting(true).send({from: owner}); + + return {collectionId, collectionAddress, contract}; +}; + +main() + .then(() => process.exit(0)) + .catch((e) => { + console.log(e); + process.exit(1); + }); + + + + + + --- /dev/null +++ b/js-packages/scripts/benchmarks/nesting/relayer.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import "./RMRKNestableMintable.sol"; + +contract Relayer is RMRKNestableMintable { + address _receiver; + + constructor(address recevier) { + _receiver = recevier; + } + + function relay(bytes calldata payload) external { + (bool succes, bytes memory _returnData) = address(_receiver).call(payload); + require(succes); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/opsFee/index.ts @@ -0,0 +1,892 @@ +import {usingEthPlaygrounds} from '@unique/tests/eth/util/index.js'; +import {EthUniqueHelper} from '@unique/tests/eth/util/playgrounds/unique.dev.js'; +import {readFile} from 'fs/promises'; +import {CollectionLimitField, CreateCollectionData, TokenPermissionField} from '@unique/tests/eth/util/playgrounds/types.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {UniqueFTCollection, UniqueNFTCollection} from '@unique/playgrounds/unique.js'; +import {Contract} from 'web3-eth-contract'; +import {createObjectCsvWriter} from 'csv-writer'; +import {FunctionFeeVM} from '../utils/types.js'; +import type {IFunctionFee} from '../utils/types.js'; +import {convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES, SUBS_PROPERTIES} from '../utils/common.js'; +import {makeNames} from '@unique/tests/util/index.js'; + + +const {dirname} = makeNames(import.meta.url); + +const main = async () => { + + const headers = [ + 'function', + 'ethFee', + 'ethGas', + 'substrate', + 'zeppelinFee', + 'zeppelinGas', + ]; + + const csvWriter20 = createObjectCsvWriter({ + path: 'erc20.csv', + header: headers, + }); + + const csvWriter721 = createObjectCsvWriter({ + path: 'erc721.csv', + header: headers, + }); + + await usingEthPlaygrounds(async (helper, privateKey) => { + console.log('\n ERC20 ops fees'); + const erc20 = FunctionFeeVM.fromModel(await erc20CalculateFeeGas(helper, privateKey)); + console.table(erc20); + await csvWriter20.writeRecords(FunctionFeeVM.toCsv(erc20)); + + console.log('\n ERC721 ops fees'); + const erc721 = FunctionFeeVM.fromModel(await erc721CalculateFeeGas(helper, privateKey)); + console.table(erc721); + + await csvWriter721.writeRecords(FunctionFeeVM.toCsv(erc721)); + }); +}; + +main() + .then(() => process.exit(0)) + .catch((e) => { + console.log(e); + process.exit(1); + }); + +async function erc721CalculateFeeGas( + helper: EthUniqueHelper, + privateKey: (seed: string) => Promise, +): Promise { + const res: IFunctionFee = {}; + const donor = await privateKey('//Alice'); + const [subReceiver] = await helper.arrange.createAccounts([10n], donor); + const ethSigner = await helper.eth.createAccountWithBalance(donor); + const ethReceiver = await helper.eth.createAccountWithBalance(donor); + const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner); + const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver); + const collection = (await createCollectionForBenchmarks( + 'nft', + helper, + privateKey, + ethSigner, + null, + PERMISSIONS, + )) as UniqueNFTCollection; + + const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner); + let zeppelelinContract: Contract | null = null; + const ZEPPELIN_OBJECT = '0x' + (await readFile(`${dirname}/../utils/openZeppelin/ERC721/bin/ZeppelinContract.bin`)).toString(); + const ZEPPELIN_ABI = JSON.parse((await readFile(`${dirname}/../utils/openZeppelin/ERC721/bin/ZeppelinContract.abi`)).toString()); + + const evmContract = await helper.ethNativeContract.collection( + helper.ethAddress.fromCollectionId(collection.collectionId), + 'nft', + ethSigner, + true, + ); + + res['createCollection'] = await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => helperContract.methods.createNFTCollection('test','test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}), + ); + + res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => helper.nft.mintCollection( + donor, + {name: 'test', description: 'test', tokenPrefix: 'test'}, + ), + ))); + + res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + async () => { + zeppelelinContract = await helper.ethContract.deployByAbi( + ethSigner, + ZEPPELIN_ABI, + ZEPPELIN_OBJECT, + ); + }, + ); + + res['mint'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.mint(ethSigner).send(), + ); + + res['mint'].zeppelin = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => zeppelelinContract!.methods.safeMint(ethSigner, '').send({from: ethSigner}), + ); + + res['mintCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.mintCross(crossSigner, []).send(), + ); + + res['mint'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.mintToken( + donor, + {Substrate: donor.address}, + ), + ))); + + res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.mintMultipleTokens(donor, [{ + owner: {Substrate: donor.address}, + }]), + ))); + + res['mintWithTokenURI'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.mintWithTokenURI(ethSigner, 'Test URI').send(), + ); + + res['mintWithTokenURI'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.mintToken( + donor, + {Ethereum: ethSigner}, + [{key: 'URI', value: 'Test URI'}], + ), + ))); + + res['mintWithTokenURI'].zeppelin = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => zeppelelinContract!.methods.safeMint(ethSigner, 'Test URI').send({from: ethSigner}), + ); + + res['setProperties'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setProperties(1, PROPERTIES.slice(0,1)).send(), + ); + + res['deleteProperties'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.deleteProperties(1, [PROPERTIES[0].key]).send(), + ); + + res['setProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setTokenProperties( + donor, + 1, + SUBS_PROPERTIES.slice(0, 1), + ), + ))); + + res['deleteProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.deleteTokenProperties( + donor, + 1, + SUBS_PROPERTIES.slice(0, 1) + .map(p => p.key), + ), + ))); + + res['transfer'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.transfer(ethReceiver, 1).send(), + ); + + res['safeTransferFrom*'] = { + zeppelin: + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => zeppelelinContract!.methods.safeTransferFrom(ethSigner, ethReceiver, 0).send({from: ethSigner}), + ), + }; + + res['transferCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethReceiver}, + () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}), + ); + + res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.transferToken( + donor, + 3, + {Substrate: subReceiver.address}, + ), + ))); + await collection.approveToken(subReceiver, 3, {Substrate: donor.address}); + + res['transferFrom*'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(), + ); + + res['transferFrom*'].zeppelin = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethReceiver}, + () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 0).send({from: ethReceiver}), + ); + + + res['transferFromCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethReceiver}, + () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}), + ); + + res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.transferTokenFrom(donor, 3, {Substrate: subReceiver.address}, {Substrate: donor.address}), + ))); + + res['burn'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.burn(1).send(), + ); + + res['burn'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.burnToken(donor, 3), + ))); + + res['approve*'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.approve(ethReceiver, 2).send(), + ); + + res['approve*'].zeppelin = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => zeppelelinContract!.methods.approve(ethReceiver, 0).send({from: ethSigner}), + ); + + res['approveCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.approveCross(crossReceiver, 2).send(), + ); + + res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.approveToken(donor, 4, {Substrate: subReceiver.address}), + ))); + + res['setApprovalForAll*'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setApprovalForAll(ethReceiver, true).send(), + ); + + res['setApprovalForAll*'].zeppelin = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => zeppelelinContract!.methods.setApprovalForAll(ethReceiver, true).send({from: ethSigner}), + ); + + res['setApprovalForAll*'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => helper.nft.setAllowanceForAll(donor, collection.collectionId, {Substrate: subReceiver.address}, true), + ))); + + res['burnFromCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethReceiver}, + () => evmContract.methods.burnFromCross(crossSigner, 2).send({from:ethReceiver}), + ); + + res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: subReceiver.address}, + () => collection.burnTokenFrom(subReceiver, 4, {Substrate: donor.address}), + ))); + + res['setTokenPropertyPermissions'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setTokenPropertyPermissions([ + ['url', [ + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], + ], + ]).send(), + ); + + res['setTokenPropertyPermissions'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setTokenPropertyPermissions(donor, [{key: 'url', permission: { + tokenOwner: true, + collectionAdmin: true, + mutable: true, + }}]), + ))); + + res['setCollectionSponsorCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionSponsorCross(crossReceiver).send(), + ); + + res['confirmCollectionSponsorship'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethReceiver}, + () => evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}), + ); + + res['removeCollectionSponsor'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.removeCollectionSponsor().send(), + ); + + res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setSponsor(donor, subReceiver.address), + ))); + + res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: subReceiver.address}, + () => collection.confirmSponsorship(subReceiver), + ))); + + res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.removeSponsor(donor), + ))); + + res['setCollectionProperties'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(), + ); + + res['deleteCollectionProperties'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(), + ); + res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setProperties(donor, PROPERTIES.slice(0, 1) + .map(p => ({key: p.key, value: p.value.toString()}))), + ))); + + res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1) + .map(p => p.key)), + ))); + + res['setCollectionLimit'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(), + ); + + res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}), + ))); + + const {collectionAddress} = await helper.eth.createCollection(ethSigner, new CreateCollectionData('A', 'B', 'C', 'nft')).send(); + const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true); + + + res['addCollectionAdminCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(), + ); + + res['removeCollectionAdminCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(), + ); + + res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.addAdmin(donor, {Ethereum: ethReceiver}), + ))); + + res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.removeAdmin(donor, {Ethereum: ethReceiver}), + ))); + + res['setCollectionNesting'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionNesting(true).send(), + ); + + res['setCollectionNesting[]'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(), + ); + + res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.disableNesting(donor), + ))); + + res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setPermissions( + donor, + { + nesting: { + tokenOwner: true, + restricted: [collection.collectionId], + }, + }, + ), + ))); + + res['setCollectionAccess'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionAccess(1).send(), + ); + + res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setPermissions(donor, {access: 'AllowList'}), + ))); + + res['addToCollectionAllowListCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(), + ); + + res['removeFromCollectionAllowListCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(), + ); + + res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.addToAllowList(donor, {Ethereum: ethReceiver}), + ))); + + res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}), + ))); + + res['setCollectionMintMode'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionMintMode(true).send(), + ); + + res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setPermissions(donor, {mintMode: false}), + ))); + + res['changeCollectionOwnerCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(), + ); + + res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.changeOwner(donor, subReceiver.address), + ))); + + return res; +} + +async function erc20CalculateFeeGas( + helper: EthUniqueHelper, + privateKey: (seed: string) => Promise, + +): Promise { + const res: IFunctionFee = {}; + const donor = await privateKey('//Alice'); + const [subReceiver] = await helper.arrange.createAccounts([10n], donor); + const ethSigner = await helper.eth.createAccountWithBalance(donor); + const ethReceiver = await helper.eth.createAccountWithBalance(donor); + const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner); + const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver); + const collection = (await createCollectionForBenchmarks( + 'ft', + helper, + privateKey, + ethSigner, + null, + PERMISSIONS, + )) as UniqueFTCollection; + + const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner); + let zeppelelinContract: Contract | null = null; + const ZEPPELIN_OBJECT = '0x' + (await readFile(`${dirname}/../utils/openZeppelin/ERC20/bin/ZeppelinContract.bin`)).toString(); + const ZEPPELIN_ABI = JSON.parse((await readFile(`${dirname}/../utils/openZeppelin/ERC20/bin/ZeppelinContract.abi`)).toString()); + + const evmContract = await helper.ethNativeContract.collection( + helper.ethAddress.fromCollectionId(collection.collectionId), + 'ft', + ethSigner, + true, + ); + + res['createCollection'] = await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => helperContract.methods.createFTCollection('test', 18,'test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}), + ); + + res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => helper.ft.mintCollection( + donor, + {name: 'test', description: 'test', tokenPrefix: 'test'}, + 18, + ), + ))); + + res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + async () => { + zeppelelinContract = await helper.ethContract.deployByAbi( + ethSigner, + ZEPPELIN_ABI, + ZEPPELIN_OBJECT, + ); + }, + ); + + res['mint'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.mint(ethSigner, 1).send(), + ); + + res['mint'].zeppelin = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => zeppelelinContract!.methods.mint(ethSigner, 1).send(), + ); + + res['mintCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.mintCross(crossSigner, 1).send(), + ); + + res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.mint( + donor, + 10n, + {Substrate: donor.address}, + ), + ))); + + res['mintBulk'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.mintBulk([{to: ethSigner, amount: 1}]).send(), + ); + + res['mintBulk'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => helper.executeExtrinsic(donor, 'api.tx.unique.createMultipleItemsEx',[collection.collectionId, { + Fungible: new Map([ + [JSON.stringify({Ethereum: ethSigner}), 1], + ]), + }], true), + ))); + + res['transfer*'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.transfer(ethReceiver, 1).send(), + ); + + res['transfer*'].zeppelin = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => zeppelelinContract!.methods.transfer(ethReceiver, 1).send(), + ); + + res['transferCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethReceiver}, + () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}), + ); + + res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.transfer( + donor, + {Substrate: subReceiver.address}, + 1n, + ), + ))); + await collection.approveTokens(subReceiver, {Substrate: donor.address}, 1n); + + res['transferFrom*'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(), + ); + + await zeppelelinContract!.methods.approve(ethSigner, 10).send({from: ethReceiver}); + + res['transferFrom*'].zeppelin = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 1).send({from: ethSigner}), + ); + + res['transferFromCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethReceiver}, + () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}), + ); + + res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.transferFrom(donor, {Substrate: subReceiver.address}, {Substrate: donor.address}, 1n), + ))); + + + res['burnTokens'] = {fee: 0n, gas: 0n}; + res['burnTokens'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.burnTokens(donor, 1n), + ))); + + + res['approve*'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.approve(ethReceiver, 2).send(), + ); + + res['approve*'].zeppelin = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => zeppelelinContract!.methods.approve(ethReceiver, 10).send(), + ); + + res['approveCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.approveCross(crossReceiver, 2).send(), + ); + + res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.approveTokens(donor, {Substrate: subReceiver.address}, 1n), + ))); + + res['burnFromCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethReceiver}, + () => evmContract.methods.burnFromCross(crossSigner, 1).send({from:ethReceiver}), + ); + + res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: subReceiver.address}, + () => collection.burnTokensFrom(subReceiver, {Substrate: donor.address}, 1n), + ))); + + res['setCollectionSponsorCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionSponsorCross(crossReceiver).send(), + ); + + res['confirmCollectionSponsorship'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethReceiver}, + () => evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}), + ); + + res['removeCollectionSponsor'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.removeCollectionSponsor().send(), + ); + + res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setSponsor(donor, subReceiver.address), + ))); + + res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: subReceiver.address}, + () => collection.confirmSponsorship(subReceiver), + ))); + + res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.removeSponsor(donor), + ))); + + res['setCollectionProperties'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(), + ); + + res['deleteCollectionProperties'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(), + ); + res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setProperties(donor, PROPERTIES.slice(0, 1) + .map(p => ({key: p.key, value: p.value.toString()}))), + ))); + + res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1) + .map(p => p.key)), + ))); + + res['setCollectionLimit'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(), + ); + + res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}), + ))); + + const {collectionAddress} = await helper.eth.createCollection(ethSigner, new CreateCollectionData('A', 'B', 'C', 'nft')).send(); + const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true); + + + res['addCollectionAdminCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(), + ); + + res['removeCollectionAdminCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(), + ); + + res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.addAdmin(donor, {Ethereum: ethReceiver}), + ))); + + res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.removeAdmin(donor, {Ethereum: ethReceiver}), + ))); + + res['setCollectionNesting'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionNesting(true).send(), + ); + + res['setCollectionNesting[]'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(), + ); + + res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.disableNesting(donor), + ))); + + res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setPermissions( + donor, + { + nesting: { + tokenOwner: true, + restricted: [collection.collectionId], + }, + }, + ), + ))); + + res['setCollectionAccess'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionAccess(1).send(), + ); + + res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setPermissions(donor, {access: 'AllowList'}), + ))); + + res['addToCollectionAllowListCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(), + ); + + res['removeFromCollectionAllowListCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(), + ); + + res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.addToAllowList(donor, {Ethereum: ethReceiver}), + ))); + + res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}), + ))); + + res['setCollectionMintMode'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => evmContract.methods.setCollectionMintMode(true).send(), + ); + + res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.setPermissions(donor, {mintMode: false}), + ))); + + res['changeCollectionOwnerCross'] = + await helper.arrange.calculcateFeeGas( + {Ethereum: ethSigner}, + () => collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(), + ); + + res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( + {Substrate: donor.address}, + () => collection.changeOwner(donor, subReceiver.address), + ))); + + return res; +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/common.ts @@ -0,0 +1,88 @@ +import {EthUniqueHelper} from '@unique/tests/eth/util/playgrounds/unique.dev.js'; +import {UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/unique.js'; +import type {ITokenPropertyPermission, TCollectionMode} from '@unique/playgrounds/types.js'; +import type {IKeyringPair} from '@polkadot/types/types'; + +export const PROPERTIES = Array(40) + .fill(0) + .map((_, i) => ({ + key: `key_${i}`, + value: Uint8Array.from(Buffer.from(`value_${i}`)), + })); + +export const SUBS_PROPERTIES = Array(40) + .fill(0) + .map((_, i) => ({ + key: `key_${i}`, + value: `value_${i}`, + })); + +export const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => ({ + key: p.key, + permission: { + tokenOwner: true, + collectionAdmin: true, + mutable: true, + }, +})); + +export function convertToTokens(value: bigint, nominal = 1000_000_000_000_000_000n): number { + return Number((value * 1000n) / nominal) / 1000; +} + +export async function createCollectionForBenchmarks( + mode : TCollectionMode, + helper: EthUniqueHelper, + privateKey: (seed: string) => Promise, + ethSigner: string, + proxyContract: string | null, + permissions: ITokenPropertyPermission[], +) { + const donor = await privateKey('//Alice'); + + const collection = await helper[mode].mintCollection(donor, { + name: 'test mintToSubstrate', + description: 'EVMHelpers', + tokenPrefix: mode, + ...(mode != 'ft' ? { + tokenPropertyPermissions: [ + { + key: 'url', + permission: { + tokenOwner: true, + collectionAdmin: true, + mutable: true, + }, + }, + { + key: 'URI', + permission: { + tokenOwner: true, + collectionAdmin: true, + mutable: true, + }, + }, + ], + } : {}), + limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0}, + permissions: {mintMode: true}, + }); + + await collection.addToAllowList(donor, { + Ethereum: helper.address.substrateToEth(donor.address), + }); + await collection.addToAllowList(donor, {Substrate: donor.address}); + await collection.addAdmin(donor, {Ethereum: ethSigner}); + await collection.addAdmin(donor, { + Ethereum: helper.address.substrateToEth(donor.address), + }); + + if(proxyContract) { + await collection.addToAllowList(donor, {Ethereum: proxyContract}); + await collection.addAdmin(donor, {Ethereum: proxyContract}); + } + if(collection instanceof UniqueNFTCollection || collection instanceof UniqueRFTCollection) + await collection.setTokenPropertyPermissions(donor, permissions); + + return collection; +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/README.md @@ -0,0 +1,12 @@ +# OpenZeppelin Contracts + +The files in this directory were sourced unmodified from OpenZeppelin Contracts v4.8.1. + +They are not meant to be edited. + +The originals can be found on [GitHub] and [npm]. + +[GitHub]: https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.8.1 +[npm]: https://www.npmjs.com/package/@openzeppelin/contracts/v/4.8.1 + +Generated with OpenZeppelin Contracts Wizard (https://zpl.in/wizard). --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/access/Ownable.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * By default, the owner account will be the one that deploys the contract. This + * can later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +abstract contract Ownable is Context { + address private _owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor() { + _transferOwnership(_msgSender()); + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + _checkOwner(); + _; + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + return _owner; + } + + /** + * @dev Throws if the sender is not the owner. + */ + function _checkOwner() internal view virtual { + require(owner() == _msgSender(), "Ownable: caller is not the owner"); + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions anymore. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby removing any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + _transferOwnership(address(0)); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual { + address oldOwner = _owner; + _owner = newOwner; + emit OwnershipTransferred(oldOwner, newOwner); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/governance/utils/IVotes.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol) +pragma solidity ^0.8.0; + +/** + * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts. + * + * _Available since v4.5._ + */ +interface IVotes { + /** + * @dev Emitted when an account changes their delegate. + */ + event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); + + /** + * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes. + */ + event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); + + /** + * @dev Returns the current amount of votes that `account` has. + */ + function getVotes(address account) external view returns (uint256); + + /** + * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`). + */ + function getPastVotes(address account, uint256 blockNumber) external view returns (uint256); + + /** + * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`). + * + * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. + * Votes that have not been delegated are still part of total supply, even though they would not participate in a + * vote. + */ + function getPastTotalSupply(uint256 blockNumber) external view returns (uint256); + + /** + * @dev Returns the delegate that `account` has chosen. + */ + function delegates(address account) external view returns (address); + + /** + * @dev Delegates votes from the sender to `delegatee`. + */ + function delegate(address delegatee) external; + + /** + * @dev Delegates votes from signer to `delegatee`. + */ + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external; +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC3156 FlashBorrower, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * _Available since v4.1._ + */ +interface IERC3156FlashBorrower { + /** + * @dev Receive a flash loan. + * @param initiator The initiator of the loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param fee The additional amount of tokens to repay. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + * @return The keccak256 hash of "IERC3156FlashBorrower.onFlashLoan" + */ + function onFlashLoan( + address initiator, + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) external returns (bytes32); +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashLender.sol) + +pragma solidity ^0.8.0; + +import "./IERC3156FlashBorrower.sol"; + +/** + * @dev Interface of the ERC3156 FlashLender, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * _Available since v4.1._ + */ +interface IERC3156FlashLender { + /** + * @dev The amount of currency available to be lended. + * @param token The loan currency. + * @return The amount of `token` that can be borrowed. + */ + function maxFlashLoan(address token) external view returns (uint256); + + /** + * @dev The fee to be charged for a given loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @return The amount of `token` to be charged for the loan, on top of the returned principal. + */ + function flashFee(address token, uint256 amount) external view returns (uint256); + + /** + * @dev Initiate a flash loan. + * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + */ + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data + ) external returns (bool); +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/security/Pausable.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +/** + * @dev Contract module which allows children to implement an emergency stop + * mechanism that can be triggered by an authorized account. + * + * This module is used through inheritance. It will make available the + * modifiers `whenNotPaused` and `whenPaused`, which can be applied to + * the functions of your contract. Note that they will not be pausable by + * simply including this module, only once the modifiers are put in place. + */ +abstract contract Pausable is Context { + /** + * @dev Emitted when the pause is triggered by `account`. + */ + event Paused(address account); + + /** + * @dev Emitted when the pause is lifted by `account`. + */ + event Unpaused(address account); + + bool private _paused; + + /** + * @dev Initializes the contract in unpaused state. + */ + constructor() { + _paused = false; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + _requireNotPaused(); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + _requirePaused(); + _; + } + + /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Throws if the contract is paused. + */ + function _requireNotPaused() internal view virtual { + require(!paused(), "Pausable: paused"); + } + + /** + * @dev Throws if the contract is not paused. + */ + function _requirePaused() internal view virtual { + require(paused(), "Pausable: not paused"); + } + + /** + * @dev Triggers stopped state. + * + * Requirements: + * + * - The contract must not be paused. + */ + function _pause() internal virtual whenNotPaused { + _paused = true; + emit Paused(_msgSender()); + } + + /** + * @dev Returns to normal state. + * + * Requirements: + * + * - The contract must be paused. + */ + function _unpause() internal virtual whenPaused { + _paused = false; + emit Unpaused(_msgSender()); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/ERC20.sol @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol) + +pragma solidity ^0.8.0; + +import "./IERC20.sol"; +import "./extensions/IERC20Metadata.sol"; +import "../../utils/Context.sol"; + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * We have followed general OpenZeppelin Contracts guidelines: functions revert + * instead returning `false` on failure. This behavior is nonetheless + * conventional and does not conflict with the expectations of ERC20 + * applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20 is Context, IERC20, IERC20Metadata { + mapping(address => uint256) private _balances; + + mapping(address => mapping(address => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + /** + * @dev Sets the values for {name} and {symbol}. + * + * The default value of {decimals} is 18. To select a different value for + * {decimals} you should overload it. + * + * All two of these values are immutable: they can only be set once during + * construction. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC20} uses, unless this function is + * overridden; + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual override returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address to, uint256 amount) public virtual override returns (bool) { + address owner = _msgSender(); + _transfer(owner, to, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Requirements: + * + * - `from` and `to` cannot be the zero address. + * - `from` must have a balance of at least `amount`. + * - the caller must have allowance for ``from``'s tokens of at least + * `amount`. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) public virtual override returns (bool) { + address spender = _msgSender(); + _spendAllowance(from, spender, amount); + _transfer(from, to, amount); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, allowance(owner, spender) + addedValue); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + address owner = _msgSender(); + uint256 currentAllowance = allowance(owner, spender); + require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); + unchecked { + _approve(owner, spender, currentAllowance - subtractedValue); + } + + return true; + } + + /** + * @dev Moves `amount` of tokens from `from` to `to`. + * + * This internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `from` must have a balance of at least `amount`. + */ + function _transfer( + address from, + address to, + uint256 amount + ) internal virtual { + require(from != address(0), "ERC20: transfer from the zero address"); + require(to != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(from, to, amount); + + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by + // decrementing then incrementing. + _balances[to] += amount; + } + + emit Transfer(from, to, amount); + + _afterTokenTransfer(from, to, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply += amount; + unchecked { + // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. + _balances[account] += amount; + } + emit Transfer(address(0), account, amount); + + _afterTokenTransfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + uint256 accountBalance = _balances[account]; + require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); + unchecked { + _balances[account] = accountBalance - amount; + // Overflow not possible: amount <= accountBalance <= totalSupply. + _totalSupply -= amount; + } + + emit Transfer(account, address(0), amount); + + _afterTokenTransfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve( + address owner, + address spender, + uint256 amount + ) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `amount`. + * + * Does not update the allowance amount in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Might emit an {Approval} event. + */ + function _spendAllowance( + address owner, + address spender, + uint256 amount + ) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + require(currentAllowance >= amount, "ERC20: insufficient allowance"); + unchecked { + _approve(owner, spender, currentAllowance - amount); + } + } + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual {} + + /** + * @dev Hook that is called after any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * has been transferred to `to`. + * - when `from` is zero, `amount` tokens have been minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens have been burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual {} +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/IERC20.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../../../utils/Context.sol"; + +/** + * @dev Extension of {ERC20} that allows token holders to destroy both their own + * tokens and those that they have an allowance for, in a way that can be + * recognized off-chain (via event analysis). + */ +abstract contract ERC20Burnable is Context, ERC20 { + /** + * @dev Destroys `amount` tokens from the caller. + * + * See {ERC20-_burn}. + */ + function burn(uint256 amount) public virtual { + _burn(_msgSender(), amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, deducting from the caller's + * allowance. + * + * See {ERC20-_burn} and {ERC20-allowance}. + * + * Requirements: + * + * - the caller must have allowance for ``accounts``'s tokens of at least + * `amount`. + */ + function burnFrom(address account, uint256 amount) public virtual { + _spendAllowance(account, _msgSender(), amount); + _burn(account, amount); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC20FlashMint.sol) + +pragma solidity ^0.8.0; + +import "../../../interfaces/IERC3156FlashBorrower.sol"; +import "../../../interfaces/IERC3156FlashLender.sol"; +import "../ERC20.sol"; + +/** + * @dev Implementation of the ERC3156 Flash loans extension, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * Adds the {flashLoan} method, which provides flash loan support at the token + * level. By default there is no fee, but this can be changed by overriding {flashFee}. + * + * _Available since v4.1._ + */ +abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { + bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); + + /** + * @dev Returns the maximum amount of tokens available for loan. + * @param token The address of the token that is requested. + * @return The amount of token that can be loaned. + */ + function maxFlashLoan(address token) public view virtual override returns (uint256) { + return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0; + } + + /** + * @dev Returns the fee applied when doing flash loans. This function calls + * the {_flashFee} function which returns the fee applied when doing flash + * loans. + * @param token The token to be flash loaned. + * @param amount The amount of tokens to be loaned. + * @return The fees applied to the corresponding flash loan. + */ + function flashFee(address token, uint256 amount) public view virtual override returns (uint256) { + require(token == address(this), "ERC20FlashMint: wrong token"); + return _flashFee(token, amount); + } + + /** + * @dev Returns the fee applied when doing flash loans. By default this + * implementation has 0 fees. This function can be overloaded to make + * the flash loan mechanism deflationary. + * @param token The token to be flash loaned. + * @param amount The amount of tokens to be loaned. + * @return The fees applied to the corresponding flash loan. + */ + function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) { + // silence warning about unused variable without the addition of bytecode. + token; + amount; + return 0; + } + + /** + * @dev Returns the receiver address of the flash fee. By default this + * implementation returns the address(0) which means the fee amount will be burnt. + * This function can be overloaded to change the fee receiver. + * @return The address for which the flash fee will be sent to. + */ + function _flashFeeReceiver() internal view virtual returns (address) { + return address(0); + } + + /** + * @dev Performs a flash loan. New tokens are minted and sent to the + * `receiver`, who is required to implement the {IERC3156FlashBorrower} + * interface. By the end of the flash loan, the receiver is expected to own + * amount + fee tokens and have them approved back to the token contract itself so + * they can be burned. + * @param receiver The receiver of the flash loan. Should implement the + * {IERC3156FlashBorrower-onFlashLoan} interface. + * @param token The token to be flash loaned. Only `address(this)` is + * supported. + * @param amount The amount of tokens to be loaned. + * @param data An arbitrary datafield that is passed to the receiver. + * @return `true` if the flash loan was successful. + */ + // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount + // minted at the beginning is always recovered and burned at the end, or else the entire function will revert. + // slither-disable-next-line reentrancy-no-eth + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data + ) public virtual override returns (bool) { + require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan"); + uint256 fee = flashFee(token, amount); + _mint(address(receiver), amount); + require( + receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, + "ERC20FlashMint: invalid return value" + ); + address flashFeeReceiver = _flashFeeReceiver(); + _spendAllowance(address(receiver), address(this), amount + fee); + if (fee == 0 || flashFeeReceiver == address(0)) { + _burn(address(receiver), amount + fee); + } else { + _burn(address(receiver), amount); + _transfer(address(receiver), flashFeeReceiver, fee); + } + return true; + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.1) (token/ERC20/extensions/ERC20Votes.sol) + +pragma solidity ^0.8.0; + +import "./draft-ERC20Permit.sol"; +import "../../../utils/math/Math.sol"; +import "../../../governance/utils/IVotes.sol"; +import "../../../utils/math/SafeCast.sol"; +import "../../../utils/cryptography/ECDSA.sol"; + +/** + * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's, + * and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1. + * + * NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module. + * + * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either + * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting + * power can be queried through the public accessors {getVotes} and {getPastVotes}. + * + * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it + * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. + * + * _Available since v4.2._ + */ +abstract contract ERC20Votes is IVotes, ERC20Permit { + struct Checkpoint { + uint32 fromBlock; + uint224 votes; + } + + bytes32 private constant _DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + + mapping(address => address) private _delegates; + mapping(address => Checkpoint[]) private _checkpoints; + Checkpoint[] private _totalSupplyCheckpoints; + + /** + * @dev Get the `pos`-th checkpoint for `account`. + */ + function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) { + return _checkpoints[account][pos]; + } + + /** + * @dev Get number of checkpoints for `account`. + */ + function numCheckpoints(address account) public view virtual returns (uint32) { + return SafeCast.toUint32(_checkpoints[account].length); + } + + /** + * @dev Get the address `account` is currently delegating to. + */ + function delegates(address account) public view virtual override returns (address) { + return _delegates[account]; + } + + /** + * @dev Gets the current votes balance for `account` + */ + function getVotes(address account) public view virtual override returns (uint256) { + uint256 pos = _checkpoints[account].length; + return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; + } + + /** + * @dev Retrieve the number of votes for `account` at the end of `blockNumber`. + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_checkpoints[account], blockNumber); + } + + /** + * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances. + * It is but NOT the sum of all the delegated votes! + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); + } + + /** + * @dev Lookup a value in a list of (sorted) checkpoints. + */ + function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) { + // We run a binary search to look for the earliest checkpoint taken after `blockNumber`. + // + // Initially we check if the block is recent to narrow the search range. + // During the loop, the index of the wanted checkpoint remains in the range [low-1, high). + // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant. + // - If the middle checkpoint is after `blockNumber`, we look in [low, mid) + // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high) + // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not + // out of bounds (in which case we're looking too far in the past and the result is 0). + // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is + // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out + // the same. + uint256 length = ckpts.length; + + uint256 low = 0; + uint256 high = length; + + if (length > 5) { + uint256 mid = length - Math.sqrt(length); + if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes; + } + + /** + * @dev Delegate votes from the sender to `delegatee`. + */ + function delegate(address delegatee) public virtual override { + _delegate(_msgSender(), delegatee); + } + + /** + * @dev Delegates votes from signer to `delegatee` + */ + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { + require(block.timestamp <= expiry, "ERC20Votes: signature expired"); + address signer = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))), + v, + r, + s + ); + require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce"); + _delegate(signer, delegatee); + } + + /** + * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). + */ + function _maxSupply() internal view virtual returns (uint224) { + return type(uint224).max; + } + + /** + * @dev Snapshots the totalSupply after it has been increased. + */ + function _mint(address account, uint256 amount) internal virtual override { + super._mint(account, amount); + require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); + + _writeCheckpoint(_totalSupplyCheckpoints, _add, amount); + } + + /** + * @dev Snapshots the totalSupply after it has been decreased. + */ + function _burn(address account, uint256 amount) internal virtual override { + super._burn(account, amount); + + _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); + } + + /** + * @dev Move voting power when tokens are transferred. + * + * Emits a {IVotes-DelegateVotesChanged} event. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override { + super._afterTokenTransfer(from, to, amount); + + _moveVotingPower(delegates(from), delegates(to), amount); + } + + /** + * @dev Change delegation for `delegator` to `delegatee`. + * + * Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}. + */ + function _delegate(address delegator, address delegatee) internal virtual { + address currentDelegate = delegates(delegator); + uint256 delegatorBalance = balanceOf(delegator); + _delegates[delegator] = delegatee; + + emit DelegateChanged(delegator, currentDelegate, delegatee); + + _moveVotingPower(currentDelegate, delegatee, delegatorBalance); + } + + function _moveVotingPower( + address src, + address dst, + uint256 amount + ) private { + if (src != dst && amount > 0) { + if (src != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); + emit DelegateVotesChanged(src, oldWeight, newWeight); + } + + if (dst != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); + emit DelegateVotesChanged(dst, oldWeight, newWeight); + } + } + } + + function _writeCheckpoint( + Checkpoint[] storage ckpts, + function(uint256, uint256) view returns (uint256) op, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight) { + uint256 pos = ckpts.length; + + Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0) : _unsafeAccess(ckpts, pos - 1); + + oldWeight = oldCkpt.votes; + newWeight = op(oldWeight, delta); + + if (pos > 0 && oldCkpt.fromBlock == block.number) { + _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight); + } else { + ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); + } + } + + function _add(uint256 a, uint256 b) private pure returns (uint256) { + return a + b; + } + + function _subtract(uint256 a, uint256 b) private pure returns (uint256) { + return a - b; + } + + /** + * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + */ + function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) { + assembly { + mstore(0, ckpts.slot) + result.slot := add(keccak256(0, 0x20), pos) + } + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) + +pragma solidity ^0.8.0; + +import "../IERC20.sol"; + +/** + * @dev Interface for the optional metadata functions from the ERC20 standard. + * + * _Available since v4.1._ + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol) + +pragma solidity ^0.8.0; + +import "./draft-IERC20Permit.sol"; +import "../ERC20.sol"; +import "../../../utils/cryptography/ECDSA.sol"; +import "../../../utils/cryptography/EIP712.sol"; +import "../../../utils/Counters.sol"; + +/** + * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + * + * _Available since v3.4._ + */ +abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { + using Counters for Counters.Counter; + + mapping(address => Counters.Counter) private _nonces; + + // solhint-disable-next-line var-name-mixedcase + bytes32 private constant _PERMIT_TYPEHASH = + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + /** + * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`. + * However, to ensure consistency with the upgradeable transpiler, we will continue + * to reserve a slot. + * @custom:oz-renamed-from _PERMIT_TYPEHASH + */ + // solhint-disable-next-line var-name-mixedcase + bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT; + + /** + * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. + * + * It's a good idea to use the same `name` that is defined as the ERC20 token name. + */ + constructor(string memory name) EIP712(name, "1") {} + + /** + * @dev See {IERC20Permit-permit}. + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { + require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); + + bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)); + + bytes32 hash = _hashTypedDataV4(structHash); + + address signer = ECDSA.recover(hash, v, r, s); + require(signer == owner, "ERC20Permit: invalid signature"); + + _approve(owner, spender, value); + } + + /** + * @dev See {IERC20Permit-nonces}. + */ + function nonces(address owner) public view virtual override returns (uint256) { + return _nonces[owner].current(); + } + + /** + * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view override returns (bytes32) { + return _domainSeparatorV4(); + } + + /** + * @dev "Consume a nonce": return the current value and increment. + * + * _Available since v4.1._ + */ + function _useNonce(address owner) internal virtual returns (uint256 current) { + Counters.Counter storage nonce = _nonces[owner]; + current = nonce.current(); + nonce.increment(); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + */ +interface IERC20Permit { + /** + * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, + * given ``owner``'s signed approval. + * + * IMPORTANT: The same issues {IERC20-approve} has related to transaction + * ordering also apply here. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `deadline` must be a timestamp in the future. + * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` + * over the EIP712-formatted function arguments. + * - the signature must use ``owner``'s current nonce (see {nonces}). + * + * For more information on the signature format, see the + * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP + * section]. + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + /** + * @dev Returns the current nonce for `owner`. This value must be + * included whenever a signature is generated for {permit}. + * + * Every successful call to {permit} increases ``owner``'s nonce by one. This + * prevents a signature from being used multiple times. + */ + function nonces(address owner) external view returns (uint256); + + /** + * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32); +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Context.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Counters.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) + +pragma solidity ^0.8.0; + +/** + * @title Counters + * @author Matt Condon (@shrugs) + * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number + * of elements in a mapping, issuing ERC721 ids, or counting request ids. + * + * Include with `using Counters for Counters.Counter;` + */ +library Counters { + struct Counter { + // This variable should never be directly accessed by users of the library: interactions must be restricted to + // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add + // this feature: see https://github.com/ethereum/solidity/issues/4637 + uint256 _value; // default: 0 + } + + function current(Counter storage counter) internal view returns (uint256) { + return counter._value; + } + + function increment(Counter storage counter) internal { + unchecked { + counter._value += 1; + } + } + + function decrement(Counter storage counter) internal { + uint256 value = counter._value; + require(value > 0, "Counter: decrement overflow"); + unchecked { + counter._value = value - 1; + } + } + + function reset(Counter storage counter) internal { + counter._value = 0; + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Strings.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) + +pragma solidity ^0.8.0; + +import "./math/Math.sol"; + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant _SYMBOLS = "0123456789abcdef"; + uint8 private constant _ADDRESS_LENGTH = 20; + + /** + * @dev Converts a `uint256` to its ASCII `string` decimal representation. + */ + function toString(uint256 value) internal pure returns (string memory) { + unchecked { + uint256 length = Math.log10(value) + 1; + string memory buffer = new string(length); + uint256 ptr; + /// @solidity memory-safe-assembly + assembly { + ptr := add(buffer, add(32, length)) + } + while (true) { + ptr--; + /// @solidity memory-safe-assembly + assembly { + mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) + } + value /= 10; + if (value == 0) break; + } + return buffer; + } + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. + */ + function toHexString(uint256 value) internal pure returns (string memory) { + unchecked { + return toHexString(value, Math.log256(value) + 1); + } + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + */ + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = _SYMBOLS[value & 0xf]; + value >>= 4; + } + require(value == 0, "Strings: hex length insufficient"); + return string(buffer); + } + + /** + * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. + */ + function toHexString(address addr) internal pure returns (string memory) { + return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/ECDSA.sol @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol) + +pragma solidity ^0.8.0; + +import "../Strings.sol"; + +/** + * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. + * + * These functions can be used to verify that a message was signed by the holder + * of the private keys of a given address. + */ +library ECDSA { + enum RecoverError { + NoError, + InvalidSignature, + InvalidSignatureLength, + InvalidSignatureS, + InvalidSignatureV // Deprecated in v4.8 + } + + function _throwError(RecoverError error) private pure { + if (error == RecoverError.NoError) { + return; // no error: do nothing + } else if (error == RecoverError.InvalidSignature) { + revert("ECDSA: invalid signature"); + } else if (error == RecoverError.InvalidSignatureLength) { + revert("ECDSA: invalid signature length"); + } else if (error == RecoverError.InvalidSignatureS) { + revert("ECDSA: invalid signature 's' value"); + } + } + + /** + * @dev Returns the address that signed a hashed message (`hash`) with + * `signature` or error string. This address can then be used for verification purposes. + * + * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {toEthSignedMessageHash} on it. + * + * Documentation for signature generation: + * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] + * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] + * + * _Available since v4.3._ + */ + function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { + if (signature.length == 65) { + bytes32 r; + bytes32 s; + uint8 v; + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + /// @solidity memory-safe-assembly + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + return tryRecover(hash, v, r, s); + } else { + return (address(0), RecoverError.InvalidSignatureLength); + } + } + + /** + * @dev Returns the address that signed a hashed message (`hash`) with + * `signature`. This address can then be used for verification purposes. + * + * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {toEthSignedMessageHash} on it. + */ + function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, signature); + _throwError(error); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. + * + * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] + * + * _Available since v4.3._ + */ + function tryRecover( + bytes32 hash, + bytes32 r, + bytes32 vs + ) internal pure returns (address, RecoverError) { + bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); + uint8 v = uint8((uint256(vs) >> 255) + 27); + return tryRecover(hash, v, r, s); + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. + * + * _Available since v4.2._ + */ + function recover( + bytes32 hash, + bytes32 r, + bytes32 vs + ) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, r, vs); + _throwError(error); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `v`, + * `r` and `s` signature fields separately. + * + * _Available since v4.3._ + */ + function tryRecover( + bytes32 hash, + uint8 v, + bytes32 r, + bytes32 s + ) internal pure returns (address, RecoverError) { + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { + return (address(0), RecoverError.InvalidSignatureS); + } + + // If the signature is valid (and not malleable), return the signer address + address signer = ecrecover(hash, v, r, s); + if (signer == address(0)) { + return (address(0), RecoverError.InvalidSignature); + } + + return (signer, RecoverError.NoError); + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `v`, + * `r` and `s` signature fields separately. + */ + function recover( + bytes32 hash, + uint8 v, + bytes32 r, + bytes32 s + ) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, v, r, s); + _throwError(error); + return recovered; + } + + /** + * @dev Returns an Ethereum Signed Message, created from a `hash`. This + * produces hash corresponding to the one signed with the + * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] + * JSON-RPC method as part of EIP-191. + * + * See {recover}. + */ + function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { + // 32 is the length in bytes of hash, + // enforced by the type signature above + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); + } + + /** + * @dev Returns an Ethereum Signed Message, created from `s`. This + * produces hash corresponding to the one signed with the + * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] + * JSON-RPC method as part of EIP-191. + * + * See {recover}. + */ + function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); + } + + /** + * @dev Returns an Ethereum Signed Typed Data, created from a + * `domainSeparator` and a `structHash`. This produces hash corresponding + * to the one signed with the + * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] + * JSON-RPC method as part of EIP-712. + * + * See {recover}. + */ + function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/EIP712.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol) + +pragma solidity ^0.8.0; + +import "./ECDSA.sol"; + +/** + * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. + * + * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, + * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding + * they need in their contracts using a combination of `abi.encode` and `keccak256`. + * + * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding + * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA + * ({_hashTypedDataV4}). + * + * The implementation of the domain separator was designed to be as efficient as possible while still properly updating + * the chain id to protect against replay attacks on an eventual fork of the chain. + * + * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method + * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. + * + * _Available since v3.4._ + */ +abstract contract EIP712 { + /* solhint-disable var-name-mixedcase */ + // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to + // invalidate the cached domain separator if the chain id changes. + bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; + uint256 private immutable _CACHED_CHAIN_ID; + address private immutable _CACHED_THIS; + + bytes32 private immutable _HASHED_NAME; + bytes32 private immutable _HASHED_VERSION; + bytes32 private immutable _TYPE_HASH; + + /* solhint-enable var-name-mixedcase */ + + /** + * @dev Initializes the domain separator and parameter caches. + * + * The meaning of `name` and `version` is specified in + * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: + * + * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. + * - `version`: the current major version of the signing domain. + * + * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart + * contract upgrade]. + */ + constructor(string memory name, string memory version) { + bytes32 hashedName = keccak256(bytes(name)); + bytes32 hashedVersion = keccak256(bytes(version)); + bytes32 typeHash = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + _HASHED_NAME = hashedName; + _HASHED_VERSION = hashedVersion; + _CACHED_CHAIN_ID = block.chainid; + _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); + _CACHED_THIS = address(this); + _TYPE_HASH = typeHash; + } + + /** + * @dev Returns the domain separator for the current chain. + */ + function _domainSeparatorV4() internal view returns (bytes32) { + if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { + return _CACHED_DOMAIN_SEPARATOR; + } else { + return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); + } + } + + function _buildDomainSeparator( + bytes32 typeHash, + bytes32 nameHash, + bytes32 versionHash + ) private view returns (bytes32) { + return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); + } + + /** + * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this + * function returns the hash of the fully encoded EIP712 message for this domain. + * + * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: + * + * ```solidity + * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( + * keccak256("Mail(address to,string contents)"), + * mailTo, + * keccak256(bytes(mailContents)) + * ))); + * address signer = ECDSA.recover(digest, signature); + * ``` + */ + function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { + return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/Math.sol @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Standard math utilities missing in the Solidity language. + */ +library Math { + enum Rounding { + Down, // Toward negative infinity + Up, // Toward infinity + Zero // Toward zero + } + + /** + * @dev Returns the largest of two numbers. + */ + function max(uint256 a, uint256 b) internal pure returns (uint256) { + return a > b ? a : b; + } + + /** + * @dev Returns the smallest of two numbers. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } + + /** + * @dev Returns the average of two numbers. The result is rounded towards + * zero. + */ + function average(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b) / 2 can overflow. + return (a & b) + (a ^ b) / 2; + } + + /** + * @dev Returns the ceiling of the division of two numbers. + * + * This differs from standard division with `/` in that it rounds up instead + * of rounding down. + */ + function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b - 1) / b can overflow on addition, so we distribute. + return a == 0 ? 0 : (a - 1) / b + 1; + } + + /** + * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) + * with further edits by Uniswap Labs also under MIT license. + */ + function mulDiv( + uint256 x, + uint256 y, + uint256 denominator + ) internal pure returns (uint256 result) { + unchecked { + // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use + // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 + // variables such that product = prod1 * 2^256 + prod0. + uint256 prod0; // Least significant 256 bits of the product + uint256 prod1; // Most significant 256 bits of the product + assembly { + let mm := mulmod(x, y, not(0)) + prod0 := mul(x, y) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + // Handle non-overflow cases, 256 by 256 division. + if (prod1 == 0) { + return prod0 / denominator; + } + + // Make sure the result is less than 2^256. Also prevents denominator == 0. + require(denominator > prod1); + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [prod1 prod0]. + uint256 remainder; + assembly { + // Compute remainder using mulmod. + remainder := mulmod(x, y, denominator) + + // Subtract 256 bit number from 512 bit number. + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. + // See https://cs.stackexchange.com/q/138556/92363. + + // Does not overflow because the denominator cannot be zero at this stage in the function. + uint256 twos = denominator & (~denominator + 1); + assembly { + // Divide denominator by twos. + denominator := div(denominator, twos) + + // Divide [prod1 prod0] by twos. + prod0 := div(prod0, twos) + + // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. + twos := add(div(sub(0, twos), twos), 1) + } + + // Shift in bits from prod1 into prod0. + prod0 |= prod1 * twos; + + // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such + // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for + // four bits. That is, denominator * inv = 1 mod 2^4. + uint256 inverse = (3 * denominator) ^ 2; + + // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works + // in modular arithmetic, doubling the correct bits in each step. + inverse *= 2 - denominator * inverse; // inverse mod 2^8 + inverse *= 2 - denominator * inverse; // inverse mod 2^16 + inverse *= 2 - denominator * inverse; // inverse mod 2^32 + inverse *= 2 - denominator * inverse; // inverse mod 2^64 + inverse *= 2 - denominator * inverse; // inverse mod 2^128 + inverse *= 2 - denominator * inverse; // inverse mod 2^256 + + // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. + // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is + // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 + // is no longer required. + result = prod0 * inverse; + return result; + } + } + + /** + * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. + */ + function mulDiv( + uint256 x, + uint256 y, + uint256 denominator, + Rounding rounding + ) internal pure returns (uint256) { + uint256 result = mulDiv(x, y, denominator); + if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { + result += 1; + } + return result; + } + + /** + * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. + * + * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). + */ + function sqrt(uint256 a) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + + // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. + // + // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have + // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. + // + // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` + // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` + // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` + // + // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. + uint256 result = 1 << (log2(a) >> 1); + + // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, + // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at + // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision + // into the expected uint128 result. + unchecked { + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + return min(result, a / result); + } + } + + /** + * @notice Calculates sqrt(a), following the selected rounding direction. + */ + function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = sqrt(a); + return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); + } + } + + /** + * @dev Return the log in base 2, rounded down, of a positive value. + * Returns 0 if given 0. + */ + function log2(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >> 128 > 0) { + value >>= 128; + result += 128; + } + if (value >> 64 > 0) { + value >>= 64; + result += 64; + } + if (value >> 32 > 0) { + value >>= 32; + result += 32; + } + if (value >> 16 > 0) { + value >>= 16; + result += 16; + } + if (value >> 8 > 0) { + value >>= 8; + result += 8; + } + if (value >> 4 > 0) { + value >>= 4; + result += 4; + } + if (value >> 2 > 0) { + value >>= 2; + result += 2; + } + if (value >> 1 > 0) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 2, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log2(value); + return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); + } + } + + /** + * @dev Return the log in base 10, rounded down, of a positive value. + * Returns 0 if given 0. + */ + function log10(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >= 10**64) { + value /= 10**64; + result += 64; + } + if (value >= 10**32) { + value /= 10**32; + result += 32; + } + if (value >= 10**16) { + value /= 10**16; + result += 16; + } + if (value >= 10**8) { + value /= 10**8; + result += 8; + } + if (value >= 10**4) { + value /= 10**4; + result += 4; + } + if (value >= 10**2) { + value /= 10**2; + result += 2; + } + if (value >= 10**1) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 10, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log10(value); + return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); + } + } + + /** + * @dev Return the log in base 256, rounded down, of a positive value. + * Returns 0 if given 0. + * + * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. + */ + function log256(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >> 128 > 0) { + value >>= 128; + result += 16; + } + if (value >> 64 > 0) { + value >>= 64; + result += 8; + } + if (value >> 32 > 0) { + value >>= 32; + result += 4; + } + if (value >> 16 > 0) { + value >>= 16; + result += 2; + } + if (value >> 8 > 0) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 10, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log256(value); + return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); + } + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/SafeCast.sol @@ -0,0 +1,1136 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) +// This file was procedurally generated from scripts/generate/templates/SafeCast.js. + +pragma solidity ^0.8.0; + +/** + * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow + * checks. + * + * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can + * easily result in undesired exploitation or bugs, since developers usually + * assume that overflows raise errors. `SafeCast` restores this intuition by + * reverting the transaction when such an operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing + * all math on `uint256` and `int256` and then downcasting. + */ +library SafeCast { + /** + * @dev Returns the downcasted uint248 from uint256, reverting on + * overflow (when the input is greater than largest uint248). + * + * Counterpart to Solidity's `uint248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + * + * _Available since v4.7._ + */ + function toUint248(uint256 value) internal pure returns (uint248) { + require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); + return uint248(value); + } + + /** + * @dev Returns the downcasted uint240 from uint256, reverting on + * overflow (when the input is greater than largest uint240). + * + * Counterpart to Solidity's `uint240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + * + * _Available since v4.7._ + */ + function toUint240(uint256 value) internal pure returns (uint240) { + require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); + return uint240(value); + } + + /** + * @dev Returns the downcasted uint232 from uint256, reverting on + * overflow (when the input is greater than largest uint232). + * + * Counterpart to Solidity's `uint232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + * + * _Available since v4.7._ + */ + function toUint232(uint256 value) internal pure returns (uint232) { + require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); + return uint232(value); + } + + /** + * @dev Returns the downcasted uint224 from uint256, reverting on + * overflow (when the input is greater than largest uint224). + * + * Counterpart to Solidity's `uint224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + * + * _Available since v4.2._ + */ + function toUint224(uint256 value) internal pure returns (uint224) { + require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); + return uint224(value); + } + + /** + * @dev Returns the downcasted uint216 from uint256, reverting on + * overflow (when the input is greater than largest uint216). + * + * Counterpart to Solidity's `uint216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + * + * _Available since v4.7._ + */ + function toUint216(uint256 value) internal pure returns (uint216) { + require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); + return uint216(value); + } + + /** + * @dev Returns the downcasted uint208 from uint256, reverting on + * overflow (when the input is greater than largest uint208). + * + * Counterpart to Solidity's `uint208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + * + * _Available since v4.7._ + */ + function toUint208(uint256 value) internal pure returns (uint208) { + require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); + return uint208(value); + } + + /** + * @dev Returns the downcasted uint200 from uint256, reverting on + * overflow (when the input is greater than largest uint200). + * + * Counterpart to Solidity's `uint200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + * + * _Available since v4.7._ + */ + function toUint200(uint256 value) internal pure returns (uint200) { + require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); + return uint200(value); + } + + /** + * @dev Returns the downcasted uint192 from uint256, reverting on + * overflow (when the input is greater than largest uint192). + * + * Counterpart to Solidity's `uint192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + * + * _Available since v4.7._ + */ + function toUint192(uint256 value) internal pure returns (uint192) { + require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); + return uint192(value); + } + + /** + * @dev Returns the downcasted uint184 from uint256, reverting on + * overflow (when the input is greater than largest uint184). + * + * Counterpart to Solidity's `uint184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + * + * _Available since v4.7._ + */ + function toUint184(uint256 value) internal pure returns (uint184) { + require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); + return uint184(value); + } + + /** + * @dev Returns the downcasted uint176 from uint256, reverting on + * overflow (when the input is greater than largest uint176). + * + * Counterpart to Solidity's `uint176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + * + * _Available since v4.7._ + */ + function toUint176(uint256 value) internal pure returns (uint176) { + require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); + return uint176(value); + } + + /** + * @dev Returns the downcasted uint168 from uint256, reverting on + * overflow (when the input is greater than largest uint168). + * + * Counterpart to Solidity's `uint168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + * + * _Available since v4.7._ + */ + function toUint168(uint256 value) internal pure returns (uint168) { + require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); + return uint168(value); + } + + /** + * @dev Returns the downcasted uint160 from uint256, reverting on + * overflow (when the input is greater than largest uint160). + * + * Counterpart to Solidity's `uint160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + * + * _Available since v4.7._ + */ + function toUint160(uint256 value) internal pure returns (uint160) { + require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); + return uint160(value); + } + + /** + * @dev Returns the downcasted uint152 from uint256, reverting on + * overflow (when the input is greater than largest uint152). + * + * Counterpart to Solidity's `uint152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + * + * _Available since v4.7._ + */ + function toUint152(uint256 value) internal pure returns (uint152) { + require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); + return uint152(value); + } + + /** + * @dev Returns the downcasted uint144 from uint256, reverting on + * overflow (when the input is greater than largest uint144). + * + * Counterpart to Solidity's `uint144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + * + * _Available since v4.7._ + */ + function toUint144(uint256 value) internal pure returns (uint144) { + require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); + return uint144(value); + } + + /** + * @dev Returns the downcasted uint136 from uint256, reverting on + * overflow (when the input is greater than largest uint136). + * + * Counterpart to Solidity's `uint136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + * + * _Available since v4.7._ + */ + function toUint136(uint256 value) internal pure returns (uint136) { + require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); + return uint136(value); + } + + /** + * @dev Returns the downcasted uint128 from uint256, reverting on + * overflow (when the input is greater than largest uint128). + * + * Counterpart to Solidity's `uint128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + * + * _Available since v2.5._ + */ + function toUint128(uint256 value) internal pure returns (uint128) { + require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); + return uint128(value); + } + + /** + * @dev Returns the downcasted uint120 from uint256, reverting on + * overflow (when the input is greater than largest uint120). + * + * Counterpart to Solidity's `uint120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + * + * _Available since v4.7._ + */ + function toUint120(uint256 value) internal pure returns (uint120) { + require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); + return uint120(value); + } + + /** + * @dev Returns the downcasted uint112 from uint256, reverting on + * overflow (when the input is greater than largest uint112). + * + * Counterpart to Solidity's `uint112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + * + * _Available since v4.7._ + */ + function toUint112(uint256 value) internal pure returns (uint112) { + require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); + return uint112(value); + } + + /** + * @dev Returns the downcasted uint104 from uint256, reverting on + * overflow (when the input is greater than largest uint104). + * + * Counterpart to Solidity's `uint104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + * + * _Available since v4.7._ + */ + function toUint104(uint256 value) internal pure returns (uint104) { + require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); + return uint104(value); + } + + /** + * @dev Returns the downcasted uint96 from uint256, reverting on + * overflow (when the input is greater than largest uint96). + * + * Counterpart to Solidity's `uint96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + * + * _Available since v4.2._ + */ + function toUint96(uint256 value) internal pure returns (uint96) { + require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); + return uint96(value); + } + + /** + * @dev Returns the downcasted uint88 from uint256, reverting on + * overflow (when the input is greater than largest uint88). + * + * Counterpart to Solidity's `uint88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + * + * _Available since v4.7._ + */ + function toUint88(uint256 value) internal pure returns (uint88) { + require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); + return uint88(value); + } + + /** + * @dev Returns the downcasted uint80 from uint256, reverting on + * overflow (when the input is greater than largest uint80). + * + * Counterpart to Solidity's `uint80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + * + * _Available since v4.7._ + */ + function toUint80(uint256 value) internal pure returns (uint80) { + require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); + return uint80(value); + } + + /** + * @dev Returns the downcasted uint72 from uint256, reverting on + * overflow (when the input is greater than largest uint72). + * + * Counterpart to Solidity's `uint72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + * + * _Available since v4.7._ + */ + function toUint72(uint256 value) internal pure returns (uint72) { + require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); + return uint72(value); + } + + /** + * @dev Returns the downcasted uint64 from uint256, reverting on + * overflow (when the input is greater than largest uint64). + * + * Counterpart to Solidity's `uint64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + * + * _Available since v2.5._ + */ + function toUint64(uint256 value) internal pure returns (uint64) { + require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); + return uint64(value); + } + + /** + * @dev Returns the downcasted uint56 from uint256, reverting on + * overflow (when the input is greater than largest uint56). + * + * Counterpart to Solidity's `uint56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + * + * _Available since v4.7._ + */ + function toUint56(uint256 value) internal pure returns (uint56) { + require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); + return uint56(value); + } + + /** + * @dev Returns the downcasted uint48 from uint256, reverting on + * overflow (when the input is greater than largest uint48). + * + * Counterpart to Solidity's `uint48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + * + * _Available since v4.7._ + */ + function toUint48(uint256 value) internal pure returns (uint48) { + require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); + return uint48(value); + } + + /** + * @dev Returns the downcasted uint40 from uint256, reverting on + * overflow (when the input is greater than largest uint40). + * + * Counterpart to Solidity's `uint40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + * + * _Available since v4.7._ + */ + function toUint40(uint256 value) internal pure returns (uint40) { + require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); + return uint40(value); + } + + /** + * @dev Returns the downcasted uint32 from uint256, reverting on + * overflow (when the input is greater than largest uint32). + * + * Counterpart to Solidity's `uint32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + * + * _Available since v2.5._ + */ + function toUint32(uint256 value) internal pure returns (uint32) { + require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); + return uint32(value); + } + + /** + * @dev Returns the downcasted uint24 from uint256, reverting on + * overflow (when the input is greater than largest uint24). + * + * Counterpart to Solidity's `uint24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + * + * _Available since v4.7._ + */ + function toUint24(uint256 value) internal pure returns (uint24) { + require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); + return uint24(value); + } + + /** + * @dev Returns the downcasted uint16 from uint256, reverting on + * overflow (when the input is greater than largest uint16). + * + * Counterpart to Solidity's `uint16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + * + * _Available since v2.5._ + */ + function toUint16(uint256 value) internal pure returns (uint16) { + require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); + return uint16(value); + } + + /** + * @dev Returns the downcasted uint8 from uint256, reverting on + * overflow (when the input is greater than largest uint8). + * + * Counterpart to Solidity's `uint8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + * + * _Available since v2.5._ + */ + function toUint8(uint256 value) internal pure returns (uint8) { + require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); + return uint8(value); + } + + /** + * @dev Converts a signed int256 into an unsigned uint256. + * + * Requirements: + * + * - input must be greater than or equal to 0. + * + * _Available since v3.0._ + */ + function toUint256(int256 value) internal pure returns (uint256) { + require(value >= 0, "SafeCast: value must be positive"); + return uint256(value); + } + + /** + * @dev Returns the downcasted int248 from int256, reverting on + * overflow (when the input is less than smallest int248 or + * greater than largest int248). + * + * Counterpart to Solidity's `int248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + * + * _Available since v4.7._ + */ + function toInt248(int256 value) internal pure returns (int248 downcasted) { + downcasted = int248(value); + require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); + } + + /** + * @dev Returns the downcasted int240 from int256, reverting on + * overflow (when the input is less than smallest int240 or + * greater than largest int240). + * + * Counterpart to Solidity's `int240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + * + * _Available since v4.7._ + */ + function toInt240(int256 value) internal pure returns (int240 downcasted) { + downcasted = int240(value); + require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); + } + + /** + * @dev Returns the downcasted int232 from int256, reverting on + * overflow (when the input is less than smallest int232 or + * greater than largest int232). + * + * Counterpart to Solidity's `int232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + * + * _Available since v4.7._ + */ + function toInt232(int256 value) internal pure returns (int232 downcasted) { + downcasted = int232(value); + require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); + } + + /** + * @dev Returns the downcasted int224 from int256, reverting on + * overflow (when the input is less than smallest int224 or + * greater than largest int224). + * + * Counterpart to Solidity's `int224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + * + * _Available since v4.7._ + */ + function toInt224(int256 value) internal pure returns (int224 downcasted) { + downcasted = int224(value); + require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); + } + + /** + * @dev Returns the downcasted int216 from int256, reverting on + * overflow (when the input is less than smallest int216 or + * greater than largest int216). + * + * Counterpart to Solidity's `int216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + * + * _Available since v4.7._ + */ + function toInt216(int256 value) internal pure returns (int216 downcasted) { + downcasted = int216(value); + require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); + } + + /** + * @dev Returns the downcasted int208 from int256, reverting on + * overflow (when the input is less than smallest int208 or + * greater than largest int208). + * + * Counterpart to Solidity's `int208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + * + * _Available since v4.7._ + */ + function toInt208(int256 value) internal pure returns (int208 downcasted) { + downcasted = int208(value); + require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); + } + + /** + * @dev Returns the downcasted int200 from int256, reverting on + * overflow (when the input is less than smallest int200 or + * greater than largest int200). + * + * Counterpart to Solidity's `int200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + * + * _Available since v4.7._ + */ + function toInt200(int256 value) internal pure returns (int200 downcasted) { + downcasted = int200(value); + require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); + } + + /** + * @dev Returns the downcasted int192 from int256, reverting on + * overflow (when the input is less than smallest int192 or + * greater than largest int192). + * + * Counterpart to Solidity's `int192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + * + * _Available since v4.7._ + */ + function toInt192(int256 value) internal pure returns (int192 downcasted) { + downcasted = int192(value); + require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); + } + + /** + * @dev Returns the downcasted int184 from int256, reverting on + * overflow (when the input is less than smallest int184 or + * greater than largest int184). + * + * Counterpart to Solidity's `int184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + * + * _Available since v4.7._ + */ + function toInt184(int256 value) internal pure returns (int184 downcasted) { + downcasted = int184(value); + require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); + } + + /** + * @dev Returns the downcasted int176 from int256, reverting on + * overflow (when the input is less than smallest int176 or + * greater than largest int176). + * + * Counterpart to Solidity's `int176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + * + * _Available since v4.7._ + */ + function toInt176(int256 value) internal pure returns (int176 downcasted) { + downcasted = int176(value); + require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); + } + + /** + * @dev Returns the downcasted int168 from int256, reverting on + * overflow (when the input is less than smallest int168 or + * greater than largest int168). + * + * Counterpart to Solidity's `int168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + * + * _Available since v4.7._ + */ + function toInt168(int256 value) internal pure returns (int168 downcasted) { + downcasted = int168(value); + require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); + } + + /** + * @dev Returns the downcasted int160 from int256, reverting on + * overflow (when the input is less than smallest int160 or + * greater than largest int160). + * + * Counterpart to Solidity's `int160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + * + * _Available since v4.7._ + */ + function toInt160(int256 value) internal pure returns (int160 downcasted) { + downcasted = int160(value); + require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); + } + + /** + * @dev Returns the downcasted int152 from int256, reverting on + * overflow (when the input is less than smallest int152 or + * greater than largest int152). + * + * Counterpart to Solidity's `int152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + * + * _Available since v4.7._ + */ + function toInt152(int256 value) internal pure returns (int152 downcasted) { + downcasted = int152(value); + require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); + } + + /** + * @dev Returns the downcasted int144 from int256, reverting on + * overflow (when the input is less than smallest int144 or + * greater than largest int144). + * + * Counterpart to Solidity's `int144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + * + * _Available since v4.7._ + */ + function toInt144(int256 value) internal pure returns (int144 downcasted) { + downcasted = int144(value); + require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); + } + + /** + * @dev Returns the downcasted int136 from int256, reverting on + * overflow (when the input is less than smallest int136 or + * greater than largest int136). + * + * Counterpart to Solidity's `int136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + * + * _Available since v4.7._ + */ + function toInt136(int256 value) internal pure returns (int136 downcasted) { + downcasted = int136(value); + require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); + } + + /** + * @dev Returns the downcasted int128 from int256, reverting on + * overflow (when the input is less than smallest int128 or + * greater than largest int128). + * + * Counterpart to Solidity's `int128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + * + * _Available since v3.1._ + */ + function toInt128(int256 value) internal pure returns (int128 downcasted) { + downcasted = int128(value); + require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); + } + + /** + * @dev Returns the downcasted int120 from int256, reverting on + * overflow (when the input is less than smallest int120 or + * greater than largest int120). + * + * Counterpart to Solidity's `int120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + * + * _Available since v4.7._ + */ + function toInt120(int256 value) internal pure returns (int120 downcasted) { + downcasted = int120(value); + require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); + } + + /** + * @dev Returns the downcasted int112 from int256, reverting on + * overflow (when the input is less than smallest int112 or + * greater than largest int112). + * + * Counterpart to Solidity's `int112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + * + * _Available since v4.7._ + */ + function toInt112(int256 value) internal pure returns (int112 downcasted) { + downcasted = int112(value); + require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); + } + + /** + * @dev Returns the downcasted int104 from int256, reverting on + * overflow (when the input is less than smallest int104 or + * greater than largest int104). + * + * Counterpart to Solidity's `int104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + * + * _Available since v4.7._ + */ + function toInt104(int256 value) internal pure returns (int104 downcasted) { + downcasted = int104(value); + require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); + } + + /** + * @dev Returns the downcasted int96 from int256, reverting on + * overflow (when the input is less than smallest int96 or + * greater than largest int96). + * + * Counterpart to Solidity's `int96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + * + * _Available since v4.7._ + */ + function toInt96(int256 value) internal pure returns (int96 downcasted) { + downcasted = int96(value); + require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); + } + + /** + * @dev Returns the downcasted int88 from int256, reverting on + * overflow (when the input is less than smallest int88 or + * greater than largest int88). + * + * Counterpart to Solidity's `int88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + * + * _Available since v4.7._ + */ + function toInt88(int256 value) internal pure returns (int88 downcasted) { + downcasted = int88(value); + require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); + } + + /** + * @dev Returns the downcasted int80 from int256, reverting on + * overflow (when the input is less than smallest int80 or + * greater than largest int80). + * + * Counterpart to Solidity's `int80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + * + * _Available since v4.7._ + */ + function toInt80(int256 value) internal pure returns (int80 downcasted) { + downcasted = int80(value); + require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); + } + + /** + * @dev Returns the downcasted int72 from int256, reverting on + * overflow (when the input is less than smallest int72 or + * greater than largest int72). + * + * Counterpart to Solidity's `int72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + * + * _Available since v4.7._ + */ + function toInt72(int256 value) internal pure returns (int72 downcasted) { + downcasted = int72(value); + require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); + } + + /** + * @dev Returns the downcasted int64 from int256, reverting on + * overflow (when the input is less than smallest int64 or + * greater than largest int64). + * + * Counterpart to Solidity's `int64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + * + * _Available since v3.1._ + */ + function toInt64(int256 value) internal pure returns (int64 downcasted) { + downcasted = int64(value); + require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); + } + + /** + * @dev Returns the downcasted int56 from int256, reverting on + * overflow (when the input is less than smallest int56 or + * greater than largest int56). + * + * Counterpart to Solidity's `int56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + * + * _Available since v4.7._ + */ + function toInt56(int256 value) internal pure returns (int56 downcasted) { + downcasted = int56(value); + require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); + } + + /** + * @dev Returns the downcasted int48 from int256, reverting on + * overflow (when the input is less than smallest int48 or + * greater than largest int48). + * + * Counterpart to Solidity's `int48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + * + * _Available since v4.7._ + */ + function toInt48(int256 value) internal pure returns (int48 downcasted) { + downcasted = int48(value); + require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); + } + + /** + * @dev Returns the downcasted int40 from int256, reverting on + * overflow (when the input is less than smallest int40 or + * greater than largest int40). + * + * Counterpart to Solidity's `int40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + * + * _Available since v4.7._ + */ + function toInt40(int256 value) internal pure returns (int40 downcasted) { + downcasted = int40(value); + require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); + } + + /** + * @dev Returns the downcasted int32 from int256, reverting on + * overflow (when the input is less than smallest int32 or + * greater than largest int32). + * + * Counterpart to Solidity's `int32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + * + * _Available since v3.1._ + */ + function toInt32(int256 value) internal pure returns (int32 downcasted) { + downcasted = int32(value); + require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); + } + + /** + * @dev Returns the downcasted int24 from int256, reverting on + * overflow (when the input is less than smallest int24 or + * greater than largest int24). + * + * Counterpart to Solidity's `int24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + * + * _Available since v4.7._ + */ + function toInt24(int256 value) internal pure returns (int24 downcasted) { + downcasted = int24(value); + require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); + } + + /** + * @dev Returns the downcasted int16 from int256, reverting on + * overflow (when the input is less than smallest int16 or + * greater than largest int16). + * + * Counterpart to Solidity's `int16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + * + * _Available since v3.1._ + */ + function toInt16(int256 value) internal pure returns (int16 downcasted) { + downcasted = int16(value); + require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); + } + + /** + * @dev Returns the downcasted int8 from int256, reverting on + * overflow (when the input is less than smallest int8 or + * greater than largest int8). + * + * Counterpart to Solidity's `int8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + * + * _Available since v3.1._ + */ + function toInt8(int256 value) internal pure returns (int8 downcasted) { + downcasted = int8(value); + require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); + } + + /** + * @dev Converts an unsigned uint256 into a signed int256. + * + * Requirements: + * + * - input must be less than or equal to maxInt256. + * + * _Available since v3.0._ + */ + function toInt256(uint256 value) internal pure returns (int256) { + // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive + require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); + return int256(value); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/ZeppelinContract.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import "./@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; +import "./@openzeppelin/contracts/security/Pausable.sol"; +import "./@openzeppelin/contracts/access/Ownable.sol"; +import "./@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; +import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; +import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol"; + +contract ZeppelinContract is ERC20, ERC20Burnable, Pausable, Ownable, ERC20Permit, ERC20Votes, ERC20FlashMint { + constructor() + ERC20("ZeppelinContract", "UNQ") + ERC20Permit("ZeppelinContract") + {} + + function pause() public onlyOwner { + _pause(); + } + + function unpause() public onlyOwner { + _unpause(); + } + + function mint(address to, uint256 amount) public onlyOwner { + _mint(to, amount); + } + + function _beforeTokenTransfer(address from, address to, uint256 amount) + internal + whenNotPaused + override + { + super._beforeTokenTransfer(from, to, amount); + } + + // The following functions are overrides required by Solidity. + + function _afterTokenTransfer(address from, address to, uint256 amount) + internal + override(ERC20, ERC20Votes) + { + super._afterTokenTransfer(from, to, amount); + } + + function _mint(address to, uint256 amount) + internal + override(ERC20, ERC20Votes) + { + super._mint(to, amount); + } + + function _burn(address account, uint256 amount) + internal + override(ERC20, ERC20Votes) + { + super._burn(account, amount); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.abi @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint32","name":"pos","type":"uint32"}],"name":"checkpoints","outputs":[{"components":[{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"uint224","name":"votes","type":"uint224"}],"internalType":"struct ERC20Votes.Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"flashFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC3156FlashBorrower","name":"receiver","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"maxFlashLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.bin @@ -0,0 +1 @@ +6101406040523480156200001257600080fd5b506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525080604051806040016040528060018152602001603160f81b8152506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525060405180604001604052806003815260200162554e5160e81b8152508160039081620000ad919062000269565b506004620000bc828262000269565b50506005805460ff1916905550620000d4336200016a565b815160209283012081519183019190912060e08290526101008190524660a0818152604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818801819052818301969096526060810194909452608080850193909352308483018190528151808603909301835260c094850190915281519190950120905291909152610120525062000335565b600580546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620001ef57607f821691505b6020821081036200021057634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200026457600081815260208120601f850160051c810160208610156200023f5750805b601f850160051c820191505b8181101562000260578281556001016200024b565b5050505b505050565b81516001600160401b03811115620002855762000285620001c4565b6200029d81620002968454620001da565b8462000216565b602080601f831160018114620002d55760008415620002bc5750858301515b600019600386901b1c1916600185901b17855562000260565b600085815260208120601f198616915b828110156200030657888601518255948401946001909101908401620002e5565b5085821015620003255787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a05160c05160e05161010051610120516125f06200038560003960006112ad015260006112fc015260006112d7015260006112300152600061125a0152600061128401526125f06000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c806370a082311161011a5780639ab24eb0116100ad578063d505accf1161007c578063d505accf1461046a578063d9d98ce41461047d578063dd62ed3e14610490578063f1127ed8146104a3578063f2fde38b146104e057600080fd5b80639ab24eb01461041e578063a457c2d714610431578063a9059cbb14610444578063c3cda5201461045757600080fd5b80638456cb59116100e95780638456cb59146103e55780638da5cb5b146103ed5780638e539e8c1461040357806395d89b411461041657600080fd5b806370a082311461038e578063715018a6146103b757806379cc6790146103bf5780637ecebe00146103d257600080fd5b80633f4ba83a1161019d5780635c19a95c1161016c5780635c19a95c146103225780635c975abb146103355780635cffe9de14610340578063613255ab146103535780636fcfff451461036657600080fd5b80633f4ba83a146102ae57806340c10f19146102b857806342966c68146102cb578063587cde1e146102de57600080fd5b8063313ce567116101d9578063313ce567146102715780633644e5151461028057806339509351146102885780633a46b1a81461029b57600080fd5b806306fdde031461020b578063095ea7b31461022957806318160ddd1461024c57806323b872dd1461025e575b600080fd5b6102136104f3565b604051610220919061217e565b60405180910390f35b61023c6102373660046121e1565b610585565b6040519015158152602001610220565b6002545b604051908152602001610220565b61023c61026c36600461220d565b61059f565b60405160128152602001610220565b6102506105c3565b61023c6102963660046121e1565b6105d2565b6102506102a93660046121e1565b6105f4565b6102b6610673565b005b6102b66102c63660046121e1565b610685565b6102b66102d936600461224e565b61069b565b61030a6102ec366004612267565b6001600160a01b039081166000908152600860205260409020541690565b6040516001600160a01b039091168152602001610220565b6102b6610330366004612267565b6106a8565b60055460ff1661023c565b61023c61034e366004612284565b6106b2565b610250610361366004612267565b610896565b610379610374366004612267565b6108be565b60405163ffffffff9091168152602001610220565b61025061039c366004612267565b6001600160a01b031660009081526020819052604090205490565b6102b66108e0565b6102b66103cd3660046121e1565b6108f2565b6102506103e0366004612267565b610907565b6102b6610925565b60055461010090046001600160a01b031661030a565b61025061041136600461224e565b610935565b610213610991565b61025061042c366004612267565b6109a0565b61023c61043f3660046121e1565b610a27565b61023c6104523660046121e1565b610aa2565b6102b6610465366004612339565b610ab0565b6102b6610478366004612393565b610be6565b61025061048b3660046121e1565b610d4a565b61025061049e366004612401565b610dab565b6104b66104b136600461243a565b610dd6565b60408051825163ffffffff1681526020928301516001600160e01b03169281019290925201610220565b6102b66104ee366004612267565b610e5a565b60606003805461050290612471565b80601f016020809104026020016040519081016040528092919081815260200182805461052e90612471565b801561057b5780601f106105505761010080835404028352916020019161057b565b820191906000526020600020905b81548152906001019060200180831161055e57829003601f168201915b5050505050905090565b600033610593818585610ed0565b60019150505b92915050565b6000336105ad858285610ff4565b6105b885858561106e565b506001949350505050565b60006105cd611223565b905090565b6000336105938185856105e58383610dab565b6105ef91906124bb565b610ed0565b600043821061064a5760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e65640060448201526064015b60405180910390fd5b6001600160a01b038316600090815260096020526040902061066c908361134a565b9392505050565b61067b611441565b6106836114a1565b565b61068d611441565b61069782826114f3565b5050565b6106a533826114fd565b50565b6106a53382611507565b60006106bd85610896565b8411156107205760405162461bcd60e51b815260206004820152602b60248201527f4552433230466c6173684d696e743a20616d6f756e742065786365656473206d60448201526a30bc233630b9b42637b0b760a91b6064820152608401610641565b600061072c8686610d4a565b905061073887866114f3565b6040516323e30c8b60e01b81527f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9906001600160a01b038916906323e30c8b906107909033908b908b9088908c908c906004016124ce565b6020604051808303816000875af11580156107af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d3919061252a565b1461082c5760405162461bcd60e51b8152602060048201526024808201527f4552433230466c6173684d696e743a20696e76616c69642072657475726e2076604482015263616c756560e01b6064820152608401610641565b6000610842883061083d858a6124bb565b610ff4565b81158061085657506001600160a01b038116155b156108735761086e8861086984896124bb565b6114fd565b610888565b61087d88876114fd565b61088888828461106e565b506001979650505050505050565b60006001600160a01b03821630146108af576000610599565b60025461059990600019612543565b6001600160a01b03811660009081526009602052604081205461059990611580565b6108e8611441565b61068360006115e9565b6108fd823383610ff4565b61069782826114fd565b6001600160a01b038116600090815260066020526040812054610599565b61092d611441565b610683611643565b60004382106109865760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e6564006044820152606401610641565b610599600a8361134a565b60606004805461050290612471565b6001600160a01b0381166000908152600960205260408120548015610a14576001600160a01b03831660009081526009602052604090206109e2600183612543565b815481106109f2576109f2612556565b60009182526020909120015464010000000090046001600160e01b0316610a17565b60005b6001600160e01b03169392505050565b60003381610a358286610dab565b905083811015610a955760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610641565b6105b88286868403610ed0565b60003361059381858561106e565b83421115610b005760405162461bcd60e51b815260206004820152601d60248201527f4552433230566f7465733a207369676e617475726520657870697265640000006044820152606401610641565b604080517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60208201526001600160a01b038816918101919091526060810186905260808101859052600090610b7a90610b729060a00160405160208183030381529060405280519060200120611680565b8585856116ce565b9050610b85816116f6565b8614610bd35760405162461bcd60e51b815260206004820152601960248201527f4552433230566f7465733a20696e76616c6964206e6f6e6365000000000000006044820152606401610641565b610bdd8188611507565b50505050505050565b83421115610c365760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610641565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9888888610c658c6116f6565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506000610cc082611680565b90506000610cd0828787876116ce565b9050896001600160a01b0316816001600160a01b031614610d335760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610641565b610d3e8a8a8a610ed0565b50505050505050505050565b60006001600160a01b0383163014610da45760405162461bcd60e51b815260206004820152601b60248201527f4552433230466c6173684d696e743a2077726f6e6720746f6b656e00000000006044820152606401610641565b600061066c565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60408051808201909152600080825260208201526001600160a01b0383166000908152600960205260409020805463ffffffff8416908110610e1a57610e1a612556565b60009182526020918290206040805180820190915291015463ffffffff8116825264010000000090046001600160e01b0316918101919091529392505050565b610e62611441565b6001600160a01b038116610ec75760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610641565b6106a5816115e9565b6001600160a01b038316610f325760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610641565b6001600160a01b038216610f935760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610641565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60006110008484610dab565b90506000198114611068578181101561105b5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610641565b6110688484848403610ed0565b50505050565b6001600160a01b0383166110d25760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610641565b6001600160a01b0382166111345760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610641565b61113f83838361171e565b6001600160a01b038316600090815260208190526040902054818110156111b75760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610641565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a361106884848461172b565b6000306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614801561127c57507f000000000000000000000000000000000000000000000000000000000000000046145b156112a657507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b8154600090818160058111156113a457600061136584611736565b61136f9085612543565b600088815260209020909150869082015463ffffffff161115611394578091506113a2565b61139f8160016124bb565b92505b505b808210156113f15760006113b8838361181e565b600088815260209020909150869082015463ffffffff1611156113dd578091506113eb565b6113e88160016124bb565b92505b506113a4565b801561142b5761141486611406600184612543565b600091825260209091200190565b5464010000000090046001600160e01b031661142e565b60005b6001600160e01b03169695505050505050565b6005546001600160a01b036101009091041633146106835760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610641565b6114a9611839565b6005805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6106978282611882565b610697828261190c565b6001600160a01b038281166000818152600860208181526040808420805485845282862054949093528787166001600160a01b03198416811790915590519190951694919391928592917f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4611068828483611924565b600063ffffffff8211156115e55760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610641565b5090565b600580546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b61164b611a61565b6005805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586114d63390565b600061059961168d611223565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b60008060006116df87878787611aa7565b915091506116ec81611b6b565b5095945050505050565b6001600160a01b03811660009081526006602052604090208054600181018255905b50919050565b611726611a61565b505050565b611726838383611cb5565b60008160000361174857506000919050565b6000600161175584611ce7565b901c6001901b9050600181848161176e5761176e61256c565b048201901c905060018184816117865761178661256c565b048201901c9050600181848161179e5761179e61256c565b048201901c905060018184816117b6576117b661256c565b048201901c905060018184816117ce576117ce61256c565b048201901c905060018184816117e6576117e661256c565b048201901c905060018184816117fe576117fe61256c565b048201901c905061066c818285816118185761181861256c565b04611d7b565b600061182d6002848418612582565b61066c908484166124bb565b60055460ff166106835760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610641565b61188c8282611d91565b6002546001600160e01b0310156118fe5760405162461bcd60e51b815260206004820152603060248201527f4552433230566f7465733a20746f74616c20737570706c79207269736b73206f60448201526f766572666c6f77696e6720766f74657360801b6064820152608401610641565b611068600a611e6483611e70565b6119168282611fc4565b611068600a61210983611e70565b816001600160a01b0316836001600160a01b0316141580156119465750600081115b15611726576001600160a01b038316156119d4576001600160a01b038316600090815260096020526040812081906119819061210985611e70565b91509150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516119c9929190918252602082015260400190565b60405180910390a250505b6001600160a01b03821615611726576001600160a01b03821660009081526009602052604081208190611a0a90611e6485611e70565b91509150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611a52929190918252602082015260400190565b60405180910390a25050505050565b60055460ff16156106835760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610641565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611ade5750600090506003611b62565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611b32573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611b5b57600060019250925050611b62565b9150600090505b94509492505050565b6000816004811115611b7f57611b7f6125a4565b03611b875750565b6001816004811115611b9b57611b9b6125a4565b03611be85760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610641565b6002816004811115611bfc57611bfc6125a4565b03611c495760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610641565b6003816004811115611c5d57611c5d6125a4565b036106a55760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610641565b6001600160a01b0383811660009081526008602052604080822054858416835291205461172692918216911683611924565b600080608083901c15611cfc57608092831c92015b604083901c15611d0e57604092831c92015b602083901c15611d2057602092831c92015b601083901c15611d3257601092831c92015b600883901c15611d4457600892831c92015b600483901c15611d5657600492831c92015b600283901c15611d6857600292831c92015b600183901c156105995760010192915050565b6000818310611d8a578161066c565b5090919050565b6001600160a01b038216611de75760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610641565b611df36000838361171e565b8060026000828254611e0591906124bb565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36106976000838361172b565b600061066c82846124bb565b82546000908190818115611ebd57611e8d87611406600185612543565b60408051808201909152905463ffffffff8116825264010000000090046001600160e01b03166020820152611ed2565b60408051808201909152600080825260208201525b905080602001516001600160e01b03169350611ef284868863ffffffff16565b9250600082118015611f0a5750805163ffffffff1643145b15611f4f57611f1883612115565b611f2788611406600186612543565b80546001600160e01b03929092166401000000000263ffffffff909216919091179055611fba565b866040518060400160405280611f6443611580565b63ffffffff168152602001611f7886612115565b6001600160e01b0390811690915282546001810184556000938452602093849020835194909301519091166401000000000263ffffffff909316929092179101555b5050935093915050565b6001600160a01b0382166120245760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610641565b6120308260008361171e565b6001600160a01b038216600090815260208190526040902054818110156120a45760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610641565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36117268360008461172b565b600061066c8284612543565b60006001600160e01b038211156115e55760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20326044820152663234206269747360c81b6064820152608401610641565b600060208083528351808285015260005b818110156121ab5785810183015185820160400152820161218f565b506000604082860101526040601f19601f8301168501019250505092915050565b6001600160a01b03811681146106a557600080fd5b600080604083850312156121f457600080fd5b82356121ff816121cc565b946020939093013593505050565b60008060006060848603121561222257600080fd5b833561222d816121cc565b9250602084013561223d816121cc565b929592945050506040919091013590565b60006020828403121561226057600080fd5b5035919050565b60006020828403121561227957600080fd5b813561066c816121cc565b60008060008060006080868803121561229c57600080fd5b85356122a7816121cc565b945060208601356122b7816121cc565b935060408601359250606086013567ffffffffffffffff808211156122db57600080fd5b818801915088601f8301126122ef57600080fd5b8135818111156122fe57600080fd5b89602082850101111561231057600080fd5b9699959850939650602001949392505050565b803560ff8116811461233457600080fd5b919050565b60008060008060008060c0878903121561235257600080fd5b863561235d816121cc565b9550602087013594506040870135935061237960608801612323565b92506080870135915060a087013590509295509295509295565b600080600080600080600060e0888a0312156123ae57600080fd5b87356123b9816121cc565b965060208801356123c9816121cc565b955060408801359450606088013593506123e560808901612323565b925060a0880135915060c0880135905092959891949750929550565b6000806040838503121561241457600080fd5b823561241f816121cc565b9150602083013561242f816121cc565b809150509250929050565b6000806040838503121561244d57600080fd5b8235612458816121cc565b9150602083013563ffffffff8116811461242f57600080fd5b600181811c9082168061248557607f821691505b60208210810361171857634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610599576105996124a5565b6001600160a01b03878116825286166020820152604081018590526060810184905260a06080820181905281018290526000828460c0840137600060c0848401015260c0601f19601f8501168301019050979650505050505050565b60006020828403121561253c57600080fd5b5051919050565b81810381811115610599576105996124a5565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b60008261259f57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052602160045260246000fdfea264697066735822122057e08ed799a9773c2de4d8c40122801fc9994042a2e3e3ac01e5aa1c384b1c4b64736f6c63430008110033 \ No newline at end of file --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/README.md @@ -0,0 +1,12 @@ +# OpenZeppelin Contracts + +The files in this directory were sourced unmodified from OpenZeppelin Contracts v4.8.1. + +They are not meant to be edited. + +The originals can be found on [GitHub] and [npm]. + +[GitHub]: https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.8.1 +[npm]: https://www.npmjs.com/package/@openzeppelin/contracts/v/4.8.1 + +Generated with OpenZeppelin Contracts Wizard (https://zpl.in/wizard). --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/access/Ownable.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * By default, the owner account will be the one that deploys the contract. This + * can later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +abstract contract Ownable is Context { + address private _owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor() { + _transferOwnership(_msgSender()); + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + _checkOwner(); + _; + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + return _owner; + } + + /** + * @dev Throws if the sender is not the owner. + */ + function _checkOwner() internal view virtual { + require(owner() == _msgSender(), "Ownable: caller is not the owner"); + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions anymore. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby removing any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + _transferOwnership(address(0)); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual { + address oldOwner = _owner; + _owner = newOwner; + emit OwnershipTransferred(oldOwner, newOwner); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/ERC721.sol @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/ERC721.sol) + +pragma solidity ^0.8.0; + +import "./IERC721.sol"; +import "./IERC721Receiver.sol"; +import "./extensions/IERC721Metadata.sol"; +import "../../utils/Address.sol"; +import "../../utils/Context.sol"; +import "../../utils/Strings.sol"; +import "../../utils/introspection/ERC165.sol"; + +/** + * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including + * the Metadata extension, but not including the Enumerable extension, which is available separately as + * {ERC721Enumerable}. + */ +contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { + using Address for address; + using Strings for uint256; + + // Token name + string private _name; + + // Token symbol + string private _symbol; + + // Mapping from token ID to owner address + mapping(uint256 => address) private _owners; + + // Mapping owner address to token count + mapping(address => uint256) private _balances; + + // Mapping from token ID to approved address + mapping(uint256 => address) private _tokenApprovals; + + // Mapping from owner to operator approvals + mapping(address => mapping(address => bool)) private _operatorApprovals; + + /** + * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IERC721).interfaceId || + interfaceId == type(IERC721Metadata).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC721-balanceOf}. + */ + function balanceOf(address owner) public view virtual override returns (uint256) { + require(owner != address(0), "ERC721: address zero is not a valid owner"); + return _balances[owner]; + } + + /** + * @dev See {IERC721-ownerOf}. + */ + function ownerOf(uint256 tokenId) public view virtual override returns (address) { + address owner = _ownerOf(tokenId); + require(owner != address(0), "ERC721: invalid token ID"); + return owner; + } + + /** + * @dev See {IERC721Metadata-name}. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev See {IERC721Metadata-symbol}. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev See {IERC721Metadata-tokenURI}. + */ + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { + _requireMinted(tokenId); + + string memory baseURI = _baseURI(); + return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; + } + + /** + * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each + * token will be the concatenation of the `baseURI` and the `tokenId`. Empty + * by default, can be overridden in child contracts. + */ + function _baseURI() internal view virtual returns (string memory) { + return ""; + } + + /** + * @dev See {IERC721-approve}. + */ + function approve(address to, uint256 tokenId) public virtual override { + address owner = ERC721.ownerOf(tokenId); + require(to != owner, "ERC721: approval to current owner"); + + require( + _msgSender() == owner || isApprovedForAll(owner, _msgSender()), + "ERC721: approve caller is not token owner or approved for all" + ); + + _approve(to, tokenId); + } + + /** + * @dev See {IERC721-getApproved}. + */ + function getApproved(uint256 tokenId) public view virtual override returns (address) { + _requireMinted(tokenId); + + return _tokenApprovals[tokenId]; + } + + /** + * @dev See {IERC721-setApprovalForAll}. + */ + function setApprovalForAll(address operator, bool approved) public virtual override { + _setApprovalForAll(_msgSender(), operator, approved); + } + + /** + * @dev See {IERC721-isApprovedForAll}. + */ + function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { + return _operatorApprovals[owner][operator]; + } + + /** + * @dev See {IERC721-transferFrom}. + */ + function transferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + //solhint-disable-next-line max-line-length + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); + + _transfer(from, to, tokenId); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + safeTransferFrom(from, to, tokenId, ""); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes memory data + ) public virtual override { + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); + _safeTransfer(from, to, tokenId, data); + } + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC721 protocol to prevent tokens from being forever locked. + * + * `data` is additional data, it has no specified format and it is sent in call to `to`. + * + * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. + * implement alternative mechanisms to perform token transfer, such as signature-based. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeTransfer( + address from, + address to, + uint256 tokenId, + bytes memory data + ) internal virtual { + _transfer(from, to, tokenId); + require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer"); + } + + /** + * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist + */ + function _ownerOf(uint256 tokenId) internal view virtual returns (address) { + return _owners[tokenId]; + } + + /** + * @dev Returns whether `tokenId` exists. + * + * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. + * + * Tokens start existing when they are minted (`_mint`), + * and stop existing when they are burned (`_burn`). + */ + function _exists(uint256 tokenId) internal view virtual returns (bool) { + return _ownerOf(tokenId) != address(0); + } + + /** + * @dev Returns whether `spender` is allowed to manage `tokenId`. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { + address owner = ERC721.ownerOf(tokenId); + return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender); + } + + /** + * @dev Safely mints `tokenId` and transfers it to `to`. + * + * Requirements: + * + * - `tokenId` must not exist. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeMint(address to, uint256 tokenId) internal virtual { + _safeMint(to, tokenId, ""); + } + + /** + * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is + * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. + */ + function _safeMint( + address to, + uint256 tokenId, + bytes memory data + ) internal virtual { + _mint(to, tokenId); + require( + _checkOnERC721Received(address(0), to, tokenId, data), + "ERC721: transfer to non ERC721Receiver implementer" + ); + } + + /** + * @dev Mints `tokenId` and transfers it to `to`. + * + * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible + * + * Requirements: + * + * - `tokenId` must not exist. + * - `to` cannot be the zero address. + * + * Emits a {Transfer} event. + */ + function _mint(address to, uint256 tokenId) internal virtual { + require(to != address(0), "ERC721: mint to the zero address"); + require(!_exists(tokenId), "ERC721: token already minted"); + + _beforeTokenTransfer(address(0), to, tokenId, 1); + + // Check that tokenId was not minted by `_beforeTokenTransfer` hook + require(!_exists(tokenId), "ERC721: token already minted"); + + unchecked { + // Will not overflow unless all 2**256 token ids are minted to the same owner. + // Given that tokens are minted one by one, it is impossible in practice that + // this ever happens. Might change if we allow batch minting. + // The ERC fails to describe this case. + _balances[to] += 1; + } + + _owners[tokenId] = to; + + emit Transfer(address(0), to, tokenId); + + _afterTokenTransfer(address(0), to, tokenId, 1); + } + + /** + * @dev Destroys `tokenId`. + * The approval is cleared when the token is burned. + * This is an internal function that does not check if the sender is authorized to operate on the token. + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits a {Transfer} event. + */ + function _burn(uint256 tokenId) internal virtual { + address owner = ERC721.ownerOf(tokenId); + + _beforeTokenTransfer(owner, address(0), tokenId, 1); + + // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook + owner = ERC721.ownerOf(tokenId); + + // Clear approvals + delete _tokenApprovals[tokenId]; + + unchecked { + // Cannot overflow, as that would require more tokens to be burned/transferred + // out than the owner initially received through minting and transferring in. + _balances[owner] -= 1; + } + delete _owners[tokenId]; + + emit Transfer(owner, address(0), tokenId); + + _afterTokenTransfer(owner, address(0), tokenId, 1); + } + + /** + * @dev Transfers `tokenId` from `from` to `to`. + * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * + * Emits a {Transfer} event. + */ + function _transfer( + address from, + address to, + uint256 tokenId + ) internal virtual { + require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); + require(to != address(0), "ERC721: transfer to the zero address"); + + _beforeTokenTransfer(from, to, tokenId, 1); + + // Check that tokenId was not transferred by `_beforeTokenTransfer` hook + require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); + + // Clear approvals from the previous owner + delete _tokenApprovals[tokenId]; + + unchecked { + // `_balances[from]` cannot overflow for the same reason as described in `_burn`: + // `from`'s balance is the number of token held, which is at least one before the current + // transfer. + // `_balances[to]` could overflow in the conditions described in `_mint`. That would require + // all 2**256 token ids to be minted, which in practice is impossible. + _balances[from] -= 1; + _balances[to] += 1; + } + _owners[tokenId] = to; + + emit Transfer(from, to, tokenId); + + _afterTokenTransfer(from, to, tokenId, 1); + } + + /** + * @dev Approve `to` to operate on `tokenId` + * + * Emits an {Approval} event. + */ + function _approve(address to, uint256 tokenId) internal virtual { + _tokenApprovals[tokenId] = to; + emit Approval(ERC721.ownerOf(tokenId), to, tokenId); + } + + /** + * @dev Approve `operator` to operate on all of `owner` tokens + * + * Emits an {ApprovalForAll} event. + */ + function _setApprovalForAll( + address owner, + address operator, + bool approved + ) internal virtual { + require(owner != operator, "ERC721: approve to caller"); + _operatorApprovals[owner][operator] = approved; + emit ApprovalForAll(owner, operator, approved); + } + + /** + * @dev Reverts if the `tokenId` has not been minted yet. + */ + function _requireMinted(uint256 tokenId) internal view virtual { + require(_exists(tokenId), "ERC721: invalid token ID"); + } + + /** + * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. + * The call is not executed if the target address is not a contract. + * + * @param from address representing the previous owner of the given token ID + * @param to target address that will receive the tokens + * @param tokenId uint256 ID of the token to be transferred + * @param data bytes optional data to send along with the call + * @return bool whether the call correctly returned the expected magic value + */ + function _checkOnERC721Received( + address from, + address to, + uint256 tokenId, + bytes memory data + ) private returns (bool) { + if (to.isContract()) { + try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) { + return retval == IERC721Receiver.onERC721Received.selector; + } catch (bytes memory reason) { + if (reason.length == 0) { + revert("ERC721: transfer to non ERC721Receiver implementer"); + } else { + /// @solidity memory-safe-assembly + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } else { + return true; + } + } + + /** + * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is + * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`. + * - When `from` is zero, the tokens will be minted for `to`. + * - When `to` is zero, ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * - `batchSize` is non-zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256, /* firstTokenId */ + uint256 batchSize + ) internal virtual { + if (batchSize > 1) { + if (from != address(0)) { + _balances[from] -= batchSize; + } + if (to != address(0)) { + _balances[to] += batchSize; + } + } + } + + /** + * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is + * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`. + * - When `from` is zero, the tokens were minted for `to`. + * - When `to` is zero, ``from``'s tokens were burned. + * - `from` and `to` are never both zero. + * - `batchSize` is non-zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual {} +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol) + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * @dev Required interface of an ERC721 compliant contract. + */ +interface IERC721 is IERC165 { + /** + * @dev Emitted when `tokenId` token is transferred from `from` to `to`. + */ + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. + */ + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. + */ + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /** + * @dev Returns the number of tokens in ``owner``'s account. + */ + function balanceOf(address owner) external view returns (uint256 balance); + + /** + * @dev Returns the owner of the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function ownerOf(uint256 tokenId) external view returns (address owner); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes calldata data + ) external; + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC721 protocol to prevent tokens from being forever locked. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /** + * @dev Transfers `tokenId` token from `from` to `to`. + * + * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 + * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must + * understand this adds an external call which potentially creates a reentrancy vulnerability. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /** + * @dev Gives permission to `to` to transfer `tokenId` token to another account. + * The approval is cleared when the token is transferred. + * + * Only a single account can be approved at a time, so approving the zero address clears previous approvals. + * + * Requirements: + * + * - The caller must own the token or be an approved operator. + * - `tokenId` must exist. + * + * Emits an {Approval} event. + */ + function approve(address to, uint256 tokenId) external; + + /** + * @dev Approve or remove `operator` as an operator for the caller. + * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the caller. + * + * Emits an {ApprovalForAll} event. + */ + function setApprovalForAll(address operator, bool _approved) external; + + /** + * @dev Returns the account approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getApproved(uint256 tokenId) external view returns (address operator); + + /** + * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. + * + * See {setApprovalForAll} + */ + function isApprovedForAll(address owner, address operator) external view returns (bool); +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol) + +pragma solidity ^0.8.0; + +/** + * @title ERC721 token receiver interface + * @dev Interface for any contract that wants to support safeTransfers + * from ERC721 asset contracts. + */ +interface IERC721Receiver { + /** + * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} + * by `operator` from `from`, this function is called. + * + * It must return its Solidity selector to confirm the token transfer. + * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. + * + * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. + */ + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external returns (bytes4); +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Burnable.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../../utils/Context.sol"; + +/** + * @title ERC721 Burnable Token + * @dev ERC721 Token that can be burned (destroyed). + */ +abstract contract ERC721Burnable is Context, ERC721 { + /** + * @dev Burns `tokenId`. See {ERC721-_burn}. + * + * Requirements: + * + * - The caller must own `tokenId` or be an approved operator. + */ + function burn(uint256 tokenId) public virtual { + //solhint-disable-next-line max-line-length + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); + _burn(tokenId); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Enumerable.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "./IERC721Enumerable.sol"; + +/** + * @dev This implements an optional extension of {ERC721} defined in the EIP that adds + * enumerability of all the token ids in the contract as well as all token ids owned by each + * account. + */ +abstract contract ERC721Enumerable is ERC721, IERC721Enumerable { + // Mapping from owner to list of owned token IDs + mapping(address => mapping(uint256 => uint256)) private _ownedTokens; + + // Mapping from token ID to index of the owner tokens list + mapping(uint256 => uint256) private _ownedTokensIndex; + + // Array with all token ids, used for enumeration + uint256[] private _allTokens; + + // Mapping from token id to position in the allTokens array + mapping(uint256 => uint256) private _allTokensIndex; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { + return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. + */ + function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { + require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds"); + return _ownedTokens[owner][index]; + } + + /** + * @dev See {IERC721Enumerable-totalSupply}. + */ + function totalSupply() public view virtual override returns (uint256) { + return _allTokens.length; + } + + /** + * @dev See {IERC721Enumerable-tokenByIndex}. + */ + function tokenByIndex(uint256 index) public view virtual override returns (uint256) { + require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds"); + return _allTokens[index]; + } + + /** + * @dev See {ERC721-_beforeTokenTransfer}. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override { + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + + if (batchSize > 1) { + // Will only trigger during construction. Batch transferring (minting) is not available afterwards. + revert("ERC721Enumerable: consecutive transfers not supported"); + } + + uint256 tokenId = firstTokenId; + + if (from == address(0)) { + _addTokenToAllTokensEnumeration(tokenId); + } else if (from != to) { + _removeTokenFromOwnerEnumeration(from, tokenId); + } + if (to == address(0)) { + _removeTokenFromAllTokensEnumeration(tokenId); + } else if (to != from) { + _addTokenToOwnerEnumeration(to, tokenId); + } + } + + /** + * @dev Private function to add a token to this extension's ownership-tracking data structures. + * @param to address representing the new owner of the given token ID + * @param tokenId uint256 ID of the token to be added to the tokens list of the given address + */ + function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { + uint256 length = ERC721.balanceOf(to); + _ownedTokens[to][length] = tokenId; + _ownedTokensIndex[tokenId] = length; + } + + /** + * @dev Private function to add a token to this extension's token tracking data structures. + * @param tokenId uint256 ID of the token to be added to the tokens list + */ + function _addTokenToAllTokensEnumeration(uint256 tokenId) private { + _allTokensIndex[tokenId] = _allTokens.length; + _allTokens.push(tokenId); + } + + /** + * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that + * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for + * gas optimizations e.g. when performing a transfer operation (avoiding double writes). + * This has O(1) time complexity, but alters the order of the _ownedTokens array. + * @param from address representing the previous owner of the given token ID + * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address + */ + function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { + // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = ERC721.balanceOf(from) - 1; + uint256 tokenIndex = _ownedTokensIndex[tokenId]; + + // When the token to delete is the last token, the swap operation is unnecessary + if (tokenIndex != lastTokenIndex) { + uint256 lastTokenId = _ownedTokens[from][lastTokenIndex]; + + _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + } + + // This also deletes the contents at the last position of the array + delete _ownedTokensIndex[tokenId]; + delete _ownedTokens[from][lastTokenIndex]; + } + + /** + * @dev Private function to remove a token from this extension's token tracking data structures. + * This has O(1) time complexity, but alters the order of the _allTokens array. + * @param tokenId uint256 ID of the token to be removed from the tokens list + */ + function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { + // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = _allTokens.length - 1; + uint256 tokenIndex = _allTokensIndex[tokenId]; + + // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so + // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding + // an 'if' statement (like in _removeTokenFromOwnerEnumeration) + uint256 lastTokenId = _allTokens[lastTokenIndex]; + + _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + + // This also deletes the contents at the last position of the array + delete _allTokensIndex[tokenId]; + _allTokens.pop(); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721URIStorage.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; + +/** + * @dev ERC721 token with storage based token URI management. + */ +abstract contract ERC721URIStorage is ERC721 { + using Strings for uint256; + + // Optional mapping for token URIs + mapping(uint256 => string) private _tokenURIs; + + /** + * @dev See {IERC721Metadata-tokenURI}. + */ + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { + _requireMinted(tokenId); + + string memory _tokenURI = _tokenURIs[tokenId]; + string memory base = _baseURI(); + + // If there is no base URI, return the token URI. + if (bytes(base).length == 0) { + return _tokenURI; + } + // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). + if (bytes(_tokenURI).length > 0) { + return string(abi.encodePacked(base, _tokenURI)); + } + + return super.tokenURI(tokenId); + } + + /** + * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { + require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token"); + _tokenURIs[tokenId] = _tokenURI; + } + + /** + * @dev See {ERC721-_burn}. This override additionally checks to see if a + * token-specific URI was set for the token, and if so, it deletes the token URI from + * the storage mapping. + */ + function _burn(uint256 tokenId) internal virtual override { + super._burn(tokenId); + + if (bytes(_tokenURIs[tokenId]).length != 0) { + delete _tokenURIs[tokenId]; + } + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol) + +pragma solidity ^0.8.0; + +import "../IERC721.sol"; + +/** + * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Enumerable is IERC721 { + /** + * @dev Returns the total amount of tokens stored by the contract. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns a token ID owned by `owner` at a given `index` of its token list. + * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. + */ + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + + /** + * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. + * Use along with {totalSupply} to enumerate all tokens. + */ + function tokenByIndex(uint256 index) external view returns (uint256); +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) + +pragma solidity ^0.8.0; + +import "../IERC721.sol"; + +/** + * @title ERC-721 Non-Fungible Token Standard, optional metadata extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Metadata is IERC721 { + /** + * @dev Returns the token collection name. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the token collection symbol. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. + */ + function tokenURI(uint256 tokenId) external view returns (string memory); +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Address.sol @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) + +pragma solidity ^0.8.1; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + * + * [IMPORTANT] + * ==== + * You shouldn't rely on `isContract` to protect against flash loan attacks! + * + * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets + * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract + * constructor. + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize/address.code.length, which returns 0 + // for contracts in construction, since the code is only stored at the end + // of the constructor execution. + + return account.code.length > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + (bool success, ) = recipient.call{value: amount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + (bool success, bytes memory returndata) = target.call{value: value}(data); + return verifyCallResultFromTarget(target, success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + (bool success, bytes memory returndata) = target.staticcall(data); + return verifyCallResultFromTarget(target, success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + (bool success, bytes memory returndata) = target.delegatecall(data); + return verifyCallResultFromTarget(target, success, returndata, errorMessage); + } + + /** + * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling + * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. + * + * _Available since v4.8._ + */ + function verifyCallResultFromTarget( + address target, + bool success, + bytes memory returndata, + string memory errorMessage + ) internal view returns (bytes memory) { + if (success) { + if (returndata.length == 0) { + // only check isContract if the call was successful and the return data is empty + // otherwise we already know that it was a contract + require(isContract(target), "Address: call to non-contract"); + } + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + /** + * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the + * revert reason or using the provided one. + * + * _Available since v4.3._ + */ + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + function _revert(bytes memory returndata, string memory errorMessage) private pure { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + /// @solidity memory-safe-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Context.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Counters.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) + +pragma solidity ^0.8.0; + +/** + * @title Counters + * @author Matt Condon (@shrugs) + * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number + * of elements in a mapping, issuing ERC721 ids, or counting request ids. + * + * Include with `using Counters for Counters.Counter;` + */ +library Counters { + struct Counter { + // This variable should never be directly accessed by users of the library: interactions must be restricted to + // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add + // this feature: see https://github.com/ethereum/solidity/issues/4637 + uint256 _value; // default: 0 + } + + function current(Counter storage counter) internal view returns (uint256) { + return counter._value; + } + + function increment(Counter storage counter) internal { + unchecked { + counter._value += 1; + } + } + + function decrement(Counter storage counter) internal { + uint256 value = counter._value; + require(value > 0, "Counter: decrement overflow"); + unchecked { + counter._value = value - 1; + } + } + + function reset(Counter storage counter) internal { + counter._value = 0; + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Strings.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) + +pragma solidity ^0.8.0; + +import "./math/Math.sol"; + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant _SYMBOLS = "0123456789abcdef"; + uint8 private constant _ADDRESS_LENGTH = 20; + + /** + * @dev Converts a `uint256` to its ASCII `string` decimal representation. + */ + function toString(uint256 value) internal pure returns (string memory) { + unchecked { + uint256 length = Math.log10(value) + 1; + string memory buffer = new string(length); + uint256 ptr; + /// @solidity memory-safe-assembly + assembly { + ptr := add(buffer, add(32, length)) + } + while (true) { + ptr--; + /// @solidity memory-safe-assembly + assembly { + mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) + } + value /= 10; + if (value == 0) break; + } + return buffer; + } + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. + */ + function toHexString(uint256 value) internal pure returns (string memory) { + unchecked { + return toHexString(value, Math.log256(value) + 1); + } + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + */ + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = _SYMBOLS[value & 0xf]; + value >>= 4; + } + require(value == 0, "Strings: hex length insufficient"); + return string(buffer); + } + + /** + * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. + */ + function toHexString(address addr) internal pure returns (string memory) { + return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/ERC165.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +/** + * @dev Implementation of the {IERC165} interface. + * + * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check + * for the additional interface id that will be supported. For example: + * + * ```solidity + * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); + * } + * ``` + * + * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. + */ +abstract contract ERC165 is IERC165 { + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IERC165).interfaceId; + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/IERC165.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC165 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-165[EIP]. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others ({ERC165Checker}). + * + * For an implementation, see {ERC165}. + */ +interface IERC165 { + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] + * to learn more about how these ids are created. + * + * This function call must use less than 30 000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/math/Math.sol @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Standard math utilities missing in the Solidity language. + */ +library Math { + enum Rounding { + Down, // Toward negative infinity + Up, // Toward infinity + Zero // Toward zero + } + + /** + * @dev Returns the largest of two numbers. + */ + function max(uint256 a, uint256 b) internal pure returns (uint256) { + return a > b ? a : b; + } + + /** + * @dev Returns the smallest of two numbers. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } + + /** + * @dev Returns the average of two numbers. The result is rounded towards + * zero. + */ + function average(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b) / 2 can overflow. + return (a & b) + (a ^ b) / 2; + } + + /** + * @dev Returns the ceiling of the division of two numbers. + * + * This differs from standard division with `/` in that it rounds up instead + * of rounding down. + */ + function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b - 1) / b can overflow on addition, so we distribute. + return a == 0 ? 0 : (a - 1) / b + 1; + } + + /** + * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) + * with further edits by Uniswap Labs also under MIT license. + */ + function mulDiv( + uint256 x, + uint256 y, + uint256 denominator + ) internal pure returns (uint256 result) { + unchecked { + // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use + // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 + // variables such that product = prod1 * 2^256 + prod0. + uint256 prod0; // Least significant 256 bits of the product + uint256 prod1; // Most significant 256 bits of the product + assembly { + let mm := mulmod(x, y, not(0)) + prod0 := mul(x, y) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + // Handle non-overflow cases, 256 by 256 division. + if (prod1 == 0) { + return prod0 / denominator; + } + + // Make sure the result is less than 2^256. Also prevents denominator == 0. + require(denominator > prod1); + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [prod1 prod0]. + uint256 remainder; + assembly { + // Compute remainder using mulmod. + remainder := mulmod(x, y, denominator) + + // Subtract 256 bit number from 512 bit number. + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. + // See https://cs.stackexchange.com/q/138556/92363. + + // Does not overflow because the denominator cannot be zero at this stage in the function. + uint256 twos = denominator & (~denominator + 1); + assembly { + // Divide denominator by twos. + denominator := div(denominator, twos) + + // Divide [prod1 prod0] by twos. + prod0 := div(prod0, twos) + + // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. + twos := add(div(sub(0, twos), twos), 1) + } + + // Shift in bits from prod1 into prod0. + prod0 |= prod1 * twos; + + // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such + // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for + // four bits. That is, denominator * inv = 1 mod 2^4. + uint256 inverse = (3 * denominator) ^ 2; + + // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works + // in modular arithmetic, doubling the correct bits in each step. + inverse *= 2 - denominator * inverse; // inverse mod 2^8 + inverse *= 2 - denominator * inverse; // inverse mod 2^16 + inverse *= 2 - denominator * inverse; // inverse mod 2^32 + inverse *= 2 - denominator * inverse; // inverse mod 2^64 + inverse *= 2 - denominator * inverse; // inverse mod 2^128 + inverse *= 2 - denominator * inverse; // inverse mod 2^256 + + // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. + // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is + // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 + // is no longer required. + result = prod0 * inverse; + return result; + } + } + + /** + * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. + */ + function mulDiv( + uint256 x, + uint256 y, + uint256 denominator, + Rounding rounding + ) internal pure returns (uint256) { + uint256 result = mulDiv(x, y, denominator); + if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { + result += 1; + } + return result; + } + + /** + * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. + * + * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). + */ + function sqrt(uint256 a) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + + // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. + // + // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have + // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. + // + // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` + // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` + // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` + // + // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. + uint256 result = 1 << (log2(a) >> 1); + + // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, + // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at + // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision + // into the expected uint128 result. + unchecked { + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + return min(result, a / result); + } + } + + /** + * @notice Calculates sqrt(a), following the selected rounding direction. + */ + function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = sqrt(a); + return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); + } + } + + /** + * @dev Return the log in base 2, rounded down, of a positive value. + * Returns 0 if given 0. + */ + function log2(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >> 128 > 0) { + value >>= 128; + result += 128; + } + if (value >> 64 > 0) { + value >>= 64; + result += 64; + } + if (value >> 32 > 0) { + value >>= 32; + result += 32; + } + if (value >> 16 > 0) { + value >>= 16; + result += 16; + } + if (value >> 8 > 0) { + value >>= 8; + result += 8; + } + if (value >> 4 > 0) { + value >>= 4; + result += 4; + } + if (value >> 2 > 0) { + value >>= 2; + result += 2; + } + if (value >> 1 > 0) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 2, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log2(value); + return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); + } + } + + /** + * @dev Return the log in base 10, rounded down, of a positive value. + * Returns 0 if given 0. + */ + function log10(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >= 10**64) { + value /= 10**64; + result += 64; + } + if (value >= 10**32) { + value /= 10**32; + result += 32; + } + if (value >= 10**16) { + value /= 10**16; + result += 16; + } + if (value >= 10**8) { + value /= 10**8; + result += 8; + } + if (value >= 10**4) { + value /= 10**4; + result += 4; + } + if (value >= 10**2) { + value /= 10**2; + result += 2; + } + if (value >= 10**1) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 10, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log10(value); + return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); + } + } + + /** + * @dev Return the log in base 256, rounded down, of a positive value. + * Returns 0 if given 0. + * + * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. + */ + function log256(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >> 128 > 0) { + value >>= 128; + result += 16; + } + if (value >> 64 > 0) { + value >>= 64; + result += 8; + } + if (value >> 32 > 0) { + value >>= 32; + result += 4; + } + if (value >> 16 > 0) { + value >>= 16; + result += 2; + } + if (value >> 8 > 0) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 10, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log256(value); + return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); + } + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/ZeppelinContract.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import "./@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; +import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; +import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; +import "./@openzeppelin/contracts/access/Ownable.sol"; +import "./@openzeppelin/contracts/utils/Counters.sol"; + +contract ZeppelinContract is ERC721, ERC721Enumerable, ERC721URIStorage, ERC721Burnable, Ownable { + using Counters for Counters.Counter; + + Counters.Counter private _tokenIdCounter; + + constructor() ERC721("ZeppelinContract", "UNQ") {} + + function _baseURI() internal pure override returns (string memory) { + return "test"; + } + + function safeMint(address to, string memory uri) public onlyOwner { + uint256 tokenId = _tokenIdCounter.current(); + _tokenIdCounter.increment(); + _safeMint(to, tokenId); + _setTokenURI(tokenId, uri); + } + + // The following functions are overrides required by Solidity. + + function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) + internal + override(ERC721, ERC721Enumerable) + { + super._beforeTokenTransfer(from, to, tokenId, batchSize); + } + + function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) { + super._burn(tokenId); + } + + function tokenURI(uint256 tokenId) + public + view + override(ERC721, ERC721URIStorage) + returns (string memory) + { + return super.tokenURI(tokenId); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(ERC721, ERC721Enumerable) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.abi @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"string","name":"uri","type":"string"}],"name":"safeMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.bin @@ -0,0 +1 @@ +60806040523480156200001157600080fd5b506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525060405180604001604052806003815260200162554e5160e81b815250816000908162000068919062000195565b50600162000077828262000195565b505050620000946200008e6200009a60201b60201c565b6200009e565b62000261565b3390565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200011b57607f821691505b6020821081036200013c57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200019057600081815260208120601f850160051c810160208610156200016b5750805b601f850160051c820191505b818110156200018c5782815560010162000177565b5050505b505050565b81516001600160401b03811115620001b157620001b1620000f0565b620001c981620001c2845462000106565b8462000142565b602080601f831160018114620002015760008415620001e85750858301515b600019600386901b1c1916600185901b1785556200018c565b600085815260208120601f198616915b82811015620002325788860151825594840194600190910190840162000211565b5085821015620002515787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b611f0d80620002716000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c80636352211e116100b8578063a22cb4651161007c578063a22cb46514610271578063b88d4fde14610284578063c87b56dd14610297578063d204c45e146102aa578063e985e9c5146102bd578063f2fde38b146102f957600080fd5b80636352211e1461022a57806370a082311461023d578063715018a6146102505780638da5cb5b1461025857806395d89b411461026957600080fd5b806323b872dd116100ff57806323b872dd146101cb5780632f745c59146101de57806342842e0e146101f157806342966c68146102045780634f6ccce71461021757600080fd5b806301ffc9a71461013c57806306fdde0314610164578063081812fc14610179578063095ea7b3146101a457806318160ddd146101b9575b600080fd5b61014f61014a3660046118ab565b61030c565b60405190151581526020015b60405180910390f35b61016c61031d565b60405161015b9190611918565b61018c61018736600461192b565b6103af565b6040516001600160a01b03909116815260200161015b565b6101b76101b2366004611960565b6103d6565b005b6008545b60405190815260200161015b565b6101b76101d936600461198a565b6104f0565b6101bd6101ec366004611960565b610522565b6101b76101ff36600461198a565b6105b8565b6101b761021236600461192b565b6105d3565b6101bd61022536600461192b565b610604565b61018c61023836600461192b565b610697565b6101bd61024b3660046119c6565b6106f7565b6101b761077d565b600b546001600160a01b031661018c565b61016c610791565b6101b761027f3660046119e1565b6107a0565b6101b7610292366004611aa9565b6107af565b61016c6102a536600461192b565b6107e7565b6101b76102b8366004611b25565b6107f2565b61014f6102cb366004611b87565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6101b76103073660046119c6565b610829565b60006103178261089f565b92915050565b60606000805461032c90611bba565b80601f016020809104026020016040519081016040528092919081815260200182805461035890611bba565b80156103a55780601f1061037a576101008083540402835291602001916103a5565b820191906000526020600020905b81548152906001019060200180831161038857829003601f168201915b5050505050905090565b60006103ba826108c4565b506000908152600460205260409020546001600160a01b031690565b60006103e182610697565b9050806001600160a01b0316836001600160a01b0316036104535760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b038216148061046f575061046f81336102cb565b6104e15760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c000000606482015260840161044a565b6104eb8383610923565b505050565b6104fb335b82610991565b6105175760405162461bcd60e51b815260040161044a90611bf4565b6104eb838383610a10565b600061052d836106f7565b821061058f5760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b606482015260840161044a565b506001600160a01b03919091166000908152600660209081526040808320938352929052205490565b6104eb838383604051806020016040528060008152506107af565b6105dc336104f5565b6105f85760405162461bcd60e51b815260040161044a90611bf4565b61060181610b81565b50565b600061060f60085490565b82106106725760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b606482015260840161044a565b6008828154811061068557610685611c41565b90600052602060002001549050919050565b6000818152600260205260408120546001600160a01b0316806103175760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b604482015260640161044a565b60006001600160a01b0382166107615760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b606482015260840161044a565b506001600160a01b031660009081526003602052604090205490565b610785610b8a565b61078f6000610be4565b565b60606001805461032c90611bba565b6107ab338383610c36565b5050565b6107b93383610991565b6107d55760405162461bcd60e51b815260040161044a90611bf4565b6107e184848484610d04565b50505050565b606061031782610d37565b6107fa610b8a565b6000610805600c5490565b9050610815600c80546001019055565b61081f8382610e4b565b6104eb8183610e65565b610831610b8a565b6001600160a01b0381166108965760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161044a565b61060181610be4565b60006001600160e01b0319821663780e9d6360e01b1480610317575061031782610ef8565b6000818152600260205260409020546001600160a01b03166106015760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b604482015260640161044a565b600081815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061095882610697565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b60008061099d83610697565b9050806001600160a01b0316846001600160a01b031614806109e457506001600160a01b0380821660009081526005602090815260408083209388168352929052205460ff165b80610a085750836001600160a01b03166109fd846103af565b6001600160a01b0316145b949350505050565b826001600160a01b0316610a2382610697565b6001600160a01b031614610a495760405162461bcd60e51b815260040161044a90611c57565b6001600160a01b038216610aab5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b606482015260840161044a565b610ab88383836001610f48565b826001600160a01b0316610acb82610697565b6001600160a01b031614610af15760405162461bcd60e51b815260040161044a90611c57565b600081815260046020908152604080832080546001600160a01b03199081169091556001600160a01b0387811680865260038552838620805460001901905590871680865283862080546001019055868652600290945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b61060181610f54565b600b546001600160a01b0316331461078f5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161044a565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b816001600160a01b0316836001600160a01b031603610c975760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c657200000000000000604482015260640161044a565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b610d0f848484610a10565b610d1b84848484610f94565b6107e15760405162461bcd60e51b815260040161044a90611c9c565b6060610d42826108c4565b6000828152600a602052604081208054610d5b90611bba565b80601f0160208091040260200160405190810160405280929190818152602001828054610d8790611bba565b8015610dd45780601f10610da957610100808354040283529160200191610dd4565b820191906000526020600020905b815481529060010190602001808311610db757829003601f168201915b505050505090506000610dfe6040805180820190915260048152631d195cdd60e21b602082015290565b90508051600003610e10575092915050565b815115610e42578082604051602001610e2a929190611cee565b60405160208183030381529060405292505050919050565b610a0884611095565b6107ab828260405180602001604052806000815250611115565b6000828152600260205260409020546001600160a01b0316610ee05760405162461bcd60e51b815260206004820152602e60248201527f45524337323155524953746f726167653a2055524920736574206f66206e6f6e60448201526d32bc34b9ba32b73a103a37b5b2b760911b606482015260840161044a565b6000828152600a602052604090206104eb8282611d6b565b60006001600160e01b031982166380ac58cd60e01b1480610f2957506001600160e01b03198216635b5e139f60e01b145b8061031757506301ffc9a760e01b6001600160e01b0319831614610317565b6107e184848484611148565b610f5d81611288565b6000818152600a602052604090208054610f7690611bba565b159050610601576000818152600a6020526040812061060191611847565b60006001600160a01b0384163b1561108a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290610fd8903390899088908890600401611e2b565b6020604051808303816000875af1925050508015611013575060408051601f3d908101601f1916820190925261101091810190611e68565b60015b611070573d808015611041576040519150601f19603f3d011682016040523d82523d6000602084013e611046565b606091505b5080516000036110685760405162461bcd60e51b815260040161044a90611c9c565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610a08565b506001949350505050565b60606110a0826108c4565b60006110c36040805180820190915260048152631d195cdd60e21b602082015290565b905060008151116110e3576040518060200160405280600081525061110e565b806110ed8461132b565b6040516020016110fe929190611cee565b6040516020818303038152906040525b9392505050565b61111f83836113be565b61112c6000848484610f94565b6104eb5760405162461bcd60e51b815260040161044a90611c9c565b61115484848484611557565b60018111156111c35760405162461bcd60e51b815260206004820152603560248201527f455243373231456e756d657261626c653a20636f6e7365637574697665207472604482015274185b9cd9995c9cc81b9bdd081cdd5c1c1bdc9d1959605a1b606482015260840161044a565b816001600160a01b03851661121f5761121a81600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b611242565b836001600160a01b0316856001600160a01b0316146112425761124285826115df565b6001600160a01b03841661125e576112598161167c565b611281565b846001600160a01b0316846001600160a01b03161461128157611281848261172b565b5050505050565b600061129382610697565b90506112a3816000846001610f48565b6112ac82610697565b600083815260046020908152604080832080546001600160a01b03199081169091556001600160a01b0385168085526003845282852080546000190190558785526002909352818420805490911690555192935084927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b606060006113388361176f565b600101905060008167ffffffffffffffff81111561135857611358611a1d565b6040519080825280601f01601f191660200182016040528015611382576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461138c57509392505050565b6001600160a01b0382166114145760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015260640161044a565b6000818152600260205260409020546001600160a01b0316156114795760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640161044a565b611487600083836001610f48565b6000818152600260205260409020546001600160a01b0316156114ec5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640161044a565b6001600160a01b038216600081815260036020908152604080832080546001019055848352600290915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60018111156107e1576001600160a01b0384161561159d576001600160a01b03841660009081526003602052604081208054839290611597908490611e9b565b90915550505b6001600160a01b038316156107e1576001600160a01b038316600090815260036020526040812080548392906115d4908490611eae565b909155505050505050565b600060016115ec846106f7565b6115f69190611e9b565b600083815260076020526040902054909150808214611649576001600160a01b03841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b5060009182526007602090815260408084208490556001600160a01b039094168352600681528383209183525290812055565b60085460009061168e90600190611e9b565b600083815260096020526040812054600880549394509092849081106116b6576116b6611c41565b9060005260206000200154905080600883815481106116d7576116d7611c41565b600091825260208083209091019290925582815260099091526040808220849055858252812055600880548061170f5761170f611ec1565b6001900381819060005260206000200160009055905550505050565b6000611736836106f7565b6001600160a01b039093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106117ae5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef810000000083106117da576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106117f857662386f26fc10000830492506010015b6305f5e1008310611810576305f5e100830492506008015b612710831061182457612710830492506004015b60648310611836576064830492506002015b600a83106103175760010192915050565b50805461185390611bba565b6000825580601f10611863575050565b601f01602090049060005260206000209081019061060191905b80821115611891576000815560010161187d565b5090565b6001600160e01b03198116811461060157600080fd5b6000602082840312156118bd57600080fd5b813561110e81611895565b60005b838110156118e35781810151838201526020016118cb565b50506000910152565b600081518084526119048160208601602086016118c8565b601f01601f19169290920160200192915050565b60208152600061110e60208301846118ec565b60006020828403121561193d57600080fd5b5035919050565b80356001600160a01b038116811461195b57600080fd5b919050565b6000806040838503121561197357600080fd5b61197c83611944565b946020939093013593505050565b60008060006060848603121561199f57600080fd5b6119a884611944565b92506119b660208501611944565b9150604084013590509250925092565b6000602082840312156119d857600080fd5b61110e82611944565b600080604083850312156119f457600080fd5b6119fd83611944565b915060208301358015158114611a1257600080fd5b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff80841115611a4e57611a4e611a1d565b604051601f8501601f19908116603f01168101908282118183101715611a7657611a76611a1d565b81604052809350858152868686011115611a8f57600080fd5b858560208301376000602087830101525050509392505050565b60008060008060808587031215611abf57600080fd5b611ac885611944565b9350611ad660208601611944565b925060408501359150606085013567ffffffffffffffff811115611af957600080fd5b8501601f81018713611b0a57600080fd5b611b1987823560208401611a33565b91505092959194509250565b60008060408385031215611b3857600080fd5b611b4183611944565b9150602083013567ffffffffffffffff811115611b5d57600080fd5b8301601f81018513611b6e57600080fd5b611b7d85823560208401611a33565b9150509250929050565b60008060408385031215611b9a57600080fd5b611ba383611944565b9150611bb160208401611944565b90509250929050565b600181811c90821680611bce57607f821691505b602082108103611bee57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252602d908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526c1c881bdc88185c1c1c9bdd9959609a1b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b60208082526025908201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060408201526437bbb732b960d91b606082015260800190565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b60008351611d008184602088016118c8565b835190830190611d148183602088016118c8565b01949350505050565b601f8211156104eb57600081815260208120601f850160051c81016020861015611d445750805b601f850160051c820191505b81811015611d6357828155600101611d50565b505050505050565b815167ffffffffffffffff811115611d8557611d85611a1d565b611d9981611d938454611bba565b84611d1d565b602080601f831160018114611dce5760008415611db65750858301515b600019600386901b1c1916600185901b178555611d63565b600085815260208120601f198616915b82811015611dfd57888601518255948401946001909101908401611dde565b5085821015611e1b5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090611e5e908301846118ec565b9695505050505050565b600060208284031215611e7a57600080fd5b815161110e81611895565b634e487b7160e01b600052601160045260246000fd5b8181038181111561031757610317611e85565b8082018082111561031757610317611e85565b634e487b7160e01b600052603160045260246000fdfea26469706673582212207960bc65d7484ab1502ccdc352c91f0fb91535a525cf99e1b6392e242548279a64736f6c63430008110033 \ No newline at end of file --- /dev/null +++ b/js-packages/scripts/benchmarks/utils/types.ts @@ -0,0 +1,62 @@ +export interface IFeeGas { + fee?: number | bigint, + gas?: number | bigint, + substrate?: number, + zeppelin?: { + fee: number | bigint, + gas: number | bigint, + } + +} +export interface IFeeGasCsv extends IFeeGasVm { + function: string, +} +export interface IFeeGasVm{ + ethFee?: number | bigint, + ethGas?: number | bigint, + substrate?: number, + zeppelinFee?: number | bigint, + zeppelinGas?: number | bigint +} +export interface IFunctionFee { + [name: string]: IFeeGas +} + + +export abstract class FunctionFeeVM { + [name: string]: IFeeGasVm + + static toCsv(model: FunctionFeeVM): IFeeGasCsv[]{ + const res: IFeeGasCsv[] = []; + Object.keys(model).forEach(key => { + res.push({ + function: key, + ethFee: model[key].ethFee, + ethGas: model[key].ethGas, + substrate: model[key].substrate, + zeppelinFee: model[key].zeppelinFee, + zeppelinGas: model[key].zeppelinGas, + }); + }); + return res; + } + static fromModel(model: IFunctionFee): FunctionFeeVM { + const res: FunctionFeeVM = {}; + + Object.keys(model).forEach(key => { + res[key] = {}; + if(model[key].fee && model[key].gas) { + res[key].ethFee = model[key].fee; + res[key].ethGas = model[key].gas; + } + if(model[key].substrate) + res[key].substrate = model[key].substrate; + if(model[key].zeppelin) { + res[key].zeppelinFee = model[key].zeppelin?.fee; + res[key].zeppelinGas = model[key].zeppelin?.gas; + } + }); + + return res; + } +} \ No newline at end of file --- /dev/null +++ b/js-packages/scripts/calibrate.ts @@ -0,0 +1,318 @@ +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingEthPlaygrounds} from '@unique/tests/eth/util/index.js'; +import {EthUniqueHelper} from '@unique/tests//eth/util/playgrounds/unique.dev.js'; + +class Fract { + static ZERO = new Fract(0n); + constructor(public readonly a: bigint, public readonly b: bigint = 1n) { + if(b === 0n) throw new Error('division by zero'); + if(b < 0n) throw new Error('missing normalization'); + } + + mul(other: Fract) { + return new Fract(this.a * other.a, this.b * other.b).optimize(); + } + + div(other: Fract) { + return this.mul(other.inv()); + } + + plus(other: Fract) { + if(this.b === other.b) { + return new Fract(this.a + other.a, this.b); + } + return new Fract(this.a * other.b + other.a * this.b, this.b * other.b).optimize(); + } + + minus(other: Fract) { + return this.plus(other.neg()); + } + + neg() { + return new Fract(-this.a, this.b); + } + inv() { + if(this.a < 0) { + return new Fract(-this.b, -this.a); + } else { + return new Fract(this.b, this.a); + } + } + + optimize() { + function gcd(x: bigint, y: bigint) { + if(x < 0n) + x = -x; + if(y < 0n) + y = -y; + while(y) { + const t = y; + y = x % y; + x = t; + } + return x; + } + const v = gcd(this.a, this.b); + return new Fract(this.a / v, this.b / v); + } + + toBigInt() { + return this.a / this.b; + } + toNumber() { + const v = this.optimize(); + return Number(v.a) / Number(v.b); + } + toString() { + const v = this.optimize(); + return `${v.a} / ${v.b}`; + } + + lt(other: Fract) { + return this.a * other.b < other.a * this.b; + } + eq(other: Fract) { + return this.a * other.b === other.a * this.b; + } + + sqrt() { + if(this.a < 0n) { + throw new Error('square root of negative numbers is not supported'); + } + + if(this.lt(new Fract(2n))) { + return this; + } + + function newtonIteration(n: Fract, x0: Fract): Fract { + const x1 = rpn(n, x0, '/', x0, '+', new Fract(2n), '/'); + if(x0.eq(x1) || x0.eq(x1.minus(new Fract(1n)))) { + return x0; + } + return newtonIteration(n, x1); + } + + return newtonIteration(this, new Fract(1n)); + } +} + +type Op = Fract | '+' | '-' | '*' | '/' | 'dup' | Op[]; +function rpn(...ops: (Op)[]) { + const stack: Fract[] = []; + for(const op of ops) { + if(op instanceof Fract) { + stack.push(op); + } else if(op === '+') { + if(stack.length < 2) + throw new Error('stack underflow'); + const b = stack.pop()!; + const a = stack.pop()!; + stack.push(a.plus(b)); + } else if(op === '*') { + if(stack.length < 2) + throw new Error('stack underflow'); + const b = stack.pop()!; + const a = stack.pop()!; + stack.push(a.mul(b)); + } else if(op === '-') { + if(stack.length < 2) + throw new Error('stack underflow'); + const b = stack.pop()!; + const a = stack.pop()!; + stack.push(a.minus(b)); + } else if(op === '/') { + if(stack.length < 2) + throw new Error('stack underflow'); + const b = stack.pop()!; + const a = stack.pop()!; + stack.push(a.div(b)); + } else if(op === 'dup') { + if(stack.length < 1) + throw new Error('stack underflow'); + const a = stack.pop()!; + stack.push(a); + stack.push(a); + } else if(Array.isArray(op)) { + stack.push(rpn(...op)); + } else { + throw new Error(`unknown operand: ${op}`); + } + } + if(stack.length != 1) + throw new Error('one element should be left on stack'); + return stack[0]!; +} + +function linearRegression(points: { x: Fract, y: Fract }[]) { + let sumxy = Fract.ZERO; + let sumx = Fract.ZERO; + let sumy = Fract.ZERO; + let sumx2 = Fract.ZERO; + const n = points.length; + for(let i = 0; i < n; i++) { + const p = points[i]; + sumxy = rpn(p.x, p.y, '*', sumxy, '+'); + sumx = sumx.plus(p.x); + sumy = sumy.plus(p.y); + sumx2 = rpn(p.x, p.x, '*', sumx2, '+'); + } + + const nb = new Fract(BigInt(n)); + + const a = rpn( + [nb, sumxy, '*', sumx, sumy, '*', '-'], + [nb, sumx2, '*', sumx, sumx, '*', '-'], + '/', + ); + const b = rpn( + [sumy, a, sumx, '*', '-'], + nb, + '/', + ); + + return {a, b}; +} + +const hypothesisLinear = (a: Fract, b: Fract) => (x: Fract) => rpn(x, a, '*', b, '+'); + +// function error(points: { x: Fract, y: Fract }[], hypothesis: (a: Fract) => Fract) { +// return points.map(p => { +// const v = hypothesis(p.x); +// const vv = p.y; + +// return rpn(v, vv, '-', 'dup', '*'); +// }).reduce((a, b) => a.plus(b), Fract.ZERO).sqrt().div(new Fract(BigInt(points.length))); +// } + +async function calibrateWeightToFee(helper: EthUniqueHelper, privateKey: (account: string) => Promise) { + const alice = await privateKey('//Alice'); + const bob = await privateKey('//Bob'); + const dataPoints = []; + + { + const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); + const token = await collection.mintToken(alice, {Substrate: alice.address}); + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); + await token.transfer(alice, {Substrate: bob.address}); + const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); + + console.log(`\t[NFT transfer] Original price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`); + } + + const api = helper.getApi(); + const base = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt(); + for(let i = -5; i < 5; i++) { + await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(base + base / 1000n * BigInt(i)))); + + const coefficient = new Fract((await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt()); + const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); + const token = await collection.mintToken(alice, {Substrate: alice.address}); + + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); + await token.transfer(alice, {Substrate: bob.address}); + const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); + + const transferPrice = new Fract(aliceBalanceBefore - aliceBalanceAfter); + + dataPoints.push({x: transferPrice, y: coefficient}); + } + const {a, b} = linearRegression(dataPoints); + + const hyp = hypothesisLinear(a, b); + // console.log(`\t[NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`); + + // 0.1 UNQ + const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(1n, 10n), '*')); + await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(perfectValue.toBigInt()))); + + { + const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); + const token = await collection.mintToken(alice, {Substrate: alice.address}); + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); + await token.transfer(alice, {Substrate: bob.address}); + const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); + + console.log(`\t[NFT transfer] Calibrated price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`); + } +} + +async function calibrateMinGasPrice(helper: EthUniqueHelper, privateKey: (account: string) => Promise) { + const alice = await privateKey('//Alice'); + const caller = await helper.eth.createAccountWithBalance(alice); + const receiver = helper.eth.createAccount(); + const dataPoints = []; + + { + const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); + const token = await collection.mintToken(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft', caller); + + const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS})); + + console.log(`\t[ETH NFT transfer] Original price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`); + } + + const api = helper.getApi(); + // const defaultCoeff = (api.consts.configuration.defaultMinGasPrice as any).toBigInt(); + const base = (await api.query.configuration.minGasPriceOverride() as any).toBigInt(); + for(let i = -8; i < 8; i++) { + const gasPrice = base + base / 100000n * BigInt(i); + const gasPriceStr = '0x' + gasPrice.toString(16); + await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(gasPrice))); + + const coefficient = new Fract((await api.query.configuration.minGasPriceOverride() as any).toBigInt()); + const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); + const token = await collection.mintToken(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft', caller); + + const transferPrice = new Fract(await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gasPrice: gasPriceStr, gas: helper.eth.DEFAULT_GAS}))); + + dataPoints.push({x: transferPrice, y: coefficient}); + } + + const {a, b} = linearRegression(dataPoints); + + const hyp = hypothesisLinear(a, b); + // console.log(`\t[ETH NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`); + + // 0.15 UNQ + const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(15n, 100n), '*')); + await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(perfectValue.toBigInt()))); + + { + const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); + const token = await collection.mintToken(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft', caller); + + const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS})); + + console.log(`\t[ETH NFT transfer] Calibrated price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`); + } +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +(async () => { + await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => { + // Subsequent runs reduce error, as price line is not actually straight, this is a curve + + const iterations = 3; + + console.log('[Calibrate WeightToFee]'); + for(let i = 0; i < iterations; i++) { + await calibrateWeightToFee(helper, privateKey); + } + + console.log(); + + console.log('[Calibrate MinGasPrice]'); + for(let i = 0; i < iterations; i++) { + await calibrateMinGasPrice(helper, privateKey); + } + }); +})(); --- /dev/null +++ b/js-packages/scripts/calibrateApply.ts @@ -0,0 +1,40 @@ +import {readFile, writeFile} from 'fs/promises'; +import path from 'path'; +import {makeNames, usingPlaygrounds} from '@unique/tests/util/index.js'; + +const {dirname} = makeNames(import.meta.url); + +const formatNumber = (num: string): string => num.split('').reverse().join('').replace(/([0-9]{3})/g, '$1_').split('').reverse().join('').replace(/^_/, ''); + +(async () => { + let weightToFeeCoefficientOverride: string; + let minGasPriceOverride: string; + await usingPlaygrounds(async (helpers, _privateKey) => { + weightToFeeCoefficientOverride = (await helpers.getApi().query.configuration.weightToFeeCoefficientOverride() as any).toBigInt().toString(); + minGasPriceOverride = (await helpers.getApi().query.configuration.minGasPriceOverride() as any).toBigInt().toString(); + }); + const constantsFile = path.resolve(dirname, '../../primitives/common/src/constants.rs'); + let constants = (await readFile(constantsFile)).toString(); + + let weight2feeFound = false; + constants = constants.replace(/(\/\*\*\/)[0-9_]+(\/\*<\/weight2fee>\*\/)/, (_f, p, s) => { + weight2feeFound = true; + return p+formatNumber(weightToFeeCoefficientOverride)+s; + }); + if(!weight2feeFound) { + throw new Error('failed to find weight2fee marker in source code'); + } + + let minGasPriceFound = false; + constants = constants.replace(/(\/\*\*\/)[0-9_]+(\/\*<\/mingasprice>\*\/)/, (_f, p, s) => { + minGasPriceFound = true; + return p+formatNumber(minGasPriceOverride)+s; + }); + if(!minGasPriceFound) { + throw new Error('failed to find mingasprice marker in source code'); + } + + await writeFile(constantsFile, constants); +})().catch(e => { + console.log(e.stack); +}); --- /dev/null +++ b/js-packages/scripts/fetchMetadata.ts @@ -0,0 +1,40 @@ +import {writeFile} from 'fs/promises'; +import {join} from 'path'; +import {exit} from 'process'; +import {fileURLToPath} from 'url'; + +// TODO: Extract metadata statically with chainql. +const url = process.env.RELAY_OPAL_HTTP_URL || process.env.RELAY_QUARTZ_HTTP_URL || process.env.RELAY_UNIQUE_HTTP_URL || process.env.RELAY_SAPPHIRE_HTTP_URL || 'http://127.0.0.1:9944'; + +const srcDir = fileURLToPath(new URL('.', import.meta.url)); + +for(let i = 0; i < 10; i++) { + try { + console.log(`Trying to fetch metadata, retry ${i + 1}/${10}`); + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + redirect: 'follow', + body: JSON.stringify({ + jsonrpc: '2.0', + id: '1', + method: 'state_getMetadata', + params: [], + }), + }); + const json = await response.json(); + const output = join(srcDir, 'metadata.json'); + console.log(`Received response, saving to ${output}`); + await writeFile(output, JSON.stringify(json)); + exit(0); + } catch (e) { + console.error('Failed to request metadata:'); + console.error(e); + console.error('Waiting 1 minute'); + await new Promise(res => setTimeout(res, 60 * 1000)); + } +} +console.error('Out of retries'); +exit(1); --- /dev/null +++ b/js-packages/scripts/generateEnv.ts @@ -0,0 +1,87 @@ +import {ApiPromise, WsProvider} from '@polkadot/api'; +import {readFile} from 'fs/promises'; +import {join} from 'path'; +import {makeNames} from '@unique/tests/util/index.js'; + +const {dirname} = makeNames(import.meta.url); + +async function fetchVersion(chain: string): Promise { + const api = await ApiPromise.create({provider: new WsProvider(chain)}); + const last = (await api.query.system.lastRuntimeUpgrade()).toJSON(); + await api.disconnect(); + return (last as any).specVersion.toString(); +} + +function setVar(env: string, key: string, value: string): string { + let found = false; + const newEnv = env.replace(new RegExp(`${key}=.+?\n`), () => { + found = true; + return `${key}=${value}\n`; + }); + if(!found) throw new Error(`env key "${key}" is not found`); + return newEnv; +} + +// Fetch and format version string +async function ff(url: string, regex: RegExp, rep: string | ((substring: string, ...params:any[]) => string)): Promise { + const ver = await fetchVersion(url); + if(ver.match(regex) === null) + throw new Error(`bad regex for ${url}`); + return ver.replace(regex, rep as any); +} +function fixupUnique(version: string): string { + if(version === 'release-v930033') + return 'release-v930033-fix-gas-price'; + if(version === 'release-v930034') + return 'release-v930034-fix-gas-price'; + return version; +} + +(async () => { + let env = (await readFile(join(dirname, '../../.env'))).toString(); + await Promise.all([ + ff('wss://rpc.polkadot.io/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'POLKADOT_MAINNET_BRANCH', v)), + ff('wss://statemint-rpc.polkadot.io/', /^(....)$/, 'release-parachains-v$1').then(v => env = setVar(env, 'STATEMINT_BUILD_BRANCH', v)), + ff('wss://acala-rpc-0.aca-api.network/', /^(.)(..)(.)$/, '$1.$2.$3').then(v => env = setVar(env, 'ACALA_BUILD_BRANCH', v)), + ff('wss://wss.api.moonbeam.network/', /^(....)$/, 'runtime-$1').then(v => env = setVar(env, 'MOONBEAM_BUILD_BRANCH', v)), + ff('wss://ws.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'UNIQUE_MAINNET_BRANCH', fixupUnique(v))), + + ff('wss://kusama-rpc.polkadot.io/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'KUSAMA_MAINNET_BRANCH', v)), + ff('wss://statemine-rpc.polkadot.io/', /^(....)$/, 'release-parachains-v$1').then(v => env = setVar(env, 'STATEMINE_BUILD_BRANCH', v)), + ff('wss://karura-rpc-0.aca-api.network/', /^(.)(..)(.)$/, 'release-karura-$1.$2.$3').then(v => env = setVar(env, 'KARURA_BUILD_BRANCH', v)), + ff('wss://wss.api.moonriver.moonbeam.network/', /^(....)$/, 'runtime-$1').then(v => env = setVar(env, 'MOONRIVER_BUILD_BRANCH', v)), + ff('wss://ws-quartz.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'QUARTZ_MAINNET_BRANCH', fixupUnique(v))), + + ff('wss://ws-westend.unique.network/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'UNIQUEWEST_MAINNET_BRANCH', v)), + ff('wss://westmint-rpc.polkadot.io/', /^(....)$/, 'parachains-v$1').then(v => env = setVar(env, 'WESTMINT_BUILD_BRANCH', v)), + ff('wss://ws-opal.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'OPAL_MAINNET_BRANCH', fixupUnique(v))), + + ff('wss://ws-eastend.unique.network/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'UNIQUEEAST_MAINNET_BRANCH', v)), + ff('wss://ws-sapphire.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'SAPPHIRE_MAINNET_BRANCH', fixupUnique(v))), + + ff('wss://rpc.astar.network/', /^(.+)$/, (_, r) => { + switch (r) { + case '55': return 'v5.3.0'; + case '57': return 'v5.4.0'; + case '61': return 'v5.11.0'; + case '66': return 'v5.18.0'; + default: throw new Error('unknown astar branch for runtime ' + r); + } + }).then(v => env = setVar(env, 'ASTAR_BUILD_BRANCH', v)), + ff('wss://shiden.api.onfinality.io/public-ws', /^(.+)$/, (_, r) => { + switch (r) { + case '90': return 'v4.49.0'; + case '96': return 'v5.4.0'; + case '100': return 'v5.10.0'; + case '104': return 'v5.15.0'; + case '106': return 'v5.18.0'; + default: throw new Error('unknown shiden branch for runtime ' + r); + } + }).then(v => env = setVar(env, 'SHIDEN_BUILD_BRANCH', v)), + ]); + console.log(env); +})().catch(e => { + console.error('Fatal'); + console.error(e.stack); + process.exit(1); +}); --- /dev/null +++ b/js-packages/scripts/generate_types/functions.sh @@ -0,0 +1,4 @@ + +function do_rpc { + curl -s --header "Content-Type: application/json" -XPOST --data "{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"$1\",\"params\":[$2]}" "$RPC_URL" +} --- /dev/null +++ b/js-packages/scripts/generate_types/generate_types_package.sh @@ -0,0 +1,208 @@ +#!/usr/bin/env bash + +# 2001 - cat file | cmd is fancier than cmd < file in most cases +# 2002 - bash replacement patterns are awful +# shellcheck disable=SC2001,SC2002 +set -eux + +DIR=$(realpath "$(dirname "$0")") +TEMPLATE=$DIR/types_template +GIT_REPO=git@github.com:UniqueNetwork/unique-types-js.git + +. "$DIR/functions.sh" + +usage() { + echo "Usage: [RPC_URL=http://localhost:9944] $0 <--rc|--release> [--force] [--push] [--rpc-url=http://localhost:9944]" 1>&2 + exit 1 +} + +rc= +release= +force= +push= + +for i in "$@"; do +case $i in + --rc) + rc=1 + if test "$release"; then usage; fi + ;; + --release) + release=1 + if test "$rc"; then usage; fi + ;; + --force) + force=1 + ;; + --push) + push=1 + ;; + --rpc-url=*) + RPC_URL=${i#*=} + ;; + *) + usage + ;; +esac +done + +if test \( ! \( "$rc" -o "$release" \) \) -o \( "${RPC_URL=}" = "" \); then + usage +elif test "$rc"; then + echo "Rc build" +else + echo "Release build" +fi + +cd "$DIR/../../" +yarn polkadot-types + +version=$(do_rpc state_getRuntimeVersion "") +spec_version=$(echo "$version" | jq -r .result.specVersion) +spec_name=$(echo "$version" | jq -r .result.specName) +echo "Spec version: $spec_version, name: $spec_name" + +case $spec_name in + opal) + package_name=@unique-nft/opal-testnet-types + repo_branch=opal-testnet + repo_tag=$repo_branch + ;; + sapphire) + package_name=@unique-nft/sapphire-mainnet-types + repo_branch=sapphire-mainnet + repo_tag=$repo_branch + ;; + quartz) + package_name=@unique-nft/quartz-mainnet-types + repo_branch=quartz-mainnet + repo_tag=$repo_branch + ;; + unique) + package_name=@unique-nft/unique-mainnet-types + repo_branch=master + repo_tag=unique-mainnet + ;; + *) + echo "unknown spec name: $spec_name" + exit 1 + ;; +esac + +if test "$rc" = 1; then + if test "$spec_name" != "opal"; then + echo "rc types can only be based on opal spec" + exit 1 + fi + package_name=@unique-nft/rc-types + repo_branch=rc + repo_tag=$repo_branch +fi + +package_version=${spec_version:0:4}.$(echo "${spec_version:4:4}" | sed 's/^0*//'). +last_patch=NEVER +for tag in $(git ls-remote -t --refs $GIT_REPO | cut -f 2 | sort -r); do + tag_prefix=refs/tags/$repo_tag-v$package_version + if [[ $tag == $tag_prefix* ]]; then + last_patch=${tag#"$tag_prefix"} + break; + fi +done +echo "Package version: ${package_version}X, name: $package_name" +echo "Last published: $package_version$last_patch" + +if test "$last_patch" = "NEVER"; then + new_package_version=${package_version}0 +else + new_package_version=${package_version}$((last_patch+1)) +fi +package_version=${package_version}$last_patch +echo "New package version: $new_package_version" + +pjsapi_ver=^$(cat "$DIR/../package.json" | jq -r '.dependencies."@polkadot/api"' | sed -e "s/^\^//") +tsnode_ver=^$(cat "$DIR/../package.json" | jq -r '.devDependencies."ts-node"' | sed -e "s/^\^//") +ts_ver=^$(cat "$DIR/../package.json" | jq -r '.devDependencies."typescript"' | sed -e "s/^\^//") + +gen=$(mktemp -d) +pushd "$gen" +git clone $GIT_REPO -b $repo_branch --depth 1 . +if test "$last_patch" != "NEVER"; then + git reset --hard "$repo_tag-v$package_version" +fi +git rm -r "*" +popd + +# Using old package_version here, becaue we first check if +# there is any difference between generated and already uplaoded types +cat "$TEMPLATE/package.json" \ +| jq '.private = false' - \ +| jq ".name = \"$package_name\"" - \ +| jq ".version = \"$package_version\"" - \ +| jq ".peerDependencies.\"@polkadot/api\" = \"$pjsapi_ver\"" - \ +| jq ".peerDependencies.\"@polkadot/types\" = \"$pjsapi_ver\"" - \ +| jq ".devDependencies.\"@polkadot/api\" = \"$pjsapi_ver\"" - \ +| jq ".devDependencies.\"@polkadot/types\" = \"$pjsapi_ver\"" - \ +| jq ".devDependencies.\"ts-node\" = \"$tsnode_ver\"" - \ +| jq ".devDependencies.\"typescript\" = \"$ts_ver\"" - \ +> "$gen/package.json" +for file in .gitignore .npmignore README.md tsconfig.json; do + cp "$TEMPLATE/$file" "$gen/" +done +package_name_replacement=$(printf '%s\n' "$package_name" | sed -e 's/[\/&]/\\&/g') +sed -i "s/PKGNAME/$package_name_replacement/" "$gen/README.md" + +rsync -ar --exclude .gitignore types/ "$gen" +for file in "$gen"/augment-* "$gen"/**/types.ts "$gen/registry.ts"; do + sed -i '1s;^;//@ts-nocheck\n;' "$file" +done + +pushd "$gen" +git add . +popd + +pushd "$gen" +if git diff --quiet HEAD && test ! "$force"; then + echo "no changes detected" + exit 0 +fi +popd + +mv "$gen/package.json" "$gen/package.old.json" +cat "$gen/package.old.json" \ +| jq ".version = \"$new_package_version\"" - \ +> "$gen/package.json" +rm "$gen/package.old.json" +pushd "$gen" +git add package.json +popd + +echo "package.json contents:" +cat "$gen/package.json" +echo "overall diff:" +pushd "$gen" +git status +git diff HEAD || true +popd + +# This check is only active if running in interactive terminal +if [ -t 0 ]; then + read -p "Is everything ok at $gen [y/n]? " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborting!" + exit 1 + fi +fi + +pushd "$gen" +yarn +yarn prepublish +git commit -m "chore: upgrade types to v$new_package_version" +git tag --force "$repo_tag-v$new_package_version" +if test "$push" = 1; then + git push --tags --force -u origin HEAD +else + echo "--push not given, origin repo left intact" + echo "To publish manually, go to $gen, and run \"git push --tags --force -u origin HEAD\"" +fi +popd --- /dev/null +++ b/js-packages/scripts/generate_types/types_template/.gitignore @@ -0,0 +1,5 @@ +*.js +*.map +*.d.ts +/node_modules +metadata.json --- /dev/null +++ b/js-packages/scripts/generate_types/types_template/.npmignore @@ -0,0 +1 @@ +/src --- /dev/null +++ b/js-packages/scripts/generate_types/types_template/README.md @@ -0,0 +1,31 @@ +# PKGNAME + +Unique network api types + +Do not edit by hand, those types are generated automatically, and definitions are located in chain repo + +## Using types + +Install library: + +```bash +yarn add --dev PKGNAME +``` + +Replace polkadot.js types with our chain types adding corresponding path override to the tsconfig `compilerOptions.paths` section: + +```json +// in tsconfig.json +{ + "compilerOptions": { + "paths": { + "@polkadot/types/lookup": ["node_modules/PKGNAME/types-lookup"] + } + } +} +``` + +Since polkadot v7 api augmentations not loaded by default, in every file, where you need to access `api.tx`, `api.query`, `api.rpc`, etc; you should explicitly import corresponding augmentation before any other `polkadot.js` related import: +``` +import 'PKGNAME/augment-api'; +``` --- /dev/null +++ b/js-packages/scripts/generate_types/types_template/package.json @@ -0,0 +1,22 @@ +{ + "name": "TODO", + "private": true, + "version": "TODO", + "main": "index.js", + "repository": "git@github.com:UniqueNetwork/unique-types-js.git", + "homepage": "https://unique.network/", + "license": "MIT", + "scripts": { + "prepublish": "tsc -d" + }, + "peerDependencies": { + "@polkadot/api": "TODO", + "@polkadot/types": "TODO" + }, + "devDependencies": { + "@polkadot/api": "TODO", + "@polkadot/types": "TODO", + "ts-node": "TODO", + "typescript": "TODO" + } +} --- /dev/null +++ b/js-packages/scripts/generate_types/types_template/tsconfig.json @@ -0,0 +1,28 @@ +{ + "exclude": [ + "node_modules", + "node_modules/**/*", + "../node_modules/**/*", + "**/node_modules/**/*" + ], + "compilerOptions": { + "target": "ES2020", + "moduleResolution": "node", + "esModuleInterop": true, + "resolveJsonModule": true, + "module": "commonjs", + "sourceMap": true, + "outDir": ".", + "rootDir": ".", + "strict": true, + "paths": { + }, + "skipLibCheck": true, + }, + "include": [ + "**/*", + ], + "lib": [ + "es2017" + ], +} --- /dev/null +++ b/js-packages/scripts/generate_types/wait_for_first_block.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +DIR=$(dirname "$0") + +. "$DIR/functions.sh" + +last_block_id=0 +block_id=0 +function get_block { + block_id_hex=$(do_rpc chain_getHeader | jq -r .result.number) + block_id=$((block_id_hex)) + echo Id = $block_id +} + +function had_new_block { + last_block_id=$block_id + get_block + if (( last_block_id != 0 && block_id > last_block_id )); then + return 0 + fi + return 1 +} + +function reset_check { + last_block_id=0 + block_id=0 +} + +while ! had_new_block; do + echo "Waiting for next block..." + sleep 12 +done +reset_check + +echo "Chain is running, but lets wait for another block after a minute, to avoid startup flakiness." +sleep 60 + +while ! had_new_block; do + echo "Waiting for another block..." + sleep 12 +done + +echo "Chain is running!" --- /dev/null +++ b/js-packages/scripts/proposefasttrack.ts @@ -0,0 +1,42 @@ +import {ApiPromise, WsProvider} from '@polkadot/api'; +import {blake2AsHex} from '@polkadot/util-crypto'; + +async function main() { + const networkUrl = process.argv[2]; + + const wsProvider = new WsProvider(networkUrl); + const api = await ApiPromise.create({provider: wsProvider}); + + const externalDemocracyProposal = (await api.query.democracy.nextExternal() as any).unwrap()[0]; + + let proposalHash; + if(externalDemocracyProposal.isInline) { + proposalHash = blake2AsHex(externalDemocracyProposal.asInline, 256); + } else if(externalDemocracyProposal.isLegacy) { + proposalHash = externalDemocracyProposal.asLegacy.toJSON().hash; + } else { + proposalHash = externalDemocracyProposal.asLookup.toJSON().hash; + } + + const voringPeriod = 7200; + const delay = 10; + + const democracyFastTrack = api.tx.democracy.fastTrack(proposalHash, voringPeriod, delay); + + const techCommThreshold = ((await api.query.technicalCommittee.members()).toJSON() as any[]).length; + const techCommProposal = api.tx.technicalCommittee.propose( + techCommThreshold, + democracyFastTrack.method.toHex(), + democracyFastTrack.encodedLength, + ); + + const encodedCall = techCommProposal.method.toHex(); + + console.log('-----------------'); + console.log('Fast Track Proposal: ', `https://polkadot.js.org/apps/?rpc=${networkUrl}#/extrinsics/decode/${encodedCall}`); + console.log('-----------------'); + + await api.disconnect(); +} + +await main(); --- /dev/null +++ b/js-packages/scripts/proposeupgrade.ts @@ -0,0 +1,53 @@ +import {ApiPromise, WsProvider} from '@polkadot/api'; +import {blake2AsHex} from '@polkadot/util-crypto'; +import {readFileSync} from 'fs'; + +async function main() { + const networkUrl = process.argv[2]; + const wasmFile = process.argv[3]; + + const wsProvider = new WsProvider(networkUrl); + const api = await ApiPromise.create({provider: wsProvider}); + + const wasmFileBytes = readFileSync(wasmFile); + const wasmFileHash = blake2AsHex(wasmFileBytes, 256); + + const authorizeUpgrade = api.tx.parachainSystem.authorizeUpgrade(wasmFileHash, true); + const enableMaintenance = api.tx.maintenance.enable(); + + const councilMembers = (await api.query.council.members()).toJSON() as any[]; + const councilProposalThreshold = Math.floor(councilMembers.length / 2) + 1; + + const democracyProposalContent = api.tx.utility.batchAll([ + authorizeUpgrade.method.toHex(), + enableMaintenance.method.toHex(), + ]).method.toHex(); + + const democracyProposalHash = blake2AsHex(democracyProposalContent, 256); + const democracyProposalPreimage = api.tx.preimage.notePreimage(democracyProposalContent).method.toHex(); + + const democracyProposal = api.tx.democracy.externalProposeDefault({ + Legacy: democracyProposalHash, + }); + + const councilProposal = api.tx.council.propose( + councilProposalThreshold, + democracyProposal, + democracyProposal.method.encodedLength, + ).method.toHex(); + + const proposeUpgradeBatch = api.tx.utility.batchAll([ + democracyProposalPreimage, + councilProposal, + ]); + + const encodedCall = proposeUpgradeBatch.method.toHex(); + + console.log('-----------------'); + console.log('Upgrade Proposal: ', `https://polkadot.js.org/apps/?rpc=${networkUrl}#/extrinsics/decode/${encodedCall}`); + console.log('-----------------'); + + await api.disconnect(); +} + +await main(); --- a/js-packages/scripts/src/benchmarks/mintFee/index.ts +++ /dev/null @@ -1,399 +0,0 @@ -import {usingEthPlaygrounds} from '@unique/tests/src/eth/util/index.js'; -import {EthUniqueHelper} from '@unique/tests/src/eth/util/playgrounds/unique.dev.js'; -import {readFile} from 'fs/promises'; -import type {ICrossAccountId} from '@unique/playgrounds/src/types.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {UniqueNFTCollection} from '@unique/playgrounds/src/unique.js'; -import {Contract} from 'web3-eth-contract'; -import {createObjectCsvWriter} from 'csv-writer'; -import {convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES} from '../utils/common.js'; -import {makeNames} from '@unique/tests/src/util/index.js'; -import type {ContractImports} from '@unique/tests/src/eth/util/playgrounds/types.js'; - -const {dirname} = makeNames(import.meta.url); - -export const CONTRACT_IMPORT: ContractImports[] = [ - { - fsPath: `${dirname}/../../../../tests/src/eth/api/CollectionHelpers.sol`, - solPath: 'eth/api/CollectionHelpers.sol', - }, - { - fsPath: `${dirname}/../../../../tests/src/eth/api/ContractHelpers.sol`, - solPath: 'eth/api/ContractHelpers.sol', - }, - { - fsPath: `${dirname}/../../../../tests/src/eth/api/UniqueRefungibleToken.sol`, - solPath: 'eth/api/UniqueRefungibleToken.sol', - }, - { - fsPath: `${dirname}/../../../../tests/src/eth/api/UniqueRefungible.sol`, - solPath: 'eth/api/UniqueRefungible.sol', - }, - { - fsPath: `${dirname}/../../../../tests/src/eth/api/UniqueNFT.sol`, - solPath: 'eth/api/UniqueNFT.sol', - }, -]; - -interface IBenchmarkResultForProp { - propertiesNumber: number; - substrateFee: number; - ethFee: number; - ethBulkFee: number; - ethMintCrossFee: number; - evmProxyContractFee: number; - evmProxyContractBulkFee: number; -} - -const main = async () => { - const benchmarks = [ - 'substrateFee', - 'ethFee', - 'ethBulkFee', - 'ethMintCrossFee', - 'evmProxyContractFee', - 'evmProxyContractBulkFee', - ]; - const headers = [ - 'propertiesNumber', - ...benchmarks, - ]; - - - const csvWriter = createObjectCsvWriter({ - path: 'properties.csv', - header: headers, - }); - - await usingEthPlaygrounds(async (helper, privateKey) => { - const CONTRACT_SOURCE = ( - await readFile(`${dirname}/proxyContract.sol`) - ).toString(); - - const donor = await privateKey('//Alice'); // Seed from account with balance on this network - const ethSigner = await helper.eth.createAccountWithBalance(donor); - - const contract = await helper.ethContract.deployByCode( - ethSigner, - 'ProxyMint', - CONTRACT_SOURCE, - CONTRACT_IMPORT, - ); - - const fees = await benchMintFee(helper, privateKey, contract); - console.log('Minting without properties'); - console.table(fees); - - const result: IBenchmarkResultForProp[] = []; - const csvResult: IBenchmarkResultForProp[] = []; - - for(let i = 1; i <= 20; i++) { - const benchResult = await benchMintWithProperties(helper, privateKey, contract, { - propertiesNumber: i, - }) as any; - - csvResult.push(benchResult); - - const minFee = Math.min(...(benchmarks.map(x => benchResult[x]))); - for(const key of benchmarks) { - const keyPercent = Math.round((benchResult[key] / minFee) * 100); - benchResult[key] = `${benchResult[key]} (${keyPercent}%)`; - } - - result.push(benchResult); - } - - await csvWriter.writeRecords(csvResult); - - console.log('Minting with properties'); - console.table(result, headers); - }); -}; - -main() - .then(() => process.exit(0)) - .catch((e) => { - console.log(e); - process.exit(1); - }); - - - -async function benchMintFee( - helper: EthUniqueHelper, - privateKey: (seed: string) => Promise, - proxyContract: Contract, -): Promise<{ - substrateFee: number; - ethFee: number; - evmProxyContractFee: number; -}> { - const donor = await privateKey('//Alice'); - const substrateReceiver = await privateKey('//Bob'); - const ethSigner = await helper.eth.createAccountWithBalance(donor); - - const nominal = helper.balance.getOneTokenNominal(); - - await helper.eth.transferBalanceFromSubstrate( - donor, - proxyContract.options.address, - 100n, - ); - - const collection = (await createCollectionForBenchmarks( - 'nft', - helper, - privateKey, - ethSigner, - proxyContract.options.address, - PERMISSIONS, - )) as UniqueNFTCollection; - - const substrateFee = await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.mintToken(donor, {Substrate: substrateReceiver.address}), - ); - - const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionContract = await helper.ethNativeContract.collection( - collectionEthAddress, - 'nft', - ); - - const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address); - - const encodedCall = collectionContract.methods - .mint(receiverEthAddress) - .encodeABI(); - - const ethFee = await helper.arrange.calculcateFee( - {Substrate: donor.address}, - async () => { - await helper.eth.sendEVM( - donor, - collectionContract.options.address, - encodedCall, - '0', - ); - }, - ); - - const evmProxyContractFee = await helper.arrange.calculcateFee( - {Ethereum: ethSigner}, - async () => { - await proxyContract.methods - .mintToSubstrate( - helper.ethAddress.fromCollectionId(collection.collectionId), - substrateReceiver.addressRaw, - ) - .send({from: ethSigner}); - }, - ); - - return { - substrateFee: convertToTokens(substrateFee, nominal), - ethFee: convertToTokens(ethFee, nominal), - evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal), - }; -} - -async function benchMintWithProperties( - helper: EthUniqueHelper, - privateKey: (seed: string) => Promise, - proxyContract: Contract, - setup: { propertiesNumber: number }, -): Promise { - const donor = await privateKey('//Alice'); // Seed from account with balance on this network - const ethSigner = await helper.eth.createAccountWithBalance(donor); - - const susbstrateReceiver = await privateKey('//Bob'); - const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address); - - const nominal = helper.balance.getOneTokenNominal(); - - const substrateFee = await calculateFeeNftMintWithProperties( - helper, - privateKey, - {Substrate: donor.address}, - ethSigner, - proxyContract.options.address, - async (collection) => { - await collection.mintToken( - donor, - {Substrate: susbstrateReceiver.address}, - PROPERTIES.slice(0, setup.propertiesNumber).map((p) => ({key: p.key, value: Buffer.from(p.value).toString()})), - ); - }, - ); - - const ethFee = await calculateFeeNftMintWithProperties( - helper, - privateKey, - {Substrate: donor.address}, - ethSigner, - proxyContract.options.address, - async (collection) => { - const evmContract = await helper.ethNativeContract.collection( - helper.ethAddress.fromCollectionId(collection.collectionId), - 'nft', - undefined, - true, - ); - - const subTokenId = await evmContract.methods.nextTokenId().call(); - - let encodedCall = evmContract.methods - .mint(receiverEthAddress) - .encodeABI(); - - await helper.eth.sendEVM( - donor, - evmContract.options.address, - encodedCall, - '0', - ); - - for(const val of PROPERTIES.slice(0, setup.propertiesNumber)) { - encodedCall = await evmContract.methods - .setProperty(subTokenId, val.key, Buffer.from(val.value)) - .encodeABI(); - - await helper.eth.sendEVM( - donor, - evmContract.options.address, - encodedCall, - '0', - ); - } - }, - ); - - const ethBulkFee = await calculateFeeNftMintWithProperties( - helper, - privateKey, - {Substrate: donor.address}, - ethSigner, - proxyContract.options.address, - async (collection) => { - const evmContract = await helper.ethNativeContract.collection( - helper.ethAddress.fromCollectionId(collection.collectionId), - 'nft', - ); - - const subTokenId = await evmContract.methods.nextTokenId().call(); - - let encodedCall = evmContract.methods - .mint(receiverEthAddress) - .encodeABI(); - - await helper.eth.sendEVM( - donor, - evmContract.options.address, - encodedCall, - '0', - ); - - encodedCall = await evmContract.methods - .setProperties( - subTokenId, - PROPERTIES.slice(0, setup.propertiesNumber), - ) - .encodeABI(); - - await helper.eth.sendEVM( - donor, - evmContract.options.address, - encodedCall, - '0', - ); - }, - ); - - const ethMintCrossFee = await calculateFeeNftMintWithProperties( - helper, - privateKey, - {Ethereum: ethSigner}, - ethSigner, - proxyContract.options.address, - async (collection) => { - const evmContract = await helper.ethNativeContract.collection( - helper.ethAddress.fromCollectionId(collection.collectionId), - 'nft', - ); - - await evmContract.methods.mintCross( - helper.ethCrossAccount.fromAddress(receiverEthAddress), - PROPERTIES.slice(0, setup.propertiesNumber), - ) - .send({from: ethSigner}); - }, - ); - - const proxyContractFee = await calculateFeeNftMintWithProperties( - helper, - privateKey, - {Ethereum: ethSigner}, - ethSigner, - proxyContract.options.address, - async (collection) => { - await proxyContract.methods - .mintToSubstrateWithProperty( - helper.ethAddress.fromCollectionId(collection.collectionId), - susbstrateReceiver.addressRaw, - PROPERTIES.slice(0, setup.propertiesNumber), - ) - .send({from: ethSigner}); - }, - ); - - const proxyContractBulkFee = await calculateFeeNftMintWithProperties( - helper, - privateKey, - {Ethereum: ethSigner}, - ethSigner, - proxyContract.options.address, - async (collection) => { - await proxyContract.methods - .mintToSubstrateBulkProperty( - helper.ethAddress.fromCollectionId(collection.collectionId), - susbstrateReceiver.addressRaw, - PROPERTIES.slice(0, setup.propertiesNumber), - ) - .send({from: ethSigner}); - }, - ); - - return { - propertiesNumber: setup.propertiesNumber, - substrateFee: convertToTokens(substrateFee, nominal), - ethFee: convertToTokens(ethFee, nominal), - ethBulkFee: convertToTokens(ethBulkFee, nominal), - ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal), - evmProxyContractFee: convertToTokens(proxyContractFee, nominal), - evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal), - }; -} - -async function calculateFeeNftMintWithProperties( - helper: EthUniqueHelper, - privateKey: (seed: string) => Promise, - payer: ICrossAccountId, - ethSigner: string, - proxyContractAddress: string, - calculatedCall: (collection: UniqueNFTCollection) => Promise, -): Promise { - const collection = (await createCollectionForBenchmarks( - 'nft', - helper, - privateKey, - ethSigner, - proxyContractAddress, - PERMISSIONS, - )) as UniqueNFTCollection; - return helper.arrange.calculcateFee(payer, async () => { - await calculatedCall(collection); - }); -} - - - --- a/js-packages/scripts/src/benchmarks/mintFee/proxyContract.sol +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-License-Identifier: Apache License -pragma solidity >=0.8.0; -import {CollectionHelpers} from "eth/api/CollectionHelpers.sol"; -import {ContractHelpers} from "eth/api/ContractHelpers.sol"; -import {UniqueRefungibleToken} from "eth/api/UniqueRefungibleToken.sol"; -import {UniqueRefungible, Collection, CrossAddress as RftCrossAccountId, Property as RftProperty} from "eth/api/UniqueRefungible.sol"; -import {UniqueNFT, CrossAddress as NftCrossAccountId, Property as NftProperty} from "eth/api/UniqueNFT.sol"; - -struct Property { - string key; - bytes value; -} - -interface SoftDeprecatedMethods { - /// @notice Set token property value. - /// @dev Throws error if `msg.sender` has no permission to edit the property. - /// @param tokenId ID of the token. - /// @param key Property key. - /// @param value Property value. - /// @dev EVM selector for this function is: 0x1752d67b, - /// or in textual repr: setProperty(uint256,string,bytes) - function setProperty( - uint256 tokenId, - string memory key, - bytes memory value - ) external; -} - -interface BenchUniqueRefungible is UniqueRefungible, SoftDeprecatedMethods {} -interface BenchUniqueNFT is UniqueNFT, SoftDeprecatedMethods {} - - - -contract ProxyMint { - bytes32 constant REFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("ReFungible")); - bytes32 constant NONFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("NFT")); - - modifier checkRestrictions(address _collection) { - Collection commonContract = Collection(_collection); - require( - commonContract.isOwnerOrAdminCross(RftCrossAccountId(msg.sender, 0)), - "Only collection admin/owner can call this method" - ); - _; - } - - /// @dev This emits when a mint to a substrate address has been made. - event MintToSub(address _toEth, uint256 _toSub, address _collection, uint256 _tokenId); - - function mintToSubstrate(address _collection, uint256 _substrateReceiver) external checkRestrictions(_collection) { - Collection commonContract = Collection(_collection); - bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType())); - uint256 tokenId; - - if (collectionType == REFUNGIBLE_COLLECTION_TYPE) { - UniqueRefungible rftCollection = UniqueRefungible(_collection); - - tokenId = rftCollection.mint(address(this)); - - rftCollection.transferFromCross( - RftCrossAccountId(address(this), 0), - RftCrossAccountId(address(0), _substrateReceiver), - tokenId - ); - } else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) { - UniqueNFT nftCollection = UniqueNFT(_collection); - tokenId = nftCollection.mint(address(this)); - - nftCollection.transferFromCross( - NftCrossAccountId(address(this), 0), - NftCrossAccountId(address(0), _substrateReceiver), - tokenId - ); - } else { - revert("Wrong collection type. Works only with NFT or RFT"); - } - - emit MintToSub(address(0), _substrateReceiver, _collection, tokenId); - } - - function mintToSubstrateWithProperty( - address _collection, - uint256 _substrateReceiver, - Property[] calldata _properties - ) external checkRestrictions(_collection) { - uint256 propertiesLength = _properties.length; - require(propertiesLength > 0, "Properies is empty"); - - Collection commonContract = Collection(_collection); - bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType())); - uint256 tokenId; - - if (collectionType == REFUNGIBLE_COLLECTION_TYPE) { - BenchUniqueRefungible rftCollection = BenchUniqueRefungible(_collection); - tokenId = rftCollection.nextTokenId(); - rftCollection.mint(address(this)); - - for (uint256 i = 0; i < propertiesLength; ++i) { - rftCollection.setProperty(tokenId, _properties[i].key, _properties[i].value); - } - rftCollection.transferFromCross( - RftCrossAccountId(address(this), 0), - RftCrossAccountId(address(0), _substrateReceiver), - tokenId - ); - } else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) { - BenchUniqueNFT nftCollection = BenchUniqueNFT(_collection); - tokenId = nftCollection.mint(address(this)); - for (uint256 i = 0; i < propertiesLength; ++i) { - nftCollection.setProperty(tokenId, _properties[i].key, _properties[i].value); - } - nftCollection.transferFromCross( - NftCrossAccountId(address(this), 0), - NftCrossAccountId(address(0), _substrateReceiver), - tokenId - ); - } else { - revert("Wrong collection type. Works only with NFT or RFT"); - } - - emit MintToSub(address(0), _substrateReceiver, _collection, tokenId); - } - - function mintToSubstrateBulkProperty( - address _collection, - uint256 _substrateReceiver, - NftProperty[] calldata _properties - ) external checkRestrictions(_collection) { - uint256 propertiesLength = _properties.length; - require(propertiesLength > 0, "Properies is empty"); - - Collection commonContract = Collection(_collection); - bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType())); - uint256 tokenId; - - if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) { - UniqueNFT nftCollection = UniqueNFT(_collection); - tokenId = nftCollection.mint(address(this)); - - nftCollection.setProperties(tokenId, _properties); - - nftCollection.transferFromCross( - NftCrossAccountId(address(this), 0), - NftCrossAccountId(address(0), _substrateReceiver), - tokenId - ); - } else { - revert("Wrong collection type. Works only with NFT or RFT"); - } - - emit MintToSub(address(0), _substrateReceiver, _collection, tokenId); - } -} --- a/js-packages/scripts/src/benchmarks/nesting/ABIGEN/RMRKNestableMintable.ts +++ /dev/null @@ -1,354 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ - -import type BN from "bn.js"; -import type { ContractOptions } from "web3-eth-contract"; -import type { EventLog } from "web3-core"; -import type { EventEmitter } from "events"; -import type { - Callback, - NonPayableTransactionObject, - BlockType, - ContractEventLog, - BaseContract, -} from "./types.js"; - -export interface EventOptions { - filter?: object; - fromBlock?: BlockType; - topics?: string[]; -} - -export type AllChildrenRejected = ContractEventLog<{ - tokenId: string; - 0: string; -}>; -export type Approval = ContractEventLog<{ - owner: string; - approved: string; - tokenId: string; - 0: string; - 1: string; - 2: string; -}>; -export type ApprovalForAll = ContractEventLog<{ - owner: string; - operator: string; - approved: boolean; - 0: string; - 1: string; - 2: boolean; -}>; -export type ChildAccepted = ContractEventLog<{ - tokenId: string; - childIndex: string; - childAddress: string; - childId: string; - 0: string; - 1: string; - 2: string; - 3: string; -}>; -export type ChildProposed = ContractEventLog<{ - tokenId: string; - childIndex: string; - childAddress: string; - childId: string; - 0: string; - 1: string; - 2: string; - 3: string; -}>; -export type ChildTransferred = ContractEventLog<{ - tokenId: string; - childIndex: string; - childAddress: string; - childId: string; - fromPending: boolean; - toZero: boolean; - 0: string; - 1: string; - 2: string; - 3: string; - 4: boolean; - 5: boolean; -}>; -export type NestTransfer = ContractEventLog<{ - from: string; - to: string; - fromTokenId: string; - toTokenId: string; - tokenId: string; - 0: string; - 1: string; - 2: string; - 3: string; - 4: string; -}>; -export type Transfer = ContractEventLog<{ - from: string; - to: string; - tokenId: string; - 0: string; - 1: string; - 2: string; -}>; - -export interface RMRKNestableMintable extends BaseContract { - constructor( - jsonInterface: any[], - address?: string, - options?: ContractOptions - ): RMRKNestableMintable; - clone(): RMRKNestableMintable; - methods: { - RMRK_INTERFACE(): NonPayableTransactionObject; - - VERSION(): NonPayableTransactionObject; - - acceptChild( - parentId: number | string | BN, - childIndex: number | string | BN, - childAddress: string, - childId: number | string | BN - ): NonPayableTransactionObject; - - addChild( - parentId: number | string | BN, - childId: number | string | BN, - data: string | number[] - ): NonPayableTransactionObject; - - approve( - to: string, - tokenId: number | string | BN - ): NonPayableTransactionObject; - - balanceOf(owner: string): NonPayableTransactionObject; - - "burn(uint256)"( - tokenId: number | string | BN - ): NonPayableTransactionObject; - - "burn(uint256,uint256)"( - tokenId: number | string | BN, - maxChildrenBurns: number | string | BN - ): NonPayableTransactionObject; - - childOf( - parentId: number | string | BN, - index: number | string | BN - ): NonPayableTransactionObject<[string, string]>; - - childrenOf( - parentId: number | string | BN - ): NonPayableTransactionObject<[string, string][]>; - - directOwnerOf(tokenId: number | string | BN): NonPayableTransactionObject<{ - 0: string; - 1: string; - 2: boolean; - }>; - - getApproved( - tokenId: number | string | BN - ): NonPayableTransactionObject; - - isApprovedForAll( - owner: string, - operator: string - ): NonPayableTransactionObject; - - mint( - to: string, - tokenId: number | string | BN - ): NonPayableTransactionObject; - - name(): NonPayableTransactionObject; - - nestMint( - to: string, - tokenId: number | string | BN, - destinationId: number | string | BN - ): NonPayableTransactionObject; - - nestTransfer( - to: string, - tokenId: number | string | BN, - destinationId: number | string | BN - ): NonPayableTransactionObject; - - nestTransferFrom( - from: string, - to: string, - tokenId: number | string | BN, - destinationId: number | string | BN, - data: string | number[] - ): NonPayableTransactionObject; - - onERC721Received( - _operator: string, - _from: string, - _tokenId: number | string | BN, - _data: string | number[] - ): NonPayableTransactionObject; - - ownerOf(tokenId: number | string | BN): NonPayableTransactionObject; - - pendingChildOf( - parentId: number | string | BN, - index: number | string | BN - ): NonPayableTransactionObject<[string, string]>; - - pendingChildrenOf( - parentId: number | string | BN - ): NonPayableTransactionObject<[string, string][]>; - - rejectAllChildren( - tokenId: number | string | BN, - maxRejections: number | string | BN - ): NonPayableTransactionObject; - - safeMint(to: string): NonPayableTransactionObject; - - "safeTransferFrom(address,address,uint256)"( - from: string, - to: string, - tokenId: number | string | BN - ): NonPayableTransactionObject; - - "safeTransferFrom(address,address,uint256,bytes)"( - from: string, - to: string, - tokenId: number | string | BN, - data: string | number[] - ): NonPayableTransactionObject; - - setApprovalForAll( - operator: string, - approved: boolean - ): NonPayableTransactionObject; - - supportsInterface( - interfaceId: string | number[] - ): NonPayableTransactionObject; - - symbol(): NonPayableTransactionObject; - - transfer( - to: string, - tokenId: number | string | BN - ): NonPayableTransactionObject; - - transferChild( - tokenId: number | string | BN, - to: string, - destinationId: number | string | BN, - childIndex: number | string | BN, - childAddress: string, - childId: number | string | BN, - isPending: boolean, - data: string | number[] - ): NonPayableTransactionObject; - - transferFrom( - from: string, - to: string, - tokenId: number | string | BN - ): NonPayableTransactionObject; - }; - events: { - AllChildrenRejected(cb?: Callback): EventEmitter; - AllChildrenRejected( - options?: EventOptions, - cb?: Callback - ): EventEmitter; - - Approval(cb?: Callback): EventEmitter; - Approval(options?: EventOptions, cb?: Callback): EventEmitter; - - ApprovalForAll(cb?: Callback): EventEmitter; - ApprovalForAll( - options?: EventOptions, - cb?: Callback - ): EventEmitter; - - ChildAccepted(cb?: Callback): EventEmitter; - ChildAccepted( - options?: EventOptions, - cb?: Callback - ): EventEmitter; - - ChildProposed(cb?: Callback): EventEmitter; - ChildProposed( - options?: EventOptions, - cb?: Callback - ): EventEmitter; - - ChildTransferred(cb?: Callback): EventEmitter; - ChildTransferred( - options?: EventOptions, - cb?: Callback - ): EventEmitter; - - NestTransfer(cb?: Callback): EventEmitter; - NestTransfer( - options?: EventOptions, - cb?: Callback - ): EventEmitter; - - Transfer(cb?: Callback): EventEmitter; - Transfer(options?: EventOptions, cb?: Callback): EventEmitter; - - allEvents(options?: EventOptions, cb?: Callback): EventEmitter; - }; - - once(event: "AllChildrenRejected", cb: Callback): void; - once( - event: "AllChildrenRejected", - options: EventOptions, - cb: Callback - ): void; - - once(event: "Approval", cb: Callback): void; - once(event: "Approval", options: EventOptions, cb: Callback): void; - - once(event: "ApprovalForAll", cb: Callback): void; - once( - event: "ApprovalForAll", - options: EventOptions, - cb: Callback - ): void; - - once(event: "ChildAccepted", cb: Callback): void; - once( - event: "ChildAccepted", - options: EventOptions, - cb: Callback - ): void; - - once(event: "ChildProposed", cb: Callback): void; - once( - event: "ChildProposed", - options: EventOptions, - cb: Callback - ): void; - - once(event: "ChildTransferred", cb: Callback): void; - once( - event: "ChildTransferred", - options: EventOptions, - cb: Callback - ): void; - - once(event: "NestTransfer", cb: Callback): void; - once( - event: "NestTransfer", - options: EventOptions, - cb: Callback - ): void; - - once(event: "Transfer", cb: Callback): void; - once(event: "Transfer", options: EventOptions, cb: Callback): void; -} --- a/js-packages/scripts/src/benchmarks/nesting/ABIGEN/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -export type { RMRKNestableMintable } from "./RMRKNestableMintable.js"; --- a/js-packages/scripts/src/benchmarks/nesting/ABIGEN/types.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* tslint:disable */ -/* eslint-disable */ -import type BN from "bn.js"; -import type { EventEmitter } from "events"; -import type { EventLog, PromiEvent, TransactionReceipt } from "web3-core"; -import type { Contract } from "web3-eth-contract"; - -export interface EstimateGasOptions { - from?: string; - gas?: number; - value?: number | string | BN; -} - -export interface EventOptions { - filter?: object; - fromBlock?: BlockType; - topics?: string[]; -} - -export type Callback = (error: Error, result: T) => void; -export interface ContractEventLog extends EventLog { - returnValues: T; -} -export interface ContractEventEmitter extends EventEmitter { - on(event: "connected", listener: (subscriptionId: string) => void): this; - on( - event: "data" | "changed", - listener: (event: ContractEventLog) => void - ): this; - on(event: "error", listener: (error: Error) => void): this; -} - -export interface NonPayableTx { - nonce?: string | number | BN; - chainId?: string | number | BN; - from?: string; - to?: string; - data?: string; - gas?: string | number | BN; - maxPriorityFeePerGas?: string | number | BN; - maxFeePerGas?: string | number | BN; - gasPrice?: string | number | BN; -} - -export interface PayableTx extends NonPayableTx { - value?: string | number | BN; -} - -export interface NonPayableTransactionObject { - arguments: any[]; - call(tx?: NonPayableTx, block?: BlockType): Promise; - send(tx?: NonPayableTx): PromiEvent; - estimateGas(tx?: NonPayableTx): Promise; - encodeABI(): string; -} - -export interface PayableTransactionObject { - arguments: any[]; - call(tx?: PayableTx, block?: BlockType): Promise; - send(tx?: PayableTx): PromiEvent; - estimateGas(tx?: PayableTx): Promise; - encodeABI(): string; -} - -export type BlockType = - | "latest" - | "pending" - | "genesis" - | "earliest" - | number - | BN; -export type BaseContract = Omit; --- a/js-packages/scripts/src/benchmarks/nesting/RMRKNestableMintable.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -import "@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol"; -import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; - -contract RMRKNestableMintable is RMRKNestable, IERC721Receiver { - uint256 private _counter; - - constructor() RMRKNestable("RMRK", "nesting") { - _counter = 1; - } - - function safeMint(address to) external { - _safeMint(to, _counter, ""); - _counter++; - } - - function mint(address to, uint256 tokenId) external { - _mint(to, tokenId, ""); - } - - function nestMint(address to, uint256 tokenId, uint256 destinationId) external { - _nestMint(to, tokenId, destinationId, ""); - } - - function nestTransfer(address to, uint256 tokenId, uint256 destinationId) public virtual { - nestTransferFrom(_msgSender(), to, tokenId, destinationId, ""); - } - - function transfer(address to, uint256 tokenId) public virtual { - transferFrom(_msgSender(), to, tokenId); - } - - function onERC721Received( - address _operator, - address _from, - uint256 _tokenId, - bytes calldata _data - ) external returns (bytes4) { - return IERC721Receiver.onERC721Received.selector; - } -} --- a/js-packages/scripts/src/benchmarks/nesting/abigen.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {runTypeChain, glob, DEFAULT_FLAGS} from 'typechain'; - - -async function main() { - const cwd = process.cwd(); - - // find all files matching the glob - const allFiles = glob(cwd, ['./ABI/*.abi']); - - await runTypeChain({ - cwd, - filesToProcess: allFiles, - allFiles, - outDir: 'ABIGEN', - target: 'web3-v1', - flags: {...DEFAULT_FLAGS, alwaysGenerateOverloads: true, tsNocheck: false}, - }); -} - -main().catch(console.error); \ No newline at end of file --- a/js-packages/scripts/src/benchmarks/nesting/index.ts +++ /dev/null @@ -1,222 +0,0 @@ -import {usingEthPlaygrounds} from '@unique/tests/src/eth/util/index.js'; -import {EthUniqueHelper} from '@unique/tests/src/eth/util/playgrounds/unique.dev.js'; -import {readFile} from 'fs/promises'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {Contract} from 'web3-eth-contract'; -import {convertToTokens} from '../utils/common.js'; -import {makeNames} from '@unique/tests/src/util/index.js'; -import type {ContractImports} from '@unique/tests/src/eth/util/playgrounds/types.js'; -import type {RMRKNestableMintable} from './ABIGEN/index.js'; - -const {dirname} = makeNames(import.meta.url); - -export const CONTRACT_IMPORT: ContractImports[] = [ - { - fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol`, - solPath: '@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol', - }, - { - fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/nestable/IERC6059.sol`, - solPath: '@rmrk-team/evm-contracts/contracts/RMRK/nestable/IERC6059.sol', - }, - { - fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/core/RMRKCore.sol`, - solPath: '@rmrk-team/evm-contracts/contracts/RMRK/core/RMRKCore.sol', - }, - { - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol`, - solPath: '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol', - }, - { - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol`, - solPath: '@openzeppelin/contracts/token/ERC721/IERC721.sol', - }, - { - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol`, - solPath: '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol', - }, - { - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Address.sol`, - solPath: '@openzeppelin/contracts/utils/Address.sol', - }, - { - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Context.sol`, - solPath: '@openzeppelin/contracts/utils/Context.sol', - }, - { - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol`, - solPath: '@openzeppelin/contracts/utils/introspection/IERC165.sol', - }, - { - fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/library/RMRKErrors.sol`, - solPath: '@rmrk-team/evm-contracts/contracts/RMRK/library/RMRKErrors.sol', - }, - { - fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/core/IRMRKCore.sol`, - solPath: '@rmrk-team/evm-contracts/contracts/RMRK/core/IRMRKCore.sol', - }, - { - fsPath: `${dirname}/RMRKNestableMintable.sol`, - solPath: 'RMRKNestableMintable.sol', - }, -]; - - -const main = async () => { - - await usingEthPlaygrounds(async (helper, privateKey) => { - - const donor = await privateKey('//Alice'); // Seed from account with balance on this network - - const eth = await measureEth(helper, donor); - const sub = await measureSub(helper, donor); - const rmrk = await measureRMRK(helper, donor); - console.table({susbtrate: sub, eth: eth, rmrk: rmrk}); - }); -}; - -async function measureRMRK(helper: EthUniqueHelper, donor: IKeyringPair) { - const CONTRACT_SOURCE = ( - await readFile(`${dirname}/RMRKNestableMintable.sol`) - ).toString(); - const RELAYER_SOURCE = (await readFile(`${dirname}/relayer.sol`)).toString(); - - const ethSigner = await helper.eth.createAccountWithBalance(donor); - - const contract = await helper.ethContract.deployByCode( - ethSigner, - 'RMRKNestableMintable', - CONTRACT_SOURCE, - CONTRACT_IMPORT, - 5000000, - ); - - const relayer = await helper.ethContract.deployByCode( - ethSigner, - 'Relayer', - RELAYER_SOURCE, - CONTRACT_IMPORT, - 5000000, - [contract.options.address], - ); - - const relayerAddress = relayer.options.address; - - const rmrk = contract as any as RMRKNestableMintable; - const createTokenFor = async (receiver: string) => { - const tokenReceipt = await rmrk.methods.safeMint(receiver).send({from: ethSigner}); - return tokenReceipt.events!['Transfer'].returnValues.tokenId as number; - }; - - - const nestId = await createTokenFor(ethSigner); - const outerCollectionNestedId = 10; - const contractOwnedNestId = await createTokenFor(relayerAddress); - const nextTokenId = contractOwnedNestId + 1; - - const nestTransfer = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => { - const addChildData = rmrk.methods.addChild(nestId, outerCollectionNestedId, []).encodeABI(); - await relayer.methods.relay(addChildData).send({from: ethSigner}); - await rmrk.methods.acceptChild(nestId, 0, relayerAddress, outerCollectionNestedId).send({from: ethSigner}); - }); - - - const nestMint = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => { - const nestMintData = rmrk.methods.nestMint(rmrk.options.address, nextTokenId, contractOwnedNestId).encodeABI(); - await relayer.methods.relay(nestMintData).send({from: ethSigner}); - const acceptNestedToken = rmrk.methods.acceptChild(contractOwnedNestId, 0, rmrk.options.address, nextTokenId).encodeABI(); - await relayer.methods.relay(acceptNestedToken).send({from: ethSigner}); - }); - - - const unnestToken = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => { - const unnestData = rmrk.methods.transferChild(contractOwnedNestId, ethSigner, 0, 0, rmrk.options.address, nextTokenId, false, []).encodeABI(); - await relayer.methods.relay(unnestData).send({from: ethSigner}); - }); - - return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)}; -} - -async function measureEth(helper: EthUniqueHelper, donor: IKeyringPair) { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId, contract} = await createNestingCollection(helper, owner); - - // Create a token to be nested to - const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner}); - const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId; - const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId); - - // Create a nested token - const nestMint = await helper.arrange.calculcateFee({Ethereum: owner}, async () => { - await contract.methods.mint(targetNftTokenAddress).send({from: owner}); - }); - - // Create a token to be nested and nest - const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner}); - const nestedTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId; - - const nestTransfer = await helper.arrange.calculcateFee({Ethereum: owner}, async () => { - await contract.methods.transfer(targetNftTokenAddress, nestedTokenId).send({from: owner}); - }); - - const unnestToken = await helper.arrange.calculcateFee({Ethereum: owner}, async () => { - await contract.methods.transferFrom(targetNftTokenAddress, owner, nestedTokenId).send({from: owner}); - }); - return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)}; -} - -async function measureSub(helper: EthUniqueHelper, donor: IKeyringPair) { - - const [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - - const targetNFTCollection = await helper.nft.mintCollection(alice); - const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address}); - - - const collectionForNesting = await helper.nft.mintCollection(bob); - // permissions should be set: - await targetNFTCollection.setPermissions(alice, { - nesting: {tokenOwner: true}, - }); - - const nestMint = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => { - await (collectionForNesting.mintToken(bob, targetTokenBob.nestingAccount())); - }); - - const nestedToken2 = await collectionForNesting.mintToken(bob); - const nestTransfer = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => { - await nestedToken2.nest(bob, targetTokenBob); - }); - - const unnestToken = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => { - await nestedToken2.transferFrom(bob, targetTokenBob.nestingAccount(), {Substrate: bob.address}); - }); - - return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)}; -} - - -const createNestingCollection = async ( - helper: EthUniqueHelper, - owner: string, -): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => { - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - await contract.methods.setCollectionNesting(true).send({from: owner}); - - return {collectionId, collectionAddress, contract}; -}; - -main() - .then(() => process.exit(0)) - .catch((e) => { - console.log(e); - process.exit(1); - }); - - - - - - --- a/js-packages/scripts/src/benchmarks/nesting/relayer.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; -import "./RMRKNestableMintable.sol"; - -contract Relayer is RMRKNestableMintable { - address _receiver; - - constructor(address recevier) { - _receiver = recevier; - } - - function relay(bytes calldata payload) external { - (bool succes, bytes memory _returnData) = address(_receiver).call(payload); - require(succes); - } -} --- a/js-packages/scripts/src/benchmarks/opsFee/index.ts +++ /dev/null @@ -1,892 +0,0 @@ -import {usingEthPlaygrounds} from '@unique/tests/src/eth/util/index.js'; -import {EthUniqueHelper} from '@unique/tests/src/eth/util/playgrounds/unique.dev.js'; -import {readFile} from 'fs/promises'; -import {CollectionLimitField, CreateCollectionData, TokenPermissionField} from '@unique/tests/src/eth/util/playgrounds/types.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {UniqueFTCollection, UniqueNFTCollection} from '@unique/playgrounds/src/unique.js'; -import {Contract} from 'web3-eth-contract'; -import {createObjectCsvWriter} from 'csv-writer'; -import {FunctionFeeVM} from '../utils/types.js'; -import type {IFunctionFee} from '../utils/types.js'; -import {convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES, SUBS_PROPERTIES} from '../utils/common.js'; -import {makeNames} from '@unique/tests/src/util/index.js'; - - -const {dirname} = makeNames(import.meta.url); - -const main = async () => { - - const headers = [ - 'function', - 'ethFee', - 'ethGas', - 'substrate', - 'zeppelinFee', - 'zeppelinGas', - ]; - - const csvWriter20 = createObjectCsvWriter({ - path: 'erc20.csv', - header: headers, - }); - - const csvWriter721 = createObjectCsvWriter({ - path: 'erc721.csv', - header: headers, - }); - - await usingEthPlaygrounds(async (helper, privateKey) => { - console.log('\n ERC20 ops fees'); - const erc20 = FunctionFeeVM.fromModel(await erc20CalculateFeeGas(helper, privateKey)); - console.table(erc20); - await csvWriter20.writeRecords(FunctionFeeVM.toCsv(erc20)); - - console.log('\n ERC721 ops fees'); - const erc721 = FunctionFeeVM.fromModel(await erc721CalculateFeeGas(helper, privateKey)); - console.table(erc721); - - await csvWriter721.writeRecords(FunctionFeeVM.toCsv(erc721)); - }); -}; - -main() - .then(() => process.exit(0)) - .catch((e) => { - console.log(e); - process.exit(1); - }); - -async function erc721CalculateFeeGas( - helper: EthUniqueHelper, - privateKey: (seed: string) => Promise, -): Promise { - const res: IFunctionFee = {}; - const donor = await privateKey('//Alice'); - const [subReceiver] = await helper.arrange.createAccounts([10n], donor); - const ethSigner = await helper.eth.createAccountWithBalance(donor); - const ethReceiver = await helper.eth.createAccountWithBalance(donor); - const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner); - const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver); - const collection = (await createCollectionForBenchmarks( - 'nft', - helper, - privateKey, - ethSigner, - null, - PERMISSIONS, - )) as UniqueNFTCollection; - - const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner); - let zeppelelinContract: Contract | null = null; - const ZEPPELIN_OBJECT = '0x' + (await readFile(`${dirname}/../utils/openZeppelin/ERC721/bin/ZeppelinContract.bin`)).toString(); - const ZEPPELIN_ABI = JSON.parse((await readFile(`${dirname}/../utils/openZeppelin/ERC721/bin/ZeppelinContract.abi`)).toString()); - - const evmContract = await helper.ethNativeContract.collection( - helper.ethAddress.fromCollectionId(collection.collectionId), - 'nft', - ethSigner, - true, - ); - - res['createCollection'] = await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => helperContract.methods.createNFTCollection('test','test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}), - ); - - res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => helper.nft.mintCollection( - donor, - {name: 'test', description: 'test', tokenPrefix: 'test'}, - ), - ))); - - res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - async () => { - zeppelelinContract = await helper.ethContract.deployByAbi( - ethSigner, - ZEPPELIN_ABI, - ZEPPELIN_OBJECT, - ); - }, - ); - - res['mint'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.mint(ethSigner).send(), - ); - - res['mint'].zeppelin = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => zeppelelinContract!.methods.safeMint(ethSigner, '').send({from: ethSigner}), - ); - - res['mintCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.mintCross(crossSigner, []).send(), - ); - - res['mint'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.mintToken( - donor, - {Substrate: donor.address}, - ), - ))); - - res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.mintMultipleTokens(donor, [{ - owner: {Substrate: donor.address}, - }]), - ))); - - res['mintWithTokenURI'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.mintWithTokenURI(ethSigner, 'Test URI').send(), - ); - - res['mintWithTokenURI'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.mintToken( - donor, - {Ethereum: ethSigner}, - [{key: 'URI', value: 'Test URI'}], - ), - ))); - - res['mintWithTokenURI'].zeppelin = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => zeppelelinContract!.methods.safeMint(ethSigner, 'Test URI').send({from: ethSigner}), - ); - - res['setProperties'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setProperties(1, PROPERTIES.slice(0,1)).send(), - ); - - res['deleteProperties'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.deleteProperties(1, [PROPERTIES[0].key]).send(), - ); - - res['setProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setTokenProperties( - donor, - 1, - SUBS_PROPERTIES.slice(0, 1), - ), - ))); - - res['deleteProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.deleteTokenProperties( - donor, - 1, - SUBS_PROPERTIES.slice(0, 1) - .map(p => p.key), - ), - ))); - - res['transfer'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.transfer(ethReceiver, 1).send(), - ); - - res['safeTransferFrom*'] = { - zeppelin: - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => zeppelelinContract!.methods.safeTransferFrom(ethSigner, ethReceiver, 0).send({from: ethSigner}), - ), - }; - - res['transferCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethReceiver}, - () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}), - ); - - res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.transferToken( - donor, - 3, - {Substrate: subReceiver.address}, - ), - ))); - await collection.approveToken(subReceiver, 3, {Substrate: donor.address}); - - res['transferFrom*'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(), - ); - - res['transferFrom*'].zeppelin = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethReceiver}, - () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 0).send({from: ethReceiver}), - ); - - - res['transferFromCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethReceiver}, - () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}), - ); - - res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.transferTokenFrom(donor, 3, {Substrate: subReceiver.address}, {Substrate: donor.address}), - ))); - - res['burn'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.burn(1).send(), - ); - - res['burn'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.burnToken(donor, 3), - ))); - - res['approve*'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.approve(ethReceiver, 2).send(), - ); - - res['approve*'].zeppelin = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => zeppelelinContract!.methods.approve(ethReceiver, 0).send({from: ethSigner}), - ); - - res['approveCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.approveCross(crossReceiver, 2).send(), - ); - - res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.approveToken(donor, 4, {Substrate: subReceiver.address}), - ))); - - res['setApprovalForAll*'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setApprovalForAll(ethReceiver, true).send(), - ); - - res['setApprovalForAll*'].zeppelin = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => zeppelelinContract!.methods.setApprovalForAll(ethReceiver, true).send({from: ethSigner}), - ); - - res['setApprovalForAll*'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => helper.nft.setAllowanceForAll(donor, collection.collectionId, {Substrate: subReceiver.address}, true), - ))); - - res['burnFromCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethReceiver}, - () => evmContract.methods.burnFromCross(crossSigner, 2).send({from:ethReceiver}), - ); - - res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: subReceiver.address}, - () => collection.burnTokenFrom(subReceiver, 4, {Substrate: donor.address}), - ))); - - res['setTokenPropertyPermissions'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setTokenPropertyPermissions([ - ['url', [ - [TokenPermissionField.Mutable, true], - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, true]], - ], - ]).send(), - ); - - res['setTokenPropertyPermissions'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setTokenPropertyPermissions(donor, [{key: 'url', permission: { - tokenOwner: true, - collectionAdmin: true, - mutable: true, - }}]), - ))); - - res['setCollectionSponsorCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionSponsorCross(crossReceiver).send(), - ); - - res['confirmCollectionSponsorship'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethReceiver}, - () => evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}), - ); - - res['removeCollectionSponsor'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.removeCollectionSponsor().send(), - ); - - res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setSponsor(donor, subReceiver.address), - ))); - - res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: subReceiver.address}, - () => collection.confirmSponsorship(subReceiver), - ))); - - res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.removeSponsor(donor), - ))); - - res['setCollectionProperties'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(), - ); - - res['deleteCollectionProperties'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(), - ); - res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setProperties(donor, PROPERTIES.slice(0, 1) - .map(p => ({key: p.key, value: p.value.toString()}))), - ))); - - res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1) - .map(p => p.key)), - ))); - - res['setCollectionLimit'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(), - ); - - res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}), - ))); - - const {collectionAddress} = await helper.eth.createCollection(ethSigner, new CreateCollectionData('A', 'B', 'C', 'nft')).send(); - const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true); - - - res['addCollectionAdminCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(), - ); - - res['removeCollectionAdminCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(), - ); - - res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.addAdmin(donor, {Ethereum: ethReceiver}), - ))); - - res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.removeAdmin(donor, {Ethereum: ethReceiver}), - ))); - - res['setCollectionNesting'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionNesting(true).send(), - ); - - res['setCollectionNesting[]'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(), - ); - - res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.disableNesting(donor), - ))); - - res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setPermissions( - donor, - { - nesting: { - tokenOwner: true, - restricted: [collection.collectionId], - }, - }, - ), - ))); - - res['setCollectionAccess'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionAccess(1).send(), - ); - - res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setPermissions(donor, {access: 'AllowList'}), - ))); - - res['addToCollectionAllowListCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(), - ); - - res['removeFromCollectionAllowListCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(), - ); - - res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.addToAllowList(donor, {Ethereum: ethReceiver}), - ))); - - res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}), - ))); - - res['setCollectionMintMode'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionMintMode(true).send(), - ); - - res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setPermissions(donor, {mintMode: false}), - ))); - - res['changeCollectionOwnerCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(), - ); - - res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.changeOwner(donor, subReceiver.address), - ))); - - return res; -} - -async function erc20CalculateFeeGas( - helper: EthUniqueHelper, - privateKey: (seed: string) => Promise, - -): Promise { - const res: IFunctionFee = {}; - const donor = await privateKey('//Alice'); - const [subReceiver] = await helper.arrange.createAccounts([10n], donor); - const ethSigner = await helper.eth.createAccountWithBalance(donor); - const ethReceiver = await helper.eth.createAccountWithBalance(donor); - const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner); - const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver); - const collection = (await createCollectionForBenchmarks( - 'ft', - helper, - privateKey, - ethSigner, - null, - PERMISSIONS, - )) as UniqueFTCollection; - - const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner); - let zeppelelinContract: Contract | null = null; - const ZEPPELIN_OBJECT = '0x' + (await readFile(`${dirname}/../utils/openZeppelin/ERC20/bin/ZeppelinContract.bin`)).toString(); - const ZEPPELIN_ABI = JSON.parse((await readFile(`${dirname}/../utils/openZeppelin/ERC20/bin/ZeppelinContract.abi`)).toString()); - - const evmContract = await helper.ethNativeContract.collection( - helper.ethAddress.fromCollectionId(collection.collectionId), - 'ft', - ethSigner, - true, - ); - - res['createCollection'] = await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => helperContract.methods.createFTCollection('test', 18,'test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}), - ); - - res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => helper.ft.mintCollection( - donor, - {name: 'test', description: 'test', tokenPrefix: 'test'}, - 18, - ), - ))); - - res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - async () => { - zeppelelinContract = await helper.ethContract.deployByAbi( - ethSigner, - ZEPPELIN_ABI, - ZEPPELIN_OBJECT, - ); - }, - ); - - res['mint'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.mint(ethSigner, 1).send(), - ); - - res['mint'].zeppelin = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => zeppelelinContract!.methods.mint(ethSigner, 1).send(), - ); - - res['mintCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.mintCross(crossSigner, 1).send(), - ); - - res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.mint( - donor, - 10n, - {Substrate: donor.address}, - ), - ))); - - res['mintBulk'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.mintBulk([{to: ethSigner, amount: 1}]).send(), - ); - - res['mintBulk'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => helper.executeExtrinsic(donor, 'api.tx.unique.createMultipleItemsEx',[collection.collectionId, { - Fungible: new Map([ - [JSON.stringify({Ethereum: ethSigner}), 1], - ]), - }], true), - ))); - - res['transfer*'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.transfer(ethReceiver, 1).send(), - ); - - res['transfer*'].zeppelin = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => zeppelelinContract!.methods.transfer(ethReceiver, 1).send(), - ); - - res['transferCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethReceiver}, - () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}), - ); - - res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.transfer( - donor, - {Substrate: subReceiver.address}, - 1n, - ), - ))); - await collection.approveTokens(subReceiver, {Substrate: donor.address}, 1n); - - res['transferFrom*'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(), - ); - - await zeppelelinContract!.methods.approve(ethSigner, 10).send({from: ethReceiver}); - - res['transferFrom*'].zeppelin = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 1).send({from: ethSigner}), - ); - - res['transferFromCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethReceiver}, - () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}), - ); - - res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.transferFrom(donor, {Substrate: subReceiver.address}, {Substrate: donor.address}, 1n), - ))); - - - res['burnTokens'] = {fee: 0n, gas: 0n}; - res['burnTokens'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.burnTokens(donor, 1n), - ))); - - - res['approve*'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.approve(ethReceiver, 2).send(), - ); - - res['approve*'].zeppelin = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => zeppelelinContract!.methods.approve(ethReceiver, 10).send(), - ); - - res['approveCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.approveCross(crossReceiver, 2).send(), - ); - - res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.approveTokens(donor, {Substrate: subReceiver.address}, 1n), - ))); - - res['burnFromCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethReceiver}, - () => evmContract.methods.burnFromCross(crossSigner, 1).send({from:ethReceiver}), - ); - - res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: subReceiver.address}, - () => collection.burnTokensFrom(subReceiver, {Substrate: donor.address}, 1n), - ))); - - res['setCollectionSponsorCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionSponsorCross(crossReceiver).send(), - ); - - res['confirmCollectionSponsorship'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethReceiver}, - () => evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}), - ); - - res['removeCollectionSponsor'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.removeCollectionSponsor().send(), - ); - - res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setSponsor(donor, subReceiver.address), - ))); - - res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: subReceiver.address}, - () => collection.confirmSponsorship(subReceiver), - ))); - - res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.removeSponsor(donor), - ))); - - res['setCollectionProperties'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(), - ); - - res['deleteCollectionProperties'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(), - ); - res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setProperties(donor, PROPERTIES.slice(0, 1) - .map(p => ({key: p.key, value: p.value.toString()}))), - ))); - - res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1) - .map(p => p.key)), - ))); - - res['setCollectionLimit'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(), - ); - - res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}), - ))); - - const {collectionAddress} = await helper.eth.createCollection(ethSigner, new CreateCollectionData('A', 'B', 'C', 'nft')).send(); - const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true); - - - res['addCollectionAdminCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(), - ); - - res['removeCollectionAdminCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(), - ); - - res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.addAdmin(donor, {Ethereum: ethReceiver}), - ))); - - res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.removeAdmin(donor, {Ethereum: ethReceiver}), - ))); - - res['setCollectionNesting'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionNesting(true).send(), - ); - - res['setCollectionNesting[]'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(), - ); - - res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.disableNesting(donor), - ))); - - res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setPermissions( - donor, - { - nesting: { - tokenOwner: true, - restricted: [collection.collectionId], - }, - }, - ), - ))); - - res['setCollectionAccess'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionAccess(1).send(), - ); - - res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setPermissions(donor, {access: 'AllowList'}), - ))); - - res['addToCollectionAllowListCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(), - ); - - res['removeFromCollectionAllowListCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(), - ); - - res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.addToAllowList(donor, {Ethereum: ethReceiver}), - ))); - - res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}), - ))); - - res['setCollectionMintMode'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => evmContract.methods.setCollectionMintMode(true).send(), - ); - - res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.setPermissions(donor, {mintMode: false}), - ))); - - res['changeCollectionOwnerCross'] = - await helper.arrange.calculcateFeeGas( - {Ethereum: ethSigner}, - () => collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(), - ); - - res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee( - {Substrate: donor.address}, - () => collection.changeOwner(donor, subReceiver.address), - ))); - - return res; -} --- a/js-packages/scripts/src/benchmarks/utils/common.ts +++ /dev/null @@ -1,88 +0,0 @@ -import {EthUniqueHelper} from '@unique/tests/src/eth/util/playgrounds/unique.dev.js'; -import {UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/src/unique.js'; -import type {ITokenPropertyPermission, TCollectionMode} from '@unique/playgrounds/src/types.js'; -import type {IKeyringPair} from '@polkadot/types/types'; - -export const PROPERTIES = Array(40) - .fill(0) - .map((_, i) => ({ - key: `key_${i}`, - value: Uint8Array.from(Buffer.from(`value_${i}`)), - })); - -export const SUBS_PROPERTIES = Array(40) - .fill(0) - .map((_, i) => ({ - key: `key_${i}`, - value: `value_${i}`, - })); - -export const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => ({ - key: p.key, - permission: { - tokenOwner: true, - collectionAdmin: true, - mutable: true, - }, -})); - -export function convertToTokens(value: bigint, nominal = 1000_000_000_000_000_000n): number { - return Number((value * 1000n) / nominal) / 1000; -} - -export async function createCollectionForBenchmarks( - mode : TCollectionMode, - helper: EthUniqueHelper, - privateKey: (seed: string) => Promise, - ethSigner: string, - proxyContract: string | null, - permissions: ITokenPropertyPermission[], -) { - const donor = await privateKey('//Alice'); - - const collection = await helper[mode].mintCollection(donor, { - name: 'test mintToSubstrate', - description: 'EVMHelpers', - tokenPrefix: mode, - ...(mode != 'ft' ? { - tokenPropertyPermissions: [ - { - key: 'url', - permission: { - tokenOwner: true, - collectionAdmin: true, - mutable: true, - }, - }, - { - key: 'URI', - permission: { - tokenOwner: true, - collectionAdmin: true, - mutable: true, - }, - }, - ], - } : {}), - limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0}, - permissions: {mintMode: true}, - }); - - await collection.addToAllowList(donor, { - Ethereum: helper.address.substrateToEth(donor.address), - }); - await collection.addToAllowList(donor, {Substrate: donor.address}); - await collection.addAdmin(donor, {Ethereum: ethSigner}); - await collection.addAdmin(donor, { - Ethereum: helper.address.substrateToEth(donor.address), - }); - - if(proxyContract) { - await collection.addToAllowList(donor, {Ethereum: proxyContract}); - await collection.addAdmin(donor, {Ethereum: proxyContract}); - } - if(collection instanceof UniqueNFTCollection || collection instanceof UniqueRFTCollection) - await collection.setTokenPropertyPermissions(donor, permissions); - - return collection; -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# OpenZeppelin Contracts - -The files in this directory were sourced unmodified from OpenZeppelin Contracts v4.8.1. - -They are not meant to be edited. - -The originals can be found on [GitHub] and [npm]. - -[GitHub]: https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.8.1 -[npm]: https://www.npmjs.com/package/@openzeppelin/contracts/v/4.8.1 - -Generated with OpenZeppelin Contracts Wizard (https://zpl.in/wizard). --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/access/Ownable.sol +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) - -pragma solidity ^0.8.0; - -import "../utils/Context.sol"; - -/** - * @dev Contract module which provides a basic access control mechanism, where - * there is an account (an owner) that can be granted exclusive access to - * specific functions. - * - * By default, the owner account will be the one that deploys the contract. This - * can later be changed with {transferOwnership}. - * - * This module is used through inheritance. It will make available the modifier - * `onlyOwner`, which can be applied to your functions to restrict their use to - * the owner. - */ -abstract contract Ownable is Context { - address private _owner; - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev Initializes the contract setting the deployer as the initial owner. - */ - constructor() { - _transferOwnership(_msgSender()); - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - _checkOwner(); - _; - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view virtual returns (address) { - return _owner; - } - - /** - * @dev Throws if the sender is not the owner. - */ - function _checkOwner() internal view virtual { - require(owner() == _msgSender(), "Ownable: caller is not the owner"); - } - - /** - * @dev Leaves the contract without owner. It will not be possible to call - * `onlyOwner` functions anymore. Can only be called by the current owner. - * - * NOTE: Renouncing ownership will leave the contract without an owner, - * thereby removing any functionality that is only available to the owner. - */ - function renounceOwnership() public virtual onlyOwner { - _transferOwnership(address(0)); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function transferOwnership(address newOwner) public virtual onlyOwner { - require(newOwner != address(0), "Ownable: new owner is the zero address"); - _transferOwnership(newOwner); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Internal function without access restriction. - */ - function _transferOwnership(address newOwner) internal virtual { - address oldOwner = _owner; - _owner = newOwner; - emit OwnershipTransferred(oldOwner, newOwner); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/governance/utils/IVotes.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol) -pragma solidity ^0.8.0; - -/** - * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts. - * - * _Available since v4.5._ - */ -interface IVotes { - /** - * @dev Emitted when an account changes their delegate. - */ - event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); - - /** - * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes. - */ - event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); - - /** - * @dev Returns the current amount of votes that `account` has. - */ - function getVotes(address account) external view returns (uint256); - - /** - * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`). - */ - function getPastVotes(address account, uint256 blockNumber) external view returns (uint256); - - /** - * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`). - * - * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. - * Votes that have not been delegated are still part of total supply, even though they would not participate in a - * vote. - */ - function getPastTotalSupply(uint256 blockNumber) external view returns (uint256); - - /** - * @dev Returns the delegate that `account` has chosen. - */ - function delegates(address account) external view returns (address); - - /** - * @dev Delegates votes from the sender to `delegatee`. - */ - function delegate(address delegatee) external; - - /** - * @dev Delegates votes from signer to `delegatee`. - */ - function delegateBySig( - address delegatee, - uint256 nonce, - uint256 expiry, - uint8 v, - bytes32 r, - bytes32 s - ) external; -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC3156 FlashBorrower, as defined in - * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. - * - * _Available since v4.1._ - */ -interface IERC3156FlashBorrower { - /** - * @dev Receive a flash loan. - * @param initiator The initiator of the loan. - * @param token The loan currency. - * @param amount The amount of tokens lent. - * @param fee The additional amount of tokens to repay. - * @param data Arbitrary data structure, intended to contain user-defined parameters. - * @return The keccak256 hash of "IERC3156FlashBorrower.onFlashLoan" - */ - function onFlashLoan( - address initiator, - address token, - uint256 amount, - uint256 fee, - bytes calldata data - ) external returns (bytes32); -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashLender.sol) - -pragma solidity ^0.8.0; - -import "./IERC3156FlashBorrower.sol"; - -/** - * @dev Interface of the ERC3156 FlashLender, as defined in - * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. - * - * _Available since v4.1._ - */ -interface IERC3156FlashLender { - /** - * @dev The amount of currency available to be lended. - * @param token The loan currency. - * @return The amount of `token` that can be borrowed. - */ - function maxFlashLoan(address token) external view returns (uint256); - - /** - * @dev The fee to be charged for a given loan. - * @param token The loan currency. - * @param amount The amount of tokens lent. - * @return The amount of `token` to be charged for the loan, on top of the returned principal. - */ - function flashFee(address token, uint256 amount) external view returns (uint256); - - /** - * @dev Initiate a flash loan. - * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. - * @param token The loan currency. - * @param amount The amount of tokens lent. - * @param data Arbitrary data structure, intended to contain user-defined parameters. - */ - function flashLoan( - IERC3156FlashBorrower receiver, - address token, - uint256 amount, - bytes calldata data - ) external returns (bool); -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/security/Pausable.sol +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - -pragma solidity ^0.8.0; - -import "../utils/Context.sol"; - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/ERC20.sol +++ /dev/null @@ -1,389 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.0; - -import "./IERC20.sol"; -import "./extensions/IERC20Metadata.sol"; -import "../../utils/Context.sol"; - -/** - * @dev Implementation of the {IERC20} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * For a generic mechanism see {ERC20PresetMinterPauser}. - * - * TIP: For a detailed writeup see our guide - * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * We have followed general OpenZeppelin Contracts guidelines: functions revert - * instead returning `false` on failure. This behavior is nonetheless - * conventional and does not conflict with the expectations of ERC20 - * applications. - * - * Additionally, an {Approval} event is emitted on calls to {transferFrom}. - * This allows applications to reconstruct the allowance for all accounts just - * by listening to said events. Other implementations of the EIP may not emit - * these events, as it isn't required by the specification. - * - * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} - * functions have been added to mitigate the well-known issues around setting - * allowances. See {IERC20-approve}. - */ -contract ERC20 is Context, IERC20, IERC20Metadata { - mapping(address => uint256) private _balances; - - mapping(address => mapping(address => uint256)) private _allowances; - - uint256 private _totalSupply; - - string private _name; - string private _symbol; - - /** - * @dev Sets the values for {name} and {symbol}. - * - * The default value of {decimals} is 18. To select a different value for - * {decimals} you should overload it. - * - * All two of these values are immutable: they can only be set once during - * construction. - */ - constructor(string memory name_, string memory symbol_) { - _name = name_; - _symbol = symbol_; - } - - /** - * @dev Returns the name of the token. - */ - function name() public view virtual override returns (string memory) { - return _name; - } - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view virtual override returns (string memory) { - return _symbol; - } - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5.05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the value {ERC20} uses, unless this function is - * overridden; - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() public view virtual override returns (uint8) { - return 18; - } - - /** - * @dev See {IERC20-totalSupply}. - */ - function totalSupply() public view virtual override returns (uint256) { - return _totalSupply; - } - - /** - * @dev See {IERC20-balanceOf}. - */ - function balanceOf(address account) public view virtual override returns (uint256) { - return _balances[account]; - } - - /** - * @dev See {IERC20-transfer}. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - the caller must have a balance of at least `amount`. - */ - function transfer(address to, uint256 amount) public virtual override returns (bool) { - address owner = _msgSender(); - _transfer(owner, to, amount); - return true; - } - - /** - * @dev See {IERC20-allowance}. - */ - function allowance(address owner, address spender) public view virtual override returns (uint256) { - return _allowances[owner][spender]; - } - - /** - * @dev See {IERC20-approve}. - * - * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on - * `transferFrom`. This is semantically equivalent to an infinite approval. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function approve(address spender, uint256 amount) public virtual override returns (bool) { - address owner = _msgSender(); - _approve(owner, spender, amount); - return true; - } - - /** - * @dev See {IERC20-transferFrom}. - * - * Emits an {Approval} event indicating the updated allowance. This is not - * required by the EIP. See the note at the beginning of {ERC20}. - * - * NOTE: Does not update the allowance if the current allowance - * is the maximum `uint256`. - * - * Requirements: - * - * - `from` and `to` cannot be the zero address. - * - `from` must have a balance of at least `amount`. - * - the caller must have allowance for ``from``'s tokens of at least - * `amount`. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) public virtual override returns (bool) { - address spender = _msgSender(); - _spendAllowance(from, spender, amount); - _transfer(from, to, amount); - return true; - } - - /** - * @dev Atomically increases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { - address owner = _msgSender(); - _approve(owner, spender, allowance(owner, spender) + addedValue); - return true; - } - - /** - * @dev Atomically decreases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `spender` must have allowance for the caller of at least - * `subtractedValue`. - */ - function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { - address owner = _msgSender(); - uint256 currentAllowance = allowance(owner, spender); - require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); - unchecked { - _approve(owner, spender, currentAllowance - subtractedValue); - } - - return true; - } - - /** - * @dev Moves `amount` of tokens from `from` to `to`. - * - * This internal function is equivalent to {transfer}, and can be used to - * e.g. implement automatic token fees, slashing mechanisms, etc. - * - * Emits a {Transfer} event. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `from` must have a balance of at least `amount`. - */ - function _transfer( - address from, - address to, - uint256 amount - ) internal virtual { - require(from != address(0), "ERC20: transfer from the zero address"); - require(to != address(0), "ERC20: transfer to the zero address"); - - _beforeTokenTransfer(from, to, amount); - - uint256 fromBalance = _balances[from]; - require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); - unchecked { - _balances[from] = fromBalance - amount; - // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by - // decrementing then incrementing. - _balances[to] += amount; - } - - emit Transfer(from, to, amount); - - _afterTokenTransfer(from, to, amount); - } - - /** @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. - * - * Emits a {Transfer} event with `from` set to the zero address. - * - * Requirements: - * - * - `account` cannot be the zero address. - */ - function _mint(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: mint to the zero address"); - - _beforeTokenTransfer(address(0), account, amount); - - _totalSupply += amount; - unchecked { - // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. - _balances[account] += amount; - } - emit Transfer(address(0), account, amount); - - _afterTokenTransfer(address(0), account, amount); - } - - /** - * @dev Destroys `amount` tokens from `account`, reducing the - * total supply. - * - * Emits a {Transfer} event with `to` set to the zero address. - * - * Requirements: - * - * - `account` cannot be the zero address. - * - `account` must have at least `amount` tokens. - */ - function _burn(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: burn from the zero address"); - - _beforeTokenTransfer(account, address(0), amount); - - uint256 accountBalance = _balances[account]; - require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); - unchecked { - _balances[account] = accountBalance - amount; - // Overflow not possible: amount <= accountBalance <= totalSupply. - _totalSupply -= amount; - } - - emit Transfer(account, address(0), amount); - - _afterTokenTransfer(account, address(0), amount); - } - - /** - * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. - * - * This internal function is equivalent to `approve`, and can be used to - * e.g. set automatic allowances for certain subsystems, etc. - * - * Emits an {Approval} event. - * - * Requirements: - * - * - `owner` cannot be the zero address. - * - `spender` cannot be the zero address. - */ - function _approve( - address owner, - address spender, - uint256 amount - ) internal virtual { - require(owner != address(0), "ERC20: approve from the zero address"); - require(spender != address(0), "ERC20: approve to the zero address"); - - _allowances[owner][spender] = amount; - emit Approval(owner, spender, amount); - } - - /** - * @dev Updates `owner` s allowance for `spender` based on spent `amount`. - * - * Does not update the allowance amount in case of infinite allowance. - * Revert if not enough allowance is available. - * - * Might emit an {Approval} event. - */ - function _spendAllowance( - address owner, - address spender, - uint256 amount - ) internal virtual { - uint256 currentAllowance = allowance(owner, spender); - if (currentAllowance != type(uint256).max) { - require(currentAllowance >= amount, "ERC20: insufficient allowance"); - unchecked { - _approve(owner, spender, currentAllowance - amount); - } - } - } - - /** - * @dev Hook that is called before any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * will be transferred to `to`. - * - when `from` is zero, `amount` tokens will be minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual {} - - /** - * @dev Hook that is called after any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * has been transferred to `to`. - * - when `from` is zero, `amount` tokens have been minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens have been burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _afterTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual {} -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/IERC20.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol) - -pragma solidity ^0.8.0; - -import "../ERC20.sol"; -import "../../../utils/Context.sol"; - -/** - * @dev Extension of {ERC20} that allows token holders to destroy both their own - * tokens and those that they have an allowance for, in a way that can be - * recognized off-chain (via event analysis). - */ -abstract contract ERC20Burnable is Context, ERC20 { - /** - * @dev Destroys `amount` tokens from the caller. - * - * See {ERC20-_burn}. - */ - function burn(uint256 amount) public virtual { - _burn(_msgSender(), amount); - } - - /** - * @dev Destroys `amount` tokens from `account`, deducting from the caller's - * allowance. - * - * See {ERC20-_burn} and {ERC20-allowance}. - * - * Requirements: - * - * - the caller must have allowance for ``accounts``'s tokens of at least - * `amount`. - */ - function burnFrom(address account, uint256 amount) public virtual { - _spendAllowance(account, _msgSender(), amount); - _burn(account, amount); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC20FlashMint.sol) - -pragma solidity ^0.8.0; - -import "../../../interfaces/IERC3156FlashBorrower.sol"; -import "../../../interfaces/IERC3156FlashLender.sol"; -import "../ERC20.sol"; - -/** - * @dev Implementation of the ERC3156 Flash loans extension, as defined in - * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. - * - * Adds the {flashLoan} method, which provides flash loan support at the token - * level. By default there is no fee, but this can be changed by overriding {flashFee}. - * - * _Available since v4.1._ - */ -abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { - bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); - - /** - * @dev Returns the maximum amount of tokens available for loan. - * @param token The address of the token that is requested. - * @return The amount of token that can be loaned. - */ - function maxFlashLoan(address token) public view virtual override returns (uint256) { - return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0; - } - - /** - * @dev Returns the fee applied when doing flash loans. This function calls - * the {_flashFee} function which returns the fee applied when doing flash - * loans. - * @param token The token to be flash loaned. - * @param amount The amount of tokens to be loaned. - * @return The fees applied to the corresponding flash loan. - */ - function flashFee(address token, uint256 amount) public view virtual override returns (uint256) { - require(token == address(this), "ERC20FlashMint: wrong token"); - return _flashFee(token, amount); - } - - /** - * @dev Returns the fee applied when doing flash loans. By default this - * implementation has 0 fees. This function can be overloaded to make - * the flash loan mechanism deflationary. - * @param token The token to be flash loaned. - * @param amount The amount of tokens to be loaned. - * @return The fees applied to the corresponding flash loan. - */ - function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) { - // silence warning about unused variable without the addition of bytecode. - token; - amount; - return 0; - } - - /** - * @dev Returns the receiver address of the flash fee. By default this - * implementation returns the address(0) which means the fee amount will be burnt. - * This function can be overloaded to change the fee receiver. - * @return The address for which the flash fee will be sent to. - */ - function _flashFeeReceiver() internal view virtual returns (address) { - return address(0); - } - - /** - * @dev Performs a flash loan. New tokens are minted and sent to the - * `receiver`, who is required to implement the {IERC3156FlashBorrower} - * interface. By the end of the flash loan, the receiver is expected to own - * amount + fee tokens and have them approved back to the token contract itself so - * they can be burned. - * @param receiver The receiver of the flash loan. Should implement the - * {IERC3156FlashBorrower-onFlashLoan} interface. - * @param token The token to be flash loaned. Only `address(this)` is - * supported. - * @param amount The amount of tokens to be loaned. - * @param data An arbitrary datafield that is passed to the receiver. - * @return `true` if the flash loan was successful. - */ - // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount - // minted at the beginning is always recovered and burned at the end, or else the entire function will revert. - // slither-disable-next-line reentrancy-no-eth - function flashLoan( - IERC3156FlashBorrower receiver, - address token, - uint256 amount, - bytes calldata data - ) public virtual override returns (bool) { - require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan"); - uint256 fee = flashFee(token, amount); - _mint(address(receiver), amount); - require( - receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, - "ERC20FlashMint: invalid return value" - ); - address flashFeeReceiver = _flashFeeReceiver(); - _spendAllowance(address(receiver), address(this), amount + fee); - if (fee == 0 || flashFeeReceiver == address(0)) { - _burn(address(receiver), amount + fee); - } else { - _burn(address(receiver), amount); - _transfer(address(receiver), flashFeeReceiver, fee); - } - return true; - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol +++ /dev/null @@ -1,275 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.1) (token/ERC20/extensions/ERC20Votes.sol) - -pragma solidity ^0.8.0; - -import "./draft-ERC20Permit.sol"; -import "../../../utils/math/Math.sol"; -import "../../../governance/utils/IVotes.sol"; -import "../../../utils/math/SafeCast.sol"; -import "../../../utils/cryptography/ECDSA.sol"; - -/** - * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's, - * and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1. - * - * NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module. - * - * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either - * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting - * power can be queried through the public accessors {getVotes} and {getPastVotes}. - * - * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it - * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. - * - * _Available since v4.2._ - */ -abstract contract ERC20Votes is IVotes, ERC20Permit { - struct Checkpoint { - uint32 fromBlock; - uint224 votes; - } - - bytes32 private constant _DELEGATION_TYPEHASH = - keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); - - mapping(address => address) private _delegates; - mapping(address => Checkpoint[]) private _checkpoints; - Checkpoint[] private _totalSupplyCheckpoints; - - /** - * @dev Get the `pos`-th checkpoint for `account`. - */ - function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) { - return _checkpoints[account][pos]; - } - - /** - * @dev Get number of checkpoints for `account`. - */ - function numCheckpoints(address account) public view virtual returns (uint32) { - return SafeCast.toUint32(_checkpoints[account].length); - } - - /** - * @dev Get the address `account` is currently delegating to. - */ - function delegates(address account) public view virtual override returns (address) { - return _delegates[account]; - } - - /** - * @dev Gets the current votes balance for `account` - */ - function getVotes(address account) public view virtual override returns (uint256) { - uint256 pos = _checkpoints[account].length; - return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; - } - - /** - * @dev Retrieve the number of votes for `account` at the end of `blockNumber`. - * - * Requirements: - * - * - `blockNumber` must have been already mined - */ - function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { - require(blockNumber < block.number, "ERC20Votes: block not yet mined"); - return _checkpointsLookup(_checkpoints[account], blockNumber); - } - - /** - * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances. - * It is but NOT the sum of all the delegated votes! - * - * Requirements: - * - * - `blockNumber` must have been already mined - */ - function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) { - require(blockNumber < block.number, "ERC20Votes: block not yet mined"); - return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); - } - - /** - * @dev Lookup a value in a list of (sorted) checkpoints. - */ - function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) { - // We run a binary search to look for the earliest checkpoint taken after `blockNumber`. - // - // Initially we check if the block is recent to narrow the search range. - // During the loop, the index of the wanted checkpoint remains in the range [low-1, high). - // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant. - // - If the middle checkpoint is after `blockNumber`, we look in [low, mid) - // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high) - // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not - // out of bounds (in which case we're looking too far in the past and the result is 0). - // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is - // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out - // the same. - uint256 length = ckpts.length; - - uint256 low = 0; - uint256 high = length; - - if (length > 5) { - uint256 mid = length - Math.sqrt(length); - if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { - high = mid; - } else { - low = mid + 1; - } - } - - while (low < high) { - uint256 mid = Math.average(low, high); - if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { - high = mid; - } else { - low = mid + 1; - } - } - - return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes; - } - - /** - * @dev Delegate votes from the sender to `delegatee`. - */ - function delegate(address delegatee) public virtual override { - _delegate(_msgSender(), delegatee); - } - - /** - * @dev Delegates votes from signer to `delegatee` - */ - function delegateBySig( - address delegatee, - uint256 nonce, - uint256 expiry, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual override { - require(block.timestamp <= expiry, "ERC20Votes: signature expired"); - address signer = ECDSA.recover( - _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))), - v, - r, - s - ); - require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce"); - _delegate(signer, delegatee); - } - - /** - * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). - */ - function _maxSupply() internal view virtual returns (uint224) { - return type(uint224).max; - } - - /** - * @dev Snapshots the totalSupply after it has been increased. - */ - function _mint(address account, uint256 amount) internal virtual override { - super._mint(account, amount); - require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); - - _writeCheckpoint(_totalSupplyCheckpoints, _add, amount); - } - - /** - * @dev Snapshots the totalSupply after it has been decreased. - */ - function _burn(address account, uint256 amount) internal virtual override { - super._burn(account, amount); - - _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); - } - - /** - * @dev Move voting power when tokens are transferred. - * - * Emits a {IVotes-DelegateVotesChanged} event. - */ - function _afterTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual override { - super._afterTokenTransfer(from, to, amount); - - _moveVotingPower(delegates(from), delegates(to), amount); - } - - /** - * @dev Change delegation for `delegator` to `delegatee`. - * - * Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}. - */ - function _delegate(address delegator, address delegatee) internal virtual { - address currentDelegate = delegates(delegator); - uint256 delegatorBalance = balanceOf(delegator); - _delegates[delegator] = delegatee; - - emit DelegateChanged(delegator, currentDelegate, delegatee); - - _moveVotingPower(currentDelegate, delegatee, delegatorBalance); - } - - function _moveVotingPower( - address src, - address dst, - uint256 amount - ) private { - if (src != dst && amount > 0) { - if (src != address(0)) { - (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); - emit DelegateVotesChanged(src, oldWeight, newWeight); - } - - if (dst != address(0)) { - (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); - emit DelegateVotesChanged(dst, oldWeight, newWeight); - } - } - } - - function _writeCheckpoint( - Checkpoint[] storage ckpts, - function(uint256, uint256) view returns (uint256) op, - uint256 delta - ) private returns (uint256 oldWeight, uint256 newWeight) { - uint256 pos = ckpts.length; - - Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0) : _unsafeAccess(ckpts, pos - 1); - - oldWeight = oldCkpt.votes; - newWeight = op(oldWeight, delta); - - if (pos > 0 && oldCkpt.fromBlock == block.number) { - _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight); - } else { - ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); - } - } - - function _add(uint256 a, uint256 b) private pure returns (uint256) { - return a + b; - } - - function _subtract(uint256 a, uint256 b) private pure returns (uint256) { - return a - b; - } - - /** - * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. - */ - function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) { - assembly { - mstore(0, ckpts.slot) - result.slot := add(keccak256(0, 0x20), pos) - } - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) - -pragma solidity ^0.8.0; - -import "../IERC20.sol"; - -/** - * @dev Interface for the optional metadata functions from the ERC20 standard. - * - * _Available since v4.1._ - */ -interface IERC20Metadata is IERC20 { - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the symbol of the token. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the decimals places of the token. - */ - function decimals() external view returns (uint8); -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol) - -pragma solidity ^0.8.0; - -import "./draft-IERC20Permit.sol"; -import "../ERC20.sol"; -import "../../../utils/cryptography/ECDSA.sol"; -import "../../../utils/cryptography/EIP712.sol"; -import "../../../utils/Counters.sol"; - -/** - * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in - * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. - * - * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by - * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't - * need to send a transaction, and thus is not required to hold Ether at all. - * - * _Available since v3.4._ - */ -abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { - using Counters for Counters.Counter; - - mapping(address => Counters.Counter) private _nonces; - - // solhint-disable-next-line var-name-mixedcase - bytes32 private constant _PERMIT_TYPEHASH = - keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); - /** - * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`. - * However, to ensure consistency with the upgradeable transpiler, we will continue - * to reserve a slot. - * @custom:oz-renamed-from _PERMIT_TYPEHASH - */ - // solhint-disable-next-line var-name-mixedcase - bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT; - - /** - * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. - * - * It's a good idea to use the same `name` that is defined as the ERC20 token name. - */ - constructor(string memory name) EIP712(name, "1") {} - - /** - * @dev See {IERC20Permit-permit}. - */ - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual override { - require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); - - bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)); - - bytes32 hash = _hashTypedDataV4(structHash); - - address signer = ECDSA.recover(hash, v, r, s); - require(signer == owner, "ERC20Permit: invalid signature"); - - _approve(owner, spender, value); - } - - /** - * @dev See {IERC20Permit-nonces}. - */ - function nonces(address owner) public view virtual override returns (uint256) { - return _nonces[owner].current(); - } - - /** - * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. - */ - // solhint-disable-next-line func-name-mixedcase - function DOMAIN_SEPARATOR() external view override returns (bytes32) { - return _domainSeparatorV4(); - } - - /** - * @dev "Consume a nonce": return the current value and increment. - * - * _Available since v4.1._ - */ - function _useNonce(address owner) internal virtual returns (uint256 current) { - Counters.Counter storage nonce = _nonces[owner]; - current = nonce.current(); - nonce.increment(); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in - * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. - * - * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by - * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't - * need to send a transaction, and thus is not required to hold Ether at all. - */ -interface IERC20Permit { - /** - * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, - * given ``owner``'s signed approval. - * - * IMPORTANT: The same issues {IERC20-approve} has related to transaction - * ordering also apply here. - * - * Emits an {Approval} event. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `deadline` must be a timestamp in the future. - * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` - * over the EIP712-formatted function arguments. - * - the signature must use ``owner``'s current nonce (see {nonces}). - * - * For more information on the signature format, see the - * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP - * section]. - */ - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external; - - /** - * @dev Returns the current nonce for `owner`. This value must be - * included whenever a signature is generated for {permit}. - * - * Every successful call to {permit} increases ``owner``'s nonce by one. This - * prevents a signature from being used multiple times. - */ - function nonces(address owner) external view returns (uint256); - - /** - * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. - */ - // solhint-disable-next-line func-name-mixedcase - function DOMAIN_SEPARATOR() external view returns (bytes32); -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Context.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Counters.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) - -pragma solidity ^0.8.0; - -/** - * @title Counters - * @author Matt Condon (@shrugs) - * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number - * of elements in a mapping, issuing ERC721 ids, or counting request ids. - * - * Include with `using Counters for Counters.Counter;` - */ -library Counters { - struct Counter { - // This variable should never be directly accessed by users of the library: interactions must be restricted to - // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add - // this feature: see https://github.com/ethereum/solidity/issues/4637 - uint256 _value; // default: 0 - } - - function current(Counter storage counter) internal view returns (uint256) { - return counter._value; - } - - function increment(Counter storage counter) internal { - unchecked { - counter._value += 1; - } - } - - function decrement(Counter storage counter) internal { - uint256 value = counter._value; - require(value > 0, "Counter: decrement overflow"); - unchecked { - counter._value = value - 1; - } - } - - function reset(Counter storage counter) internal { - counter._value = 0; - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Strings.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) - -pragma solidity ^0.8.0; - -import "./math/Math.sol"; - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - unchecked { - uint256 length = Math.log10(value) + 1; - string memory buffer = new string(length); - uint256 ptr; - /// @solidity memory-safe-assembly - assembly { - ptr := add(buffer, add(32, length)) - } - while (true) { - ptr--; - /// @solidity memory-safe-assembly - assembly { - mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) - } - value /= 10; - if (value == 0) break; - } - return buffer; - } - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - unchecked { - return toHexString(value, Math.log256(value) + 1); - } - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/ECDSA.sol +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol) - -pragma solidity ^0.8.0; - -import "../Strings.sol"; - -/** - * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. - * - * These functions can be used to verify that a message was signed by the holder - * of the private keys of a given address. - */ -library ECDSA { - enum RecoverError { - NoError, - InvalidSignature, - InvalidSignatureLength, - InvalidSignatureS, - InvalidSignatureV // Deprecated in v4.8 - } - - function _throwError(RecoverError error) private pure { - if (error == RecoverError.NoError) { - return; // no error: do nothing - } else if (error == RecoverError.InvalidSignature) { - revert("ECDSA: invalid signature"); - } else if (error == RecoverError.InvalidSignatureLength) { - revert("ECDSA: invalid signature length"); - } else if (error == RecoverError.InvalidSignatureS) { - revert("ECDSA: invalid signature 's' value"); - } - } - - /** - * @dev Returns the address that signed a hashed message (`hash`) with - * `signature` or error string. This address can then be used for verification purposes. - * - * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: - * this function rejects them by requiring the `s` value to be in the lower - * half order, and the `v` value to be either 27 or 28. - * - * IMPORTANT: `hash` _must_ be the result of a hash operation for the - * verification to be secure: it is possible to craft signatures that - * recover to arbitrary addresses for non-hashed data. A safe way to ensure - * this is by receiving a hash of the original message (which may otherwise - * be too long), and then calling {toEthSignedMessageHash} on it. - * - * Documentation for signature generation: - * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] - * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] - * - * _Available since v4.3._ - */ - function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { - if (signature.length == 65) { - bytes32 r; - bytes32 s; - uint8 v; - // ecrecover takes the signature parameters, and the only way to get them - // currently is to use assembly. - /// @solidity memory-safe-assembly - assembly { - r := mload(add(signature, 0x20)) - s := mload(add(signature, 0x40)) - v := byte(0, mload(add(signature, 0x60))) - } - return tryRecover(hash, v, r, s); - } else { - return (address(0), RecoverError.InvalidSignatureLength); - } - } - - /** - * @dev Returns the address that signed a hashed message (`hash`) with - * `signature`. This address can then be used for verification purposes. - * - * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: - * this function rejects them by requiring the `s` value to be in the lower - * half order, and the `v` value to be either 27 or 28. - * - * IMPORTANT: `hash` _must_ be the result of a hash operation for the - * verification to be secure: it is possible to craft signatures that - * recover to arbitrary addresses for non-hashed data. A safe way to ensure - * this is by receiving a hash of the original message (which may otherwise - * be too long), and then calling {toEthSignedMessageHash} on it. - */ - function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, signature); - _throwError(error); - return recovered; - } - - /** - * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. - * - * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] - * - * _Available since v4.3._ - */ - function tryRecover( - bytes32 hash, - bytes32 r, - bytes32 vs - ) internal pure returns (address, RecoverError) { - bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); - uint8 v = uint8((uint256(vs) >> 255) + 27); - return tryRecover(hash, v, r, s); - } - - /** - * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. - * - * _Available since v4.2._ - */ - function recover( - bytes32 hash, - bytes32 r, - bytes32 vs - ) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, r, vs); - _throwError(error); - return recovered; - } - - /** - * @dev Overload of {ECDSA-tryRecover} that receives the `v`, - * `r` and `s` signature fields separately. - * - * _Available since v4.3._ - */ - function tryRecover( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address, RecoverError) { - // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature - // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines - // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most - // signatures from current libraries generate a unique signature with an s-value in the lower half order. - // - // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value - // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or - // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept - // these malleable signatures as well. - if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { - return (address(0), RecoverError.InvalidSignatureS); - } - - // If the signature is valid (and not malleable), return the signer address - address signer = ecrecover(hash, v, r, s); - if (signer == address(0)) { - return (address(0), RecoverError.InvalidSignature); - } - - return (signer, RecoverError.NoError); - } - - /** - * @dev Overload of {ECDSA-recover} that receives the `v`, - * `r` and `s` signature fields separately. - */ - function recover( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address) { - (address recovered, RecoverError error) = tryRecover(hash, v, r, s); - _throwError(error); - return recovered; - } - - /** - * @dev Returns an Ethereum Signed Message, created from a `hash`. This - * produces hash corresponding to the one signed with the - * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] - * JSON-RPC method as part of EIP-191. - * - * See {recover}. - */ - function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { - // 32 is the length in bytes of hash, - // enforced by the type signature above - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); - } - - /** - * @dev Returns an Ethereum Signed Message, created from `s`. This - * produces hash corresponding to the one signed with the - * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] - * JSON-RPC method as part of EIP-191. - * - * See {recover}. - */ - function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); - } - - /** - * @dev Returns an Ethereum Signed Typed Data, created from a - * `domainSeparator` and a `structHash`. This produces hash corresponding - * to the one signed with the - * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] - * JSON-RPC method as part of EIP-712. - * - * See {recover}. - */ - function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/EIP712.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol) - -pragma solidity ^0.8.0; - -import "./ECDSA.sol"; - -/** - * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. - * - * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, - * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding - * they need in their contracts using a combination of `abi.encode` and `keccak256`. - * - * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding - * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA - * ({_hashTypedDataV4}). - * - * The implementation of the domain separator was designed to be as efficient as possible while still properly updating - * the chain id to protect against replay attacks on an eventual fork of the chain. - * - * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method - * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. - * - * _Available since v3.4._ - */ -abstract contract EIP712 { - /* solhint-disable var-name-mixedcase */ - // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to - // invalidate the cached domain separator if the chain id changes. - bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; - uint256 private immutable _CACHED_CHAIN_ID; - address private immutable _CACHED_THIS; - - bytes32 private immutable _HASHED_NAME; - bytes32 private immutable _HASHED_VERSION; - bytes32 private immutable _TYPE_HASH; - - /* solhint-enable var-name-mixedcase */ - - /** - * @dev Initializes the domain separator and parameter caches. - * - * The meaning of `name` and `version` is specified in - * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: - * - * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. - * - `version`: the current major version of the signing domain. - * - * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart - * contract upgrade]. - */ - constructor(string memory name, string memory version) { - bytes32 hashedName = keccak256(bytes(name)); - bytes32 hashedVersion = keccak256(bytes(version)); - bytes32 typeHash = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - _HASHED_NAME = hashedName; - _HASHED_VERSION = hashedVersion; - _CACHED_CHAIN_ID = block.chainid; - _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); - _CACHED_THIS = address(this); - _TYPE_HASH = typeHash; - } - - /** - * @dev Returns the domain separator for the current chain. - */ - function _domainSeparatorV4() internal view returns (bytes32) { - if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { - return _CACHED_DOMAIN_SEPARATOR; - } else { - return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); - } - } - - function _buildDomainSeparator( - bytes32 typeHash, - bytes32 nameHash, - bytes32 versionHash - ) private view returns (bytes32) { - return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); - } - - /** - * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this - * function returns the hash of the fully encoded EIP712 message for this domain. - * - * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: - * - * ```solidity - * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( - * keccak256("Mail(address to,string contents)"), - * mailTo, - * keccak256(bytes(mailContents)) - * ))); - * address signer = ECDSA.recover(digest, signature); - * ``` - */ - function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { - return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/Math.sol +++ /dev/null @@ -1,345 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Standard math utilities missing in the Solidity language. - */ -library Math { - enum Rounding { - Down, // Toward negative infinity - Up, // Toward infinity - Zero // Toward zero - } - - /** - * @dev Returns the largest of two numbers. - */ - function max(uint256 a, uint256 b) internal pure returns (uint256) { - return a > b ? a : b; - } - - /** - * @dev Returns the smallest of two numbers. - */ - function min(uint256 a, uint256 b) internal pure returns (uint256) { - return a < b ? a : b; - } - - /** - * @dev Returns the average of two numbers. The result is rounded towards - * zero. - */ - function average(uint256 a, uint256 b) internal pure returns (uint256) { - // (a + b) / 2 can overflow. - return (a & b) + (a ^ b) / 2; - } - - /** - * @dev Returns the ceiling of the division of two numbers. - * - * This differs from standard division with `/` in that it rounds up instead - * of rounding down. - */ - function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { - // (a + b - 1) / b can overflow on addition, so we distribute. - return a == 0 ? 0 : (a - 1) / b + 1; - } - - /** - * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 - * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) - * with further edits by Uniswap Labs also under MIT license. - */ - function mulDiv( - uint256 x, - uint256 y, - uint256 denominator - ) internal pure returns (uint256 result) { - unchecked { - // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use - // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 - // variables such that product = prod1 * 2^256 + prod0. - uint256 prod0; // Least significant 256 bits of the product - uint256 prod1; // Most significant 256 bits of the product - assembly { - let mm := mulmod(x, y, not(0)) - prod0 := mul(x, y) - prod1 := sub(sub(mm, prod0), lt(mm, prod0)) - } - - // Handle non-overflow cases, 256 by 256 division. - if (prod1 == 0) { - return prod0 / denominator; - } - - // Make sure the result is less than 2^256. Also prevents denominator == 0. - require(denominator > prod1); - - /////////////////////////////////////////////// - // 512 by 256 division. - /////////////////////////////////////////////// - - // Make division exact by subtracting the remainder from [prod1 prod0]. - uint256 remainder; - assembly { - // Compute remainder using mulmod. - remainder := mulmod(x, y, denominator) - - // Subtract 256 bit number from 512 bit number. - prod1 := sub(prod1, gt(remainder, prod0)) - prod0 := sub(prod0, remainder) - } - - // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. - // See https://cs.stackexchange.com/q/138556/92363. - - // Does not overflow because the denominator cannot be zero at this stage in the function. - uint256 twos = denominator & (~denominator + 1); - assembly { - // Divide denominator by twos. - denominator := div(denominator, twos) - - // Divide [prod1 prod0] by twos. - prod0 := div(prod0, twos) - - // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. - twos := add(div(sub(0, twos), twos), 1) - } - - // Shift in bits from prod1 into prod0. - prod0 |= prod1 * twos; - - // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such - // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for - // four bits. That is, denominator * inv = 1 mod 2^4. - uint256 inverse = (3 * denominator) ^ 2; - - // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works - // in modular arithmetic, doubling the correct bits in each step. - inverse *= 2 - denominator * inverse; // inverse mod 2^8 - inverse *= 2 - denominator * inverse; // inverse mod 2^16 - inverse *= 2 - denominator * inverse; // inverse mod 2^32 - inverse *= 2 - denominator * inverse; // inverse mod 2^64 - inverse *= 2 - denominator * inverse; // inverse mod 2^128 - inverse *= 2 - denominator * inverse; // inverse mod 2^256 - - // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. - // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is - // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 - // is no longer required. - result = prod0 * inverse; - return result; - } - } - - /** - * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. - */ - function mulDiv( - uint256 x, - uint256 y, - uint256 denominator, - Rounding rounding - ) internal pure returns (uint256) { - uint256 result = mulDiv(x, y, denominator); - if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { - result += 1; - } - return result; - } - - /** - * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. - * - * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). - */ - function sqrt(uint256 a) internal pure returns (uint256) { - if (a == 0) { - return 0; - } - - // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. - // - // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have - // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. - // - // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` - // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` - // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` - // - // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. - uint256 result = 1 << (log2(a) >> 1); - - // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, - // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at - // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision - // into the expected uint128 result. - unchecked { - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - return min(result, a / result); - } - } - - /** - * @notice Calculates sqrt(a), following the selected rounding direction. - */ - function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { - unchecked { - uint256 result = sqrt(a); - return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); - } - } - - /** - * @dev Return the log in base 2, rounded down, of a positive value. - * Returns 0 if given 0. - */ - function log2(uint256 value) internal pure returns (uint256) { - uint256 result = 0; - unchecked { - if (value >> 128 > 0) { - value >>= 128; - result += 128; - } - if (value >> 64 > 0) { - value >>= 64; - result += 64; - } - if (value >> 32 > 0) { - value >>= 32; - result += 32; - } - if (value >> 16 > 0) { - value >>= 16; - result += 16; - } - if (value >> 8 > 0) { - value >>= 8; - result += 8; - } - if (value >> 4 > 0) { - value >>= 4; - result += 4; - } - if (value >> 2 > 0) { - value >>= 2; - result += 2; - } - if (value >> 1 > 0) { - result += 1; - } - } - return result; - } - - /** - * @dev Return the log in base 2, following the selected rounding direction, of a positive value. - * Returns 0 if given 0. - */ - function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { - unchecked { - uint256 result = log2(value); - return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); - } - } - - /** - * @dev Return the log in base 10, rounded down, of a positive value. - * Returns 0 if given 0. - */ - function log10(uint256 value) internal pure returns (uint256) { - uint256 result = 0; - unchecked { - if (value >= 10**64) { - value /= 10**64; - result += 64; - } - if (value >= 10**32) { - value /= 10**32; - result += 32; - } - if (value >= 10**16) { - value /= 10**16; - result += 16; - } - if (value >= 10**8) { - value /= 10**8; - result += 8; - } - if (value >= 10**4) { - value /= 10**4; - result += 4; - } - if (value >= 10**2) { - value /= 10**2; - result += 2; - } - if (value >= 10**1) { - result += 1; - } - } - return result; - } - - /** - * @dev Return the log in base 10, following the selected rounding direction, of a positive value. - * Returns 0 if given 0. - */ - function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { - unchecked { - uint256 result = log10(value); - return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); - } - } - - /** - * @dev Return the log in base 256, rounded down, of a positive value. - * Returns 0 if given 0. - * - * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. - */ - function log256(uint256 value) internal pure returns (uint256) { - uint256 result = 0; - unchecked { - if (value >> 128 > 0) { - value >>= 128; - result += 16; - } - if (value >> 64 > 0) { - value >>= 64; - result += 8; - } - if (value >> 32 > 0) { - value >>= 32; - result += 4; - } - if (value >> 16 > 0) { - value >>= 16; - result += 2; - } - if (value >> 8 > 0) { - result += 1; - } - } - return result; - } - - /** - * @dev Return the log in base 10, following the selected rounding direction, of a positive value. - * Returns 0 if given 0. - */ - function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { - unchecked { - uint256 result = log256(value); - return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); - } - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/SafeCast.sol +++ /dev/null @@ -1,1136 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) -// This file was procedurally generated from scripts/generate/templates/SafeCast.js. - -pragma solidity ^0.8.0; - -/** - * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow - * checks. - * - * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can - * easily result in undesired exploitation or bugs, since developers usually - * assume that overflows raise errors. `SafeCast` restores this intuition by - * reverting the transaction when such an operation overflows. - * - * Using this library instead of the unchecked operations eliminates an entire - * class of bugs, so it's recommended to use it always. - * - * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing - * all math on `uint256` and `int256` and then downcasting. - */ -library SafeCast { - /** - * @dev Returns the downcasted uint248 from uint256, reverting on - * overflow (when the input is greater than largest uint248). - * - * Counterpart to Solidity's `uint248` operator. - * - * Requirements: - * - * - input must fit into 248 bits - * - * _Available since v4.7._ - */ - function toUint248(uint256 value) internal pure returns (uint248) { - require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); - return uint248(value); - } - - /** - * @dev Returns the downcasted uint240 from uint256, reverting on - * overflow (when the input is greater than largest uint240). - * - * Counterpart to Solidity's `uint240` operator. - * - * Requirements: - * - * - input must fit into 240 bits - * - * _Available since v4.7._ - */ - function toUint240(uint256 value) internal pure returns (uint240) { - require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); - return uint240(value); - } - - /** - * @dev Returns the downcasted uint232 from uint256, reverting on - * overflow (when the input is greater than largest uint232). - * - * Counterpart to Solidity's `uint232` operator. - * - * Requirements: - * - * - input must fit into 232 bits - * - * _Available since v4.7._ - */ - function toUint232(uint256 value) internal pure returns (uint232) { - require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); - return uint232(value); - } - - /** - * @dev Returns the downcasted uint224 from uint256, reverting on - * overflow (when the input is greater than largest uint224). - * - * Counterpart to Solidity's `uint224` operator. - * - * Requirements: - * - * - input must fit into 224 bits - * - * _Available since v4.2._ - */ - function toUint224(uint256 value) internal pure returns (uint224) { - require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); - return uint224(value); - } - - /** - * @dev Returns the downcasted uint216 from uint256, reverting on - * overflow (when the input is greater than largest uint216). - * - * Counterpart to Solidity's `uint216` operator. - * - * Requirements: - * - * - input must fit into 216 bits - * - * _Available since v4.7._ - */ - function toUint216(uint256 value) internal pure returns (uint216) { - require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); - return uint216(value); - } - - /** - * @dev Returns the downcasted uint208 from uint256, reverting on - * overflow (when the input is greater than largest uint208). - * - * Counterpart to Solidity's `uint208` operator. - * - * Requirements: - * - * - input must fit into 208 bits - * - * _Available since v4.7._ - */ - function toUint208(uint256 value) internal pure returns (uint208) { - require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); - return uint208(value); - } - - /** - * @dev Returns the downcasted uint200 from uint256, reverting on - * overflow (when the input is greater than largest uint200). - * - * Counterpart to Solidity's `uint200` operator. - * - * Requirements: - * - * - input must fit into 200 bits - * - * _Available since v4.7._ - */ - function toUint200(uint256 value) internal pure returns (uint200) { - require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); - return uint200(value); - } - - /** - * @dev Returns the downcasted uint192 from uint256, reverting on - * overflow (when the input is greater than largest uint192). - * - * Counterpart to Solidity's `uint192` operator. - * - * Requirements: - * - * - input must fit into 192 bits - * - * _Available since v4.7._ - */ - function toUint192(uint256 value) internal pure returns (uint192) { - require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); - return uint192(value); - } - - /** - * @dev Returns the downcasted uint184 from uint256, reverting on - * overflow (when the input is greater than largest uint184). - * - * Counterpart to Solidity's `uint184` operator. - * - * Requirements: - * - * - input must fit into 184 bits - * - * _Available since v4.7._ - */ - function toUint184(uint256 value) internal pure returns (uint184) { - require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); - return uint184(value); - } - - /** - * @dev Returns the downcasted uint176 from uint256, reverting on - * overflow (when the input is greater than largest uint176). - * - * Counterpart to Solidity's `uint176` operator. - * - * Requirements: - * - * - input must fit into 176 bits - * - * _Available since v4.7._ - */ - function toUint176(uint256 value) internal pure returns (uint176) { - require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); - return uint176(value); - } - - /** - * @dev Returns the downcasted uint168 from uint256, reverting on - * overflow (when the input is greater than largest uint168). - * - * Counterpart to Solidity's `uint168` operator. - * - * Requirements: - * - * - input must fit into 168 bits - * - * _Available since v4.7._ - */ - function toUint168(uint256 value) internal pure returns (uint168) { - require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); - return uint168(value); - } - - /** - * @dev Returns the downcasted uint160 from uint256, reverting on - * overflow (when the input is greater than largest uint160). - * - * Counterpart to Solidity's `uint160` operator. - * - * Requirements: - * - * - input must fit into 160 bits - * - * _Available since v4.7._ - */ - function toUint160(uint256 value) internal pure returns (uint160) { - require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); - return uint160(value); - } - - /** - * @dev Returns the downcasted uint152 from uint256, reverting on - * overflow (when the input is greater than largest uint152). - * - * Counterpart to Solidity's `uint152` operator. - * - * Requirements: - * - * - input must fit into 152 bits - * - * _Available since v4.7._ - */ - function toUint152(uint256 value) internal pure returns (uint152) { - require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); - return uint152(value); - } - - /** - * @dev Returns the downcasted uint144 from uint256, reverting on - * overflow (when the input is greater than largest uint144). - * - * Counterpart to Solidity's `uint144` operator. - * - * Requirements: - * - * - input must fit into 144 bits - * - * _Available since v4.7._ - */ - function toUint144(uint256 value) internal pure returns (uint144) { - require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); - return uint144(value); - } - - /** - * @dev Returns the downcasted uint136 from uint256, reverting on - * overflow (when the input is greater than largest uint136). - * - * Counterpart to Solidity's `uint136` operator. - * - * Requirements: - * - * - input must fit into 136 bits - * - * _Available since v4.7._ - */ - function toUint136(uint256 value) internal pure returns (uint136) { - require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); - return uint136(value); - } - - /** - * @dev Returns the downcasted uint128 from uint256, reverting on - * overflow (when the input is greater than largest uint128). - * - * Counterpart to Solidity's `uint128` operator. - * - * Requirements: - * - * - input must fit into 128 bits - * - * _Available since v2.5._ - */ - function toUint128(uint256 value) internal pure returns (uint128) { - require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); - return uint128(value); - } - - /** - * @dev Returns the downcasted uint120 from uint256, reverting on - * overflow (when the input is greater than largest uint120). - * - * Counterpart to Solidity's `uint120` operator. - * - * Requirements: - * - * - input must fit into 120 bits - * - * _Available since v4.7._ - */ - function toUint120(uint256 value) internal pure returns (uint120) { - require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); - return uint120(value); - } - - /** - * @dev Returns the downcasted uint112 from uint256, reverting on - * overflow (when the input is greater than largest uint112). - * - * Counterpart to Solidity's `uint112` operator. - * - * Requirements: - * - * - input must fit into 112 bits - * - * _Available since v4.7._ - */ - function toUint112(uint256 value) internal pure returns (uint112) { - require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); - return uint112(value); - } - - /** - * @dev Returns the downcasted uint104 from uint256, reverting on - * overflow (when the input is greater than largest uint104). - * - * Counterpart to Solidity's `uint104` operator. - * - * Requirements: - * - * - input must fit into 104 bits - * - * _Available since v4.7._ - */ - function toUint104(uint256 value) internal pure returns (uint104) { - require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); - return uint104(value); - } - - /** - * @dev Returns the downcasted uint96 from uint256, reverting on - * overflow (when the input is greater than largest uint96). - * - * Counterpart to Solidity's `uint96` operator. - * - * Requirements: - * - * - input must fit into 96 bits - * - * _Available since v4.2._ - */ - function toUint96(uint256 value) internal pure returns (uint96) { - require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); - return uint96(value); - } - - /** - * @dev Returns the downcasted uint88 from uint256, reverting on - * overflow (when the input is greater than largest uint88). - * - * Counterpart to Solidity's `uint88` operator. - * - * Requirements: - * - * - input must fit into 88 bits - * - * _Available since v4.7._ - */ - function toUint88(uint256 value) internal pure returns (uint88) { - require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); - return uint88(value); - } - - /** - * @dev Returns the downcasted uint80 from uint256, reverting on - * overflow (when the input is greater than largest uint80). - * - * Counterpart to Solidity's `uint80` operator. - * - * Requirements: - * - * - input must fit into 80 bits - * - * _Available since v4.7._ - */ - function toUint80(uint256 value) internal pure returns (uint80) { - require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); - return uint80(value); - } - - /** - * @dev Returns the downcasted uint72 from uint256, reverting on - * overflow (when the input is greater than largest uint72). - * - * Counterpart to Solidity's `uint72` operator. - * - * Requirements: - * - * - input must fit into 72 bits - * - * _Available since v4.7._ - */ - function toUint72(uint256 value) internal pure returns (uint72) { - require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); - return uint72(value); - } - - /** - * @dev Returns the downcasted uint64 from uint256, reverting on - * overflow (when the input is greater than largest uint64). - * - * Counterpart to Solidity's `uint64` operator. - * - * Requirements: - * - * - input must fit into 64 bits - * - * _Available since v2.5._ - */ - function toUint64(uint256 value) internal pure returns (uint64) { - require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); - return uint64(value); - } - - /** - * @dev Returns the downcasted uint56 from uint256, reverting on - * overflow (when the input is greater than largest uint56). - * - * Counterpart to Solidity's `uint56` operator. - * - * Requirements: - * - * - input must fit into 56 bits - * - * _Available since v4.7._ - */ - function toUint56(uint256 value) internal pure returns (uint56) { - require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); - return uint56(value); - } - - /** - * @dev Returns the downcasted uint48 from uint256, reverting on - * overflow (when the input is greater than largest uint48). - * - * Counterpart to Solidity's `uint48` operator. - * - * Requirements: - * - * - input must fit into 48 bits - * - * _Available since v4.7._ - */ - function toUint48(uint256 value) internal pure returns (uint48) { - require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); - return uint48(value); - } - - /** - * @dev Returns the downcasted uint40 from uint256, reverting on - * overflow (when the input is greater than largest uint40). - * - * Counterpart to Solidity's `uint40` operator. - * - * Requirements: - * - * - input must fit into 40 bits - * - * _Available since v4.7._ - */ - function toUint40(uint256 value) internal pure returns (uint40) { - require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); - return uint40(value); - } - - /** - * @dev Returns the downcasted uint32 from uint256, reverting on - * overflow (when the input is greater than largest uint32). - * - * Counterpart to Solidity's `uint32` operator. - * - * Requirements: - * - * - input must fit into 32 bits - * - * _Available since v2.5._ - */ - function toUint32(uint256 value) internal pure returns (uint32) { - require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); - return uint32(value); - } - - /** - * @dev Returns the downcasted uint24 from uint256, reverting on - * overflow (when the input is greater than largest uint24). - * - * Counterpart to Solidity's `uint24` operator. - * - * Requirements: - * - * - input must fit into 24 bits - * - * _Available since v4.7._ - */ - function toUint24(uint256 value) internal pure returns (uint24) { - require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); - return uint24(value); - } - - /** - * @dev Returns the downcasted uint16 from uint256, reverting on - * overflow (when the input is greater than largest uint16). - * - * Counterpart to Solidity's `uint16` operator. - * - * Requirements: - * - * - input must fit into 16 bits - * - * _Available since v2.5._ - */ - function toUint16(uint256 value) internal pure returns (uint16) { - require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); - return uint16(value); - } - - /** - * @dev Returns the downcasted uint8 from uint256, reverting on - * overflow (when the input is greater than largest uint8). - * - * Counterpart to Solidity's `uint8` operator. - * - * Requirements: - * - * - input must fit into 8 bits - * - * _Available since v2.5._ - */ - function toUint8(uint256 value) internal pure returns (uint8) { - require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); - return uint8(value); - } - - /** - * @dev Converts a signed int256 into an unsigned uint256. - * - * Requirements: - * - * - input must be greater than or equal to 0. - * - * _Available since v3.0._ - */ - function toUint256(int256 value) internal pure returns (uint256) { - require(value >= 0, "SafeCast: value must be positive"); - return uint256(value); - } - - /** - * @dev Returns the downcasted int248 from int256, reverting on - * overflow (when the input is less than smallest int248 or - * greater than largest int248). - * - * Counterpart to Solidity's `int248` operator. - * - * Requirements: - * - * - input must fit into 248 bits - * - * _Available since v4.7._ - */ - function toInt248(int256 value) internal pure returns (int248 downcasted) { - downcasted = int248(value); - require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); - } - - /** - * @dev Returns the downcasted int240 from int256, reverting on - * overflow (when the input is less than smallest int240 or - * greater than largest int240). - * - * Counterpart to Solidity's `int240` operator. - * - * Requirements: - * - * - input must fit into 240 bits - * - * _Available since v4.7._ - */ - function toInt240(int256 value) internal pure returns (int240 downcasted) { - downcasted = int240(value); - require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); - } - - /** - * @dev Returns the downcasted int232 from int256, reverting on - * overflow (when the input is less than smallest int232 or - * greater than largest int232). - * - * Counterpart to Solidity's `int232` operator. - * - * Requirements: - * - * - input must fit into 232 bits - * - * _Available since v4.7._ - */ - function toInt232(int256 value) internal pure returns (int232 downcasted) { - downcasted = int232(value); - require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); - } - - /** - * @dev Returns the downcasted int224 from int256, reverting on - * overflow (when the input is less than smallest int224 or - * greater than largest int224). - * - * Counterpart to Solidity's `int224` operator. - * - * Requirements: - * - * - input must fit into 224 bits - * - * _Available since v4.7._ - */ - function toInt224(int256 value) internal pure returns (int224 downcasted) { - downcasted = int224(value); - require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); - } - - /** - * @dev Returns the downcasted int216 from int256, reverting on - * overflow (when the input is less than smallest int216 or - * greater than largest int216). - * - * Counterpart to Solidity's `int216` operator. - * - * Requirements: - * - * - input must fit into 216 bits - * - * _Available since v4.7._ - */ - function toInt216(int256 value) internal pure returns (int216 downcasted) { - downcasted = int216(value); - require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); - } - - /** - * @dev Returns the downcasted int208 from int256, reverting on - * overflow (when the input is less than smallest int208 or - * greater than largest int208). - * - * Counterpart to Solidity's `int208` operator. - * - * Requirements: - * - * - input must fit into 208 bits - * - * _Available since v4.7._ - */ - function toInt208(int256 value) internal pure returns (int208 downcasted) { - downcasted = int208(value); - require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); - } - - /** - * @dev Returns the downcasted int200 from int256, reverting on - * overflow (when the input is less than smallest int200 or - * greater than largest int200). - * - * Counterpart to Solidity's `int200` operator. - * - * Requirements: - * - * - input must fit into 200 bits - * - * _Available since v4.7._ - */ - function toInt200(int256 value) internal pure returns (int200 downcasted) { - downcasted = int200(value); - require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); - } - - /** - * @dev Returns the downcasted int192 from int256, reverting on - * overflow (when the input is less than smallest int192 or - * greater than largest int192). - * - * Counterpart to Solidity's `int192` operator. - * - * Requirements: - * - * - input must fit into 192 bits - * - * _Available since v4.7._ - */ - function toInt192(int256 value) internal pure returns (int192 downcasted) { - downcasted = int192(value); - require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); - } - - /** - * @dev Returns the downcasted int184 from int256, reverting on - * overflow (when the input is less than smallest int184 or - * greater than largest int184). - * - * Counterpart to Solidity's `int184` operator. - * - * Requirements: - * - * - input must fit into 184 bits - * - * _Available since v4.7._ - */ - function toInt184(int256 value) internal pure returns (int184 downcasted) { - downcasted = int184(value); - require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); - } - - /** - * @dev Returns the downcasted int176 from int256, reverting on - * overflow (when the input is less than smallest int176 or - * greater than largest int176). - * - * Counterpart to Solidity's `int176` operator. - * - * Requirements: - * - * - input must fit into 176 bits - * - * _Available since v4.7._ - */ - function toInt176(int256 value) internal pure returns (int176 downcasted) { - downcasted = int176(value); - require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); - } - - /** - * @dev Returns the downcasted int168 from int256, reverting on - * overflow (when the input is less than smallest int168 or - * greater than largest int168). - * - * Counterpart to Solidity's `int168` operator. - * - * Requirements: - * - * - input must fit into 168 bits - * - * _Available since v4.7._ - */ - function toInt168(int256 value) internal pure returns (int168 downcasted) { - downcasted = int168(value); - require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); - } - - /** - * @dev Returns the downcasted int160 from int256, reverting on - * overflow (when the input is less than smallest int160 or - * greater than largest int160). - * - * Counterpart to Solidity's `int160` operator. - * - * Requirements: - * - * - input must fit into 160 bits - * - * _Available since v4.7._ - */ - function toInt160(int256 value) internal pure returns (int160 downcasted) { - downcasted = int160(value); - require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); - } - - /** - * @dev Returns the downcasted int152 from int256, reverting on - * overflow (when the input is less than smallest int152 or - * greater than largest int152). - * - * Counterpart to Solidity's `int152` operator. - * - * Requirements: - * - * - input must fit into 152 bits - * - * _Available since v4.7._ - */ - function toInt152(int256 value) internal pure returns (int152 downcasted) { - downcasted = int152(value); - require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); - } - - /** - * @dev Returns the downcasted int144 from int256, reverting on - * overflow (when the input is less than smallest int144 or - * greater than largest int144). - * - * Counterpart to Solidity's `int144` operator. - * - * Requirements: - * - * - input must fit into 144 bits - * - * _Available since v4.7._ - */ - function toInt144(int256 value) internal pure returns (int144 downcasted) { - downcasted = int144(value); - require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); - } - - /** - * @dev Returns the downcasted int136 from int256, reverting on - * overflow (when the input is less than smallest int136 or - * greater than largest int136). - * - * Counterpart to Solidity's `int136` operator. - * - * Requirements: - * - * - input must fit into 136 bits - * - * _Available since v4.7._ - */ - function toInt136(int256 value) internal pure returns (int136 downcasted) { - downcasted = int136(value); - require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); - } - - /** - * @dev Returns the downcasted int128 from int256, reverting on - * overflow (when the input is less than smallest int128 or - * greater than largest int128). - * - * Counterpart to Solidity's `int128` operator. - * - * Requirements: - * - * - input must fit into 128 bits - * - * _Available since v3.1._ - */ - function toInt128(int256 value) internal pure returns (int128 downcasted) { - downcasted = int128(value); - require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); - } - - /** - * @dev Returns the downcasted int120 from int256, reverting on - * overflow (when the input is less than smallest int120 or - * greater than largest int120). - * - * Counterpart to Solidity's `int120` operator. - * - * Requirements: - * - * - input must fit into 120 bits - * - * _Available since v4.7._ - */ - function toInt120(int256 value) internal pure returns (int120 downcasted) { - downcasted = int120(value); - require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); - } - - /** - * @dev Returns the downcasted int112 from int256, reverting on - * overflow (when the input is less than smallest int112 or - * greater than largest int112). - * - * Counterpart to Solidity's `int112` operator. - * - * Requirements: - * - * - input must fit into 112 bits - * - * _Available since v4.7._ - */ - function toInt112(int256 value) internal pure returns (int112 downcasted) { - downcasted = int112(value); - require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); - } - - /** - * @dev Returns the downcasted int104 from int256, reverting on - * overflow (when the input is less than smallest int104 or - * greater than largest int104). - * - * Counterpart to Solidity's `int104` operator. - * - * Requirements: - * - * - input must fit into 104 bits - * - * _Available since v4.7._ - */ - function toInt104(int256 value) internal pure returns (int104 downcasted) { - downcasted = int104(value); - require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); - } - - /** - * @dev Returns the downcasted int96 from int256, reverting on - * overflow (when the input is less than smallest int96 or - * greater than largest int96). - * - * Counterpart to Solidity's `int96` operator. - * - * Requirements: - * - * - input must fit into 96 bits - * - * _Available since v4.7._ - */ - function toInt96(int256 value) internal pure returns (int96 downcasted) { - downcasted = int96(value); - require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); - } - - /** - * @dev Returns the downcasted int88 from int256, reverting on - * overflow (when the input is less than smallest int88 or - * greater than largest int88). - * - * Counterpart to Solidity's `int88` operator. - * - * Requirements: - * - * - input must fit into 88 bits - * - * _Available since v4.7._ - */ - function toInt88(int256 value) internal pure returns (int88 downcasted) { - downcasted = int88(value); - require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); - } - - /** - * @dev Returns the downcasted int80 from int256, reverting on - * overflow (when the input is less than smallest int80 or - * greater than largest int80). - * - * Counterpart to Solidity's `int80` operator. - * - * Requirements: - * - * - input must fit into 80 bits - * - * _Available since v4.7._ - */ - function toInt80(int256 value) internal pure returns (int80 downcasted) { - downcasted = int80(value); - require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); - } - - /** - * @dev Returns the downcasted int72 from int256, reverting on - * overflow (when the input is less than smallest int72 or - * greater than largest int72). - * - * Counterpart to Solidity's `int72` operator. - * - * Requirements: - * - * - input must fit into 72 bits - * - * _Available since v4.7._ - */ - function toInt72(int256 value) internal pure returns (int72 downcasted) { - downcasted = int72(value); - require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); - } - - /** - * @dev Returns the downcasted int64 from int256, reverting on - * overflow (when the input is less than smallest int64 or - * greater than largest int64). - * - * Counterpart to Solidity's `int64` operator. - * - * Requirements: - * - * - input must fit into 64 bits - * - * _Available since v3.1._ - */ - function toInt64(int256 value) internal pure returns (int64 downcasted) { - downcasted = int64(value); - require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); - } - - /** - * @dev Returns the downcasted int56 from int256, reverting on - * overflow (when the input is less than smallest int56 or - * greater than largest int56). - * - * Counterpart to Solidity's `int56` operator. - * - * Requirements: - * - * - input must fit into 56 bits - * - * _Available since v4.7._ - */ - function toInt56(int256 value) internal pure returns (int56 downcasted) { - downcasted = int56(value); - require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); - } - - /** - * @dev Returns the downcasted int48 from int256, reverting on - * overflow (when the input is less than smallest int48 or - * greater than largest int48). - * - * Counterpart to Solidity's `int48` operator. - * - * Requirements: - * - * - input must fit into 48 bits - * - * _Available since v4.7._ - */ - function toInt48(int256 value) internal pure returns (int48 downcasted) { - downcasted = int48(value); - require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); - } - - /** - * @dev Returns the downcasted int40 from int256, reverting on - * overflow (when the input is less than smallest int40 or - * greater than largest int40). - * - * Counterpart to Solidity's `int40` operator. - * - * Requirements: - * - * - input must fit into 40 bits - * - * _Available since v4.7._ - */ - function toInt40(int256 value) internal pure returns (int40 downcasted) { - downcasted = int40(value); - require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); - } - - /** - * @dev Returns the downcasted int32 from int256, reverting on - * overflow (when the input is less than smallest int32 or - * greater than largest int32). - * - * Counterpart to Solidity's `int32` operator. - * - * Requirements: - * - * - input must fit into 32 bits - * - * _Available since v3.1._ - */ - function toInt32(int256 value) internal pure returns (int32 downcasted) { - downcasted = int32(value); - require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); - } - - /** - * @dev Returns the downcasted int24 from int256, reverting on - * overflow (when the input is less than smallest int24 or - * greater than largest int24). - * - * Counterpart to Solidity's `int24` operator. - * - * Requirements: - * - * - input must fit into 24 bits - * - * _Available since v4.7._ - */ - function toInt24(int256 value) internal pure returns (int24 downcasted) { - downcasted = int24(value); - require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); - } - - /** - * @dev Returns the downcasted int16 from int256, reverting on - * overflow (when the input is less than smallest int16 or - * greater than largest int16). - * - * Counterpart to Solidity's `int16` operator. - * - * Requirements: - * - * - input must fit into 16 bits - * - * _Available since v3.1._ - */ - function toInt16(int256 value) internal pure returns (int16 downcasted) { - downcasted = int16(value); - require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); - } - - /** - * @dev Returns the downcasted int8 from int256, reverting on - * overflow (when the input is less than smallest int8 or - * greater than largest int8). - * - * Counterpart to Solidity's `int8` operator. - * - * Requirements: - * - * - input must fit into 8 bits - * - * _Available since v3.1._ - */ - function toInt8(int256 value) internal pure returns (int8 downcasted) { - downcasted = int8(value); - require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); - } - - /** - * @dev Converts an unsigned uint256 into a signed int256. - * - * Requirements: - * - * - input must be less than or equal to maxInt256. - * - * _Available since v3.0._ - */ - function toInt256(uint256 value) internal pure returns (int256) { - // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive - require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); - return int256(value); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/ZeppelinContract.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -import "./@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; -import "./@openzeppelin/contracts/security/Pausable.sol"; -import "./@openzeppelin/contracts/access/Ownable.sol"; -import "./@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; -import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; -import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol"; - -contract ZeppelinContract is ERC20, ERC20Burnable, Pausable, Ownable, ERC20Permit, ERC20Votes, ERC20FlashMint { - constructor() - ERC20("ZeppelinContract", "UNQ") - ERC20Permit("ZeppelinContract") - {} - - function pause() public onlyOwner { - _pause(); - } - - function unpause() public onlyOwner { - _unpause(); - } - - function mint(address to, uint256 amount) public onlyOwner { - _mint(to, amount); - } - - function _beforeTokenTransfer(address from, address to, uint256 amount) - internal - whenNotPaused - override - { - super._beforeTokenTransfer(from, to, amount); - } - - // The following functions are overrides required by Solidity. - - function _afterTokenTransfer(address from, address to, uint256 amount) - internal - override(ERC20, ERC20Votes) - { - super._afterTokenTransfer(from, to, amount); - } - - function _mint(address to, uint256 amount) - internal - override(ERC20, ERC20Votes) - { - super._mint(to, amount); - } - - function _burn(address account, uint256 amount) - internal - override(ERC20, ERC20Votes) - { - super._burn(account, amount); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.abi +++ /dev/null @@ -1 +0,0 @@ -[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint32","name":"pos","type":"uint32"}],"name":"checkpoints","outputs":[{"components":[{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"uint224","name":"votes","type":"uint224"}],"internalType":"struct ERC20Votes.Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"flashFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC3156FlashBorrower","name":"receiver","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"maxFlashLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.bin +++ /dev/null @@ -1 +0,0 @@ -6101406040523480156200001257600080fd5b506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525080604051806040016040528060018152602001603160f81b8152506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525060405180604001604052806003815260200162554e5160e81b8152508160039081620000ad919062000269565b506004620000bc828262000269565b50506005805460ff1916905550620000d4336200016a565b815160209283012081519183019190912060e08290526101008190524660a0818152604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818801819052818301969096526060810194909452608080850193909352308483018190528151808603909301835260c094850190915281519190950120905291909152610120525062000335565b600580546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620001ef57607f821691505b6020821081036200021057634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200026457600081815260208120601f850160051c810160208610156200023f5750805b601f850160051c820191505b8181101562000260578281556001016200024b565b5050505b505050565b81516001600160401b03811115620002855762000285620001c4565b6200029d81620002968454620001da565b8462000216565b602080601f831160018114620002d55760008415620002bc5750858301515b600019600386901b1c1916600185901b17855562000260565b600085815260208120601f198616915b828110156200030657888601518255948401946001909101908401620002e5565b5085821015620003255787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a05160c05160e05161010051610120516125f06200038560003960006112ad015260006112fc015260006112d7015260006112300152600061125a0152600061128401526125f06000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c806370a082311161011a5780639ab24eb0116100ad578063d505accf1161007c578063d505accf1461046a578063d9d98ce41461047d578063dd62ed3e14610490578063f1127ed8146104a3578063f2fde38b146104e057600080fd5b80639ab24eb01461041e578063a457c2d714610431578063a9059cbb14610444578063c3cda5201461045757600080fd5b80638456cb59116100e95780638456cb59146103e55780638da5cb5b146103ed5780638e539e8c1461040357806395d89b411461041657600080fd5b806370a082311461038e578063715018a6146103b757806379cc6790146103bf5780637ecebe00146103d257600080fd5b80633f4ba83a1161019d5780635c19a95c1161016c5780635c19a95c146103225780635c975abb146103355780635cffe9de14610340578063613255ab146103535780636fcfff451461036657600080fd5b80633f4ba83a146102ae57806340c10f19146102b857806342966c68146102cb578063587cde1e146102de57600080fd5b8063313ce567116101d9578063313ce567146102715780633644e5151461028057806339509351146102885780633a46b1a81461029b57600080fd5b806306fdde031461020b578063095ea7b31461022957806318160ddd1461024c57806323b872dd1461025e575b600080fd5b6102136104f3565b604051610220919061217e565b60405180910390f35b61023c6102373660046121e1565b610585565b6040519015158152602001610220565b6002545b604051908152602001610220565b61023c61026c36600461220d565b61059f565b60405160128152602001610220565b6102506105c3565b61023c6102963660046121e1565b6105d2565b6102506102a93660046121e1565b6105f4565b6102b6610673565b005b6102b66102c63660046121e1565b610685565b6102b66102d936600461224e565b61069b565b61030a6102ec366004612267565b6001600160a01b039081166000908152600860205260409020541690565b6040516001600160a01b039091168152602001610220565b6102b6610330366004612267565b6106a8565b60055460ff1661023c565b61023c61034e366004612284565b6106b2565b610250610361366004612267565b610896565b610379610374366004612267565b6108be565b60405163ffffffff9091168152602001610220565b61025061039c366004612267565b6001600160a01b031660009081526020819052604090205490565b6102b66108e0565b6102b66103cd3660046121e1565b6108f2565b6102506103e0366004612267565b610907565b6102b6610925565b60055461010090046001600160a01b031661030a565b61025061041136600461224e565b610935565b610213610991565b61025061042c366004612267565b6109a0565b61023c61043f3660046121e1565b610a27565b61023c6104523660046121e1565b610aa2565b6102b6610465366004612339565b610ab0565b6102b6610478366004612393565b610be6565b61025061048b3660046121e1565b610d4a565b61025061049e366004612401565b610dab565b6104b66104b136600461243a565b610dd6565b60408051825163ffffffff1681526020928301516001600160e01b03169281019290925201610220565b6102b66104ee366004612267565b610e5a565b60606003805461050290612471565b80601f016020809104026020016040519081016040528092919081815260200182805461052e90612471565b801561057b5780601f106105505761010080835404028352916020019161057b565b820191906000526020600020905b81548152906001019060200180831161055e57829003601f168201915b5050505050905090565b600033610593818585610ed0565b60019150505b92915050565b6000336105ad858285610ff4565b6105b885858561106e565b506001949350505050565b60006105cd611223565b905090565b6000336105938185856105e58383610dab565b6105ef91906124bb565b610ed0565b600043821061064a5760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e65640060448201526064015b60405180910390fd5b6001600160a01b038316600090815260096020526040902061066c908361134a565b9392505050565b61067b611441565b6106836114a1565b565b61068d611441565b61069782826114f3565b5050565b6106a533826114fd565b50565b6106a53382611507565b60006106bd85610896565b8411156107205760405162461bcd60e51b815260206004820152602b60248201527f4552433230466c6173684d696e743a20616d6f756e742065786365656473206d60448201526a30bc233630b9b42637b0b760a91b6064820152608401610641565b600061072c8686610d4a565b905061073887866114f3565b6040516323e30c8b60e01b81527f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9906001600160a01b038916906323e30c8b906107909033908b908b9088908c908c906004016124ce565b6020604051808303816000875af11580156107af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d3919061252a565b1461082c5760405162461bcd60e51b8152602060048201526024808201527f4552433230466c6173684d696e743a20696e76616c69642072657475726e2076604482015263616c756560e01b6064820152608401610641565b6000610842883061083d858a6124bb565b610ff4565b81158061085657506001600160a01b038116155b156108735761086e8861086984896124bb565b6114fd565b610888565b61087d88876114fd565b61088888828461106e565b506001979650505050505050565b60006001600160a01b03821630146108af576000610599565b60025461059990600019612543565b6001600160a01b03811660009081526009602052604081205461059990611580565b6108e8611441565b61068360006115e9565b6108fd823383610ff4565b61069782826114fd565b6001600160a01b038116600090815260066020526040812054610599565b61092d611441565b610683611643565b60004382106109865760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e6564006044820152606401610641565b610599600a8361134a565b60606004805461050290612471565b6001600160a01b0381166000908152600960205260408120548015610a14576001600160a01b03831660009081526009602052604090206109e2600183612543565b815481106109f2576109f2612556565b60009182526020909120015464010000000090046001600160e01b0316610a17565b60005b6001600160e01b03169392505050565b60003381610a358286610dab565b905083811015610a955760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610641565b6105b88286868403610ed0565b60003361059381858561106e565b83421115610b005760405162461bcd60e51b815260206004820152601d60248201527f4552433230566f7465733a207369676e617475726520657870697265640000006044820152606401610641565b604080517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60208201526001600160a01b038816918101919091526060810186905260808101859052600090610b7a90610b729060a00160405160208183030381529060405280519060200120611680565b8585856116ce565b9050610b85816116f6565b8614610bd35760405162461bcd60e51b815260206004820152601960248201527f4552433230566f7465733a20696e76616c6964206e6f6e6365000000000000006044820152606401610641565b610bdd8188611507565b50505050505050565b83421115610c365760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610641565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9888888610c658c6116f6565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506000610cc082611680565b90506000610cd0828787876116ce565b9050896001600160a01b0316816001600160a01b031614610d335760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610641565b610d3e8a8a8a610ed0565b50505050505050505050565b60006001600160a01b0383163014610da45760405162461bcd60e51b815260206004820152601b60248201527f4552433230466c6173684d696e743a2077726f6e6720746f6b656e00000000006044820152606401610641565b600061066c565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60408051808201909152600080825260208201526001600160a01b0383166000908152600960205260409020805463ffffffff8416908110610e1a57610e1a612556565b60009182526020918290206040805180820190915291015463ffffffff8116825264010000000090046001600160e01b0316918101919091529392505050565b610e62611441565b6001600160a01b038116610ec75760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610641565b6106a5816115e9565b6001600160a01b038316610f325760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610641565b6001600160a01b038216610f935760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610641565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60006110008484610dab565b90506000198114611068578181101561105b5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610641565b6110688484848403610ed0565b50505050565b6001600160a01b0383166110d25760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610641565b6001600160a01b0382166111345760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610641565b61113f83838361171e565b6001600160a01b038316600090815260208190526040902054818110156111b75760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610641565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a361106884848461172b565b6000306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614801561127c57507f000000000000000000000000000000000000000000000000000000000000000046145b156112a657507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b8154600090818160058111156113a457600061136584611736565b61136f9085612543565b600088815260209020909150869082015463ffffffff161115611394578091506113a2565b61139f8160016124bb565b92505b505b808210156113f15760006113b8838361181e565b600088815260209020909150869082015463ffffffff1611156113dd578091506113eb565b6113e88160016124bb565b92505b506113a4565b801561142b5761141486611406600184612543565b600091825260209091200190565b5464010000000090046001600160e01b031661142e565b60005b6001600160e01b03169695505050505050565b6005546001600160a01b036101009091041633146106835760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610641565b6114a9611839565b6005805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6106978282611882565b610697828261190c565b6001600160a01b038281166000818152600860208181526040808420805485845282862054949093528787166001600160a01b03198416811790915590519190951694919391928592917f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4611068828483611924565b600063ffffffff8211156115e55760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610641565b5090565b600580546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b61164b611a61565b6005805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586114d63390565b600061059961168d611223565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b60008060006116df87878787611aa7565b915091506116ec81611b6b565b5095945050505050565b6001600160a01b03811660009081526006602052604090208054600181018255905b50919050565b611726611a61565b505050565b611726838383611cb5565b60008160000361174857506000919050565b6000600161175584611ce7565b901c6001901b9050600181848161176e5761176e61256c565b048201901c905060018184816117865761178661256c565b048201901c9050600181848161179e5761179e61256c565b048201901c905060018184816117b6576117b661256c565b048201901c905060018184816117ce576117ce61256c565b048201901c905060018184816117e6576117e661256c565b048201901c905060018184816117fe576117fe61256c565b048201901c905061066c818285816118185761181861256c565b04611d7b565b600061182d6002848418612582565b61066c908484166124bb565b60055460ff166106835760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610641565b61188c8282611d91565b6002546001600160e01b0310156118fe5760405162461bcd60e51b815260206004820152603060248201527f4552433230566f7465733a20746f74616c20737570706c79207269736b73206f60448201526f766572666c6f77696e6720766f74657360801b6064820152608401610641565b611068600a611e6483611e70565b6119168282611fc4565b611068600a61210983611e70565b816001600160a01b0316836001600160a01b0316141580156119465750600081115b15611726576001600160a01b038316156119d4576001600160a01b038316600090815260096020526040812081906119819061210985611e70565b91509150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516119c9929190918252602082015260400190565b60405180910390a250505b6001600160a01b03821615611726576001600160a01b03821660009081526009602052604081208190611a0a90611e6485611e70565b91509150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611a52929190918252602082015260400190565b60405180910390a25050505050565b60055460ff16156106835760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610641565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611ade5750600090506003611b62565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611b32573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611b5b57600060019250925050611b62565b9150600090505b94509492505050565b6000816004811115611b7f57611b7f6125a4565b03611b875750565b6001816004811115611b9b57611b9b6125a4565b03611be85760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610641565b6002816004811115611bfc57611bfc6125a4565b03611c495760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610641565b6003816004811115611c5d57611c5d6125a4565b036106a55760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610641565b6001600160a01b0383811660009081526008602052604080822054858416835291205461172692918216911683611924565b600080608083901c15611cfc57608092831c92015b604083901c15611d0e57604092831c92015b602083901c15611d2057602092831c92015b601083901c15611d3257601092831c92015b600883901c15611d4457600892831c92015b600483901c15611d5657600492831c92015b600283901c15611d6857600292831c92015b600183901c156105995760010192915050565b6000818310611d8a578161066c565b5090919050565b6001600160a01b038216611de75760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610641565b611df36000838361171e565b8060026000828254611e0591906124bb565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36106976000838361172b565b600061066c82846124bb565b82546000908190818115611ebd57611e8d87611406600185612543565b60408051808201909152905463ffffffff8116825264010000000090046001600160e01b03166020820152611ed2565b60408051808201909152600080825260208201525b905080602001516001600160e01b03169350611ef284868863ffffffff16565b9250600082118015611f0a5750805163ffffffff1643145b15611f4f57611f1883612115565b611f2788611406600186612543565b80546001600160e01b03929092166401000000000263ffffffff909216919091179055611fba565b866040518060400160405280611f6443611580565b63ffffffff168152602001611f7886612115565b6001600160e01b0390811690915282546001810184556000938452602093849020835194909301519091166401000000000263ffffffff909316929092179101555b5050935093915050565b6001600160a01b0382166120245760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610641565b6120308260008361171e565b6001600160a01b038216600090815260208190526040902054818110156120a45760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610641565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36117268360008461172b565b600061066c8284612543565b60006001600160e01b038211156115e55760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20326044820152663234206269747360c81b6064820152608401610641565b600060208083528351808285015260005b818110156121ab5785810183015185820160400152820161218f565b506000604082860101526040601f19601f8301168501019250505092915050565b6001600160a01b03811681146106a557600080fd5b600080604083850312156121f457600080fd5b82356121ff816121cc565b946020939093013593505050565b60008060006060848603121561222257600080fd5b833561222d816121cc565b9250602084013561223d816121cc565b929592945050506040919091013590565b60006020828403121561226057600080fd5b5035919050565b60006020828403121561227957600080fd5b813561066c816121cc565b60008060008060006080868803121561229c57600080fd5b85356122a7816121cc565b945060208601356122b7816121cc565b935060408601359250606086013567ffffffffffffffff808211156122db57600080fd5b818801915088601f8301126122ef57600080fd5b8135818111156122fe57600080fd5b89602082850101111561231057600080fd5b9699959850939650602001949392505050565b803560ff8116811461233457600080fd5b919050565b60008060008060008060c0878903121561235257600080fd5b863561235d816121cc565b9550602087013594506040870135935061237960608801612323565b92506080870135915060a087013590509295509295509295565b600080600080600080600060e0888a0312156123ae57600080fd5b87356123b9816121cc565b965060208801356123c9816121cc565b955060408801359450606088013593506123e560808901612323565b925060a0880135915060c0880135905092959891949750929550565b6000806040838503121561241457600080fd5b823561241f816121cc565b9150602083013561242f816121cc565b809150509250929050565b6000806040838503121561244d57600080fd5b8235612458816121cc565b9150602083013563ffffffff8116811461242f57600080fd5b600181811c9082168061248557607f821691505b60208210810361171857634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610599576105996124a5565b6001600160a01b03878116825286166020820152604081018590526060810184905260a06080820181905281018290526000828460c0840137600060c0848401015260c0601f19601f8501168301019050979650505050505050565b60006020828403121561253c57600080fd5b5051919050565b81810381811115610599576105996124a5565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b60008261259f57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052602160045260246000fdfea264697066735822122057e08ed799a9773c2de4d8c40122801fc9994042a2e3e3ac01e5aa1c384b1c4b64736f6c63430008110033 \ No newline at end of file --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# OpenZeppelin Contracts - -The files in this directory were sourced unmodified from OpenZeppelin Contracts v4.8.1. - -They are not meant to be edited. - -The originals can be found on [GitHub] and [npm]. - -[GitHub]: https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.8.1 -[npm]: https://www.npmjs.com/package/@openzeppelin/contracts/v/4.8.1 - -Generated with OpenZeppelin Contracts Wizard (https://zpl.in/wizard). --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/access/Ownable.sol +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) - -pragma solidity ^0.8.0; - -import "../utils/Context.sol"; - -/** - * @dev Contract module which provides a basic access control mechanism, where - * there is an account (an owner) that can be granted exclusive access to - * specific functions. - * - * By default, the owner account will be the one that deploys the contract. This - * can later be changed with {transferOwnership}. - * - * This module is used through inheritance. It will make available the modifier - * `onlyOwner`, which can be applied to your functions to restrict their use to - * the owner. - */ -abstract contract Ownable is Context { - address private _owner; - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev Initializes the contract setting the deployer as the initial owner. - */ - constructor() { - _transferOwnership(_msgSender()); - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - _checkOwner(); - _; - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view virtual returns (address) { - return _owner; - } - - /** - * @dev Throws if the sender is not the owner. - */ - function _checkOwner() internal view virtual { - require(owner() == _msgSender(), "Ownable: caller is not the owner"); - } - - /** - * @dev Leaves the contract without owner. It will not be possible to call - * `onlyOwner` functions anymore. Can only be called by the current owner. - * - * NOTE: Renouncing ownership will leave the contract without an owner, - * thereby removing any functionality that is only available to the owner. - */ - function renounceOwnership() public virtual onlyOwner { - _transferOwnership(address(0)); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function transferOwnership(address newOwner) public virtual onlyOwner { - require(newOwner != address(0), "Ownable: new owner is the zero address"); - _transferOwnership(newOwner); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Internal function without access restriction. - */ - function _transferOwnership(address newOwner) internal virtual { - address oldOwner = _owner; - _owner = newOwner; - emit OwnershipTransferred(oldOwner, newOwner); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/ERC721.sol +++ /dev/null @@ -1,503 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/ERC721.sol) - -pragma solidity ^0.8.0; - -import "./IERC721.sol"; -import "./IERC721Receiver.sol"; -import "./extensions/IERC721Metadata.sol"; -import "../../utils/Address.sol"; -import "../../utils/Context.sol"; -import "../../utils/Strings.sol"; -import "../../utils/introspection/ERC165.sol"; - -/** - * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including - * the Metadata extension, but not including the Enumerable extension, which is available separately as - * {ERC721Enumerable}. - */ -contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { - using Address for address; - using Strings for uint256; - - // Token name - string private _name; - - // Token symbol - string private _symbol; - - // Mapping from token ID to owner address - mapping(uint256 => address) private _owners; - - // Mapping owner address to token count - mapping(address => uint256) private _balances; - - // Mapping from token ID to approved address - mapping(uint256 => address) private _tokenApprovals; - - // Mapping from owner to operator approvals - mapping(address => mapping(address => bool)) private _operatorApprovals; - - /** - * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. - */ - constructor(string memory name_, string memory symbol_) { - _name = name_; - _symbol = symbol_; - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { - return - interfaceId == type(IERC721).interfaceId || - interfaceId == type(IERC721Metadata).interfaceId || - super.supportsInterface(interfaceId); - } - - /** - * @dev See {IERC721-balanceOf}. - */ - function balanceOf(address owner) public view virtual override returns (uint256) { - require(owner != address(0), "ERC721: address zero is not a valid owner"); - return _balances[owner]; - } - - /** - * @dev See {IERC721-ownerOf}. - */ - function ownerOf(uint256 tokenId) public view virtual override returns (address) { - address owner = _ownerOf(tokenId); - require(owner != address(0), "ERC721: invalid token ID"); - return owner; - } - - /** - * @dev See {IERC721Metadata-name}. - */ - function name() public view virtual override returns (string memory) { - return _name; - } - - /** - * @dev See {IERC721Metadata-symbol}. - */ - function symbol() public view virtual override returns (string memory) { - return _symbol; - } - - /** - * @dev See {IERC721Metadata-tokenURI}. - */ - function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { - _requireMinted(tokenId); - - string memory baseURI = _baseURI(); - return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; - } - - /** - * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each - * token will be the concatenation of the `baseURI` and the `tokenId`. Empty - * by default, can be overridden in child contracts. - */ - function _baseURI() internal view virtual returns (string memory) { - return ""; - } - - /** - * @dev See {IERC721-approve}. - */ - function approve(address to, uint256 tokenId) public virtual override { - address owner = ERC721.ownerOf(tokenId); - require(to != owner, "ERC721: approval to current owner"); - - require( - _msgSender() == owner || isApprovedForAll(owner, _msgSender()), - "ERC721: approve caller is not token owner or approved for all" - ); - - _approve(to, tokenId); - } - - /** - * @dev See {IERC721-getApproved}. - */ - function getApproved(uint256 tokenId) public view virtual override returns (address) { - _requireMinted(tokenId); - - return _tokenApprovals[tokenId]; - } - - /** - * @dev See {IERC721-setApprovalForAll}. - */ - function setApprovalForAll(address operator, bool approved) public virtual override { - _setApprovalForAll(_msgSender(), operator, approved); - } - - /** - * @dev See {IERC721-isApprovedForAll}. - */ - function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { - return _operatorApprovals[owner][operator]; - } - - /** - * @dev See {IERC721-transferFrom}. - */ - function transferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { - //solhint-disable-next-line max-line-length - require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); - - _transfer(from, to, tokenId); - } - - /** - * @dev See {IERC721-safeTransferFrom}. - */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { - safeTransferFrom(from, to, tokenId, ""); - } - - /** - * @dev See {IERC721-safeTransferFrom}. - */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory data - ) public virtual override { - require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); - _safeTransfer(from, to, tokenId, data); - } - - /** - * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients - * are aware of the ERC721 protocol to prevent tokens from being forever locked. - * - * `data` is additional data, it has no specified format and it is sent in call to `to`. - * - * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. - * implement alternative mechanisms to perform token transfer, such as signature-based. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `tokenId` token must exist and be owned by `from`. - * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - * - * Emits a {Transfer} event. - */ - function _safeTransfer( - address from, - address to, - uint256 tokenId, - bytes memory data - ) internal virtual { - _transfer(from, to, tokenId); - require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer"); - } - - /** - * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist - */ - function _ownerOf(uint256 tokenId) internal view virtual returns (address) { - return _owners[tokenId]; - } - - /** - * @dev Returns whether `tokenId` exists. - * - * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. - * - * Tokens start existing when they are minted (`_mint`), - * and stop existing when they are burned (`_burn`). - */ - function _exists(uint256 tokenId) internal view virtual returns (bool) { - return _ownerOf(tokenId) != address(0); - } - - /** - * @dev Returns whether `spender` is allowed to manage `tokenId`. - * - * Requirements: - * - * - `tokenId` must exist. - */ - function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { - address owner = ERC721.ownerOf(tokenId); - return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender); - } - - /** - * @dev Safely mints `tokenId` and transfers it to `to`. - * - * Requirements: - * - * - `tokenId` must not exist. - * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - * - * Emits a {Transfer} event. - */ - function _safeMint(address to, uint256 tokenId) internal virtual { - _safeMint(to, tokenId, ""); - } - - /** - * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is - * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. - */ - function _safeMint( - address to, - uint256 tokenId, - bytes memory data - ) internal virtual { - _mint(to, tokenId); - require( - _checkOnERC721Received(address(0), to, tokenId, data), - "ERC721: transfer to non ERC721Receiver implementer" - ); - } - - /** - * @dev Mints `tokenId` and transfers it to `to`. - * - * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible - * - * Requirements: - * - * - `tokenId` must not exist. - * - `to` cannot be the zero address. - * - * Emits a {Transfer} event. - */ - function _mint(address to, uint256 tokenId) internal virtual { - require(to != address(0), "ERC721: mint to the zero address"); - require(!_exists(tokenId), "ERC721: token already minted"); - - _beforeTokenTransfer(address(0), to, tokenId, 1); - - // Check that tokenId was not minted by `_beforeTokenTransfer` hook - require(!_exists(tokenId), "ERC721: token already minted"); - - unchecked { - // Will not overflow unless all 2**256 token ids are minted to the same owner. - // Given that tokens are minted one by one, it is impossible in practice that - // this ever happens. Might change if we allow batch minting. - // The ERC fails to describe this case. - _balances[to] += 1; - } - - _owners[tokenId] = to; - - emit Transfer(address(0), to, tokenId); - - _afterTokenTransfer(address(0), to, tokenId, 1); - } - - /** - * @dev Destroys `tokenId`. - * The approval is cleared when the token is burned. - * This is an internal function that does not check if the sender is authorized to operate on the token. - * - * Requirements: - * - * - `tokenId` must exist. - * - * Emits a {Transfer} event. - */ - function _burn(uint256 tokenId) internal virtual { - address owner = ERC721.ownerOf(tokenId); - - _beforeTokenTransfer(owner, address(0), tokenId, 1); - - // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook - owner = ERC721.ownerOf(tokenId); - - // Clear approvals - delete _tokenApprovals[tokenId]; - - unchecked { - // Cannot overflow, as that would require more tokens to be burned/transferred - // out than the owner initially received through minting and transferring in. - _balances[owner] -= 1; - } - delete _owners[tokenId]; - - emit Transfer(owner, address(0), tokenId); - - _afterTokenTransfer(owner, address(0), tokenId, 1); - } - - /** - * @dev Transfers `tokenId` from `from` to `to`. - * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - `tokenId` token must be owned by `from`. - * - * Emits a {Transfer} event. - */ - function _transfer( - address from, - address to, - uint256 tokenId - ) internal virtual { - require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); - require(to != address(0), "ERC721: transfer to the zero address"); - - _beforeTokenTransfer(from, to, tokenId, 1); - - // Check that tokenId was not transferred by `_beforeTokenTransfer` hook - require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); - - // Clear approvals from the previous owner - delete _tokenApprovals[tokenId]; - - unchecked { - // `_balances[from]` cannot overflow for the same reason as described in `_burn`: - // `from`'s balance is the number of token held, which is at least one before the current - // transfer. - // `_balances[to]` could overflow in the conditions described in `_mint`. That would require - // all 2**256 token ids to be minted, which in practice is impossible. - _balances[from] -= 1; - _balances[to] += 1; - } - _owners[tokenId] = to; - - emit Transfer(from, to, tokenId); - - _afterTokenTransfer(from, to, tokenId, 1); - } - - /** - * @dev Approve `to` to operate on `tokenId` - * - * Emits an {Approval} event. - */ - function _approve(address to, uint256 tokenId) internal virtual { - _tokenApprovals[tokenId] = to; - emit Approval(ERC721.ownerOf(tokenId), to, tokenId); - } - - /** - * @dev Approve `operator` to operate on all of `owner` tokens - * - * Emits an {ApprovalForAll} event. - */ - function _setApprovalForAll( - address owner, - address operator, - bool approved - ) internal virtual { - require(owner != operator, "ERC721: approve to caller"); - _operatorApprovals[owner][operator] = approved; - emit ApprovalForAll(owner, operator, approved); - } - - /** - * @dev Reverts if the `tokenId` has not been minted yet. - */ - function _requireMinted(uint256 tokenId) internal view virtual { - require(_exists(tokenId), "ERC721: invalid token ID"); - } - - /** - * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. - * The call is not executed if the target address is not a contract. - * - * @param from address representing the previous owner of the given token ID - * @param to target address that will receive the tokens - * @param tokenId uint256 ID of the token to be transferred - * @param data bytes optional data to send along with the call - * @return bool whether the call correctly returned the expected magic value - */ - function _checkOnERC721Received( - address from, - address to, - uint256 tokenId, - bytes memory data - ) private returns (bool) { - if (to.isContract()) { - try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) { - return retval == IERC721Receiver.onERC721Received.selector; - } catch (bytes memory reason) { - if (reason.length == 0) { - revert("ERC721: transfer to non ERC721Receiver implementer"); - } else { - /// @solidity memory-safe-assembly - assembly { - revert(add(32, reason), mload(reason)) - } - } - } - } else { - return true; - } - } - - /** - * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is - * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1. - * - * Calling conditions: - * - * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`. - * - When `from` is zero, the tokens will be minted for `to`. - * - When `to` is zero, ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. - * - `batchSize` is non-zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer( - address from, - address to, - uint256, /* firstTokenId */ - uint256 batchSize - ) internal virtual { - if (batchSize > 1) { - if (from != address(0)) { - _balances[from] -= batchSize; - } - if (to != address(0)) { - _balances[to] += batchSize; - } - } - } - - /** - * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is - * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1. - * - * Calling conditions: - * - * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`. - * - When `from` is zero, the tokens were minted for `to`. - * - When `to` is zero, ``from``'s tokens were burned. - * - `from` and `to` are never both zero. - * - `batchSize` is non-zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _afterTokenTransfer( - address from, - address to, - uint256 firstTokenId, - uint256 batchSize - ) internal virtual {} -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721.sol +++ /dev/null @@ -1,145 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol) - -pragma solidity ^0.8.0; - -import "../../utils/introspection/IERC165.sol"; - -/** - * @dev Required interface of an ERC721 compliant contract. - */ -interface IERC721 is IERC165 { - /** - * @dev Emitted when `tokenId` token is transferred from `from` to `to`. - */ - event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); - - /** - * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. - */ - event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); - - /** - * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. - */ - event ApprovalForAll(address indexed owner, address indexed operator, bool approved); - - /** - * @dev Returns the number of tokens in ``owner``'s account. - */ - function balanceOf(address owner) external view returns (uint256 balance); - - /** - * @dev Returns the owner of the `tokenId` token. - * - * Requirements: - * - * - `tokenId` must exist. - */ - function ownerOf(uint256 tokenId) external view returns (address owner); - - /** - * @dev Safely transfers `tokenId` token from `from` to `to`. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `tokenId` token must exist and be owned by `from`. - * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. - * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - * - * Emits a {Transfer} event. - */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes calldata data - ) external; - - /** - * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients - * are aware of the ERC721 protocol to prevent tokens from being forever locked. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `tokenId` token must exist and be owned by `from`. - * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. - * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - * - * Emits a {Transfer} event. - */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) external; - - /** - * @dev Transfers `tokenId` token from `from` to `to`. - * - * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 - * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must - * understand this adds an external call which potentially creates a reentrancy vulnerability. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `tokenId` token must be owned by `from`. - * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 tokenId - ) external; - - /** - * @dev Gives permission to `to` to transfer `tokenId` token to another account. - * The approval is cleared when the token is transferred. - * - * Only a single account can be approved at a time, so approving the zero address clears previous approvals. - * - * Requirements: - * - * - The caller must own the token or be an approved operator. - * - `tokenId` must exist. - * - * Emits an {Approval} event. - */ - function approve(address to, uint256 tokenId) external; - - /** - * @dev Approve or remove `operator` as an operator for the caller. - * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. - * - * Requirements: - * - * - The `operator` cannot be the caller. - * - * Emits an {ApprovalForAll} event. - */ - function setApprovalForAll(address operator, bool _approved) external; - - /** - * @dev Returns the account approved for `tokenId` token. - * - * Requirements: - * - * - `tokenId` must exist. - */ - function getApproved(uint256 tokenId) external view returns (address operator); - - /** - * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. - * - * See {setApprovalForAll} - */ - function isApprovedForAll(address owner, address operator) external view returns (bool); -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol) - -pragma solidity ^0.8.0; - -/** - * @title ERC721 token receiver interface - * @dev Interface for any contract that wants to support safeTransfers - * from ERC721 asset contracts. - */ -interface IERC721Receiver { - /** - * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} - * by `operator` from `from`, this function is called. - * - * It must return its Solidity selector to confirm the token transfer. - * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. - * - * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. - */ - function onERC721Received( - address operator, - address from, - uint256 tokenId, - bytes calldata data - ) external returns (bytes4); -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Burnable.sol) - -pragma solidity ^0.8.0; - -import "../ERC721.sol"; -import "../../../utils/Context.sol"; - -/** - * @title ERC721 Burnable Token - * @dev ERC721 Token that can be burned (destroyed). - */ -abstract contract ERC721Burnable is Context, ERC721 { - /** - * @dev Burns `tokenId`. See {ERC721-_burn}. - * - * Requirements: - * - * - The caller must own `tokenId` or be an approved operator. - */ - function burn(uint256 tokenId) public virtual { - //solhint-disable-next-line max-line-length - require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); - _burn(tokenId); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Enumerable.sol) - -pragma solidity ^0.8.0; - -import "../ERC721.sol"; -import "./IERC721Enumerable.sol"; - -/** - * @dev This implements an optional extension of {ERC721} defined in the EIP that adds - * enumerability of all the token ids in the contract as well as all token ids owned by each - * account. - */ -abstract contract ERC721Enumerable is ERC721, IERC721Enumerable { - // Mapping from owner to list of owned token IDs - mapping(address => mapping(uint256 => uint256)) private _ownedTokens; - - // Mapping from token ID to index of the owner tokens list - mapping(uint256 => uint256) private _ownedTokensIndex; - - // Array with all token ids, used for enumeration - uint256[] private _allTokens; - - // Mapping from token id to position in the allTokens array - mapping(uint256 => uint256) private _allTokensIndex; - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { - return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. - */ - function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { - require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds"); - return _ownedTokens[owner][index]; - } - - /** - * @dev See {IERC721Enumerable-totalSupply}. - */ - function totalSupply() public view virtual override returns (uint256) { - return _allTokens.length; - } - - /** - * @dev See {IERC721Enumerable-tokenByIndex}. - */ - function tokenByIndex(uint256 index) public view virtual override returns (uint256) { - require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds"); - return _allTokens[index]; - } - - /** - * @dev See {ERC721-_beforeTokenTransfer}. - */ - function _beforeTokenTransfer( - address from, - address to, - uint256 firstTokenId, - uint256 batchSize - ) internal virtual override { - super._beforeTokenTransfer(from, to, firstTokenId, batchSize); - - if (batchSize > 1) { - // Will only trigger during construction. Batch transferring (minting) is not available afterwards. - revert("ERC721Enumerable: consecutive transfers not supported"); - } - - uint256 tokenId = firstTokenId; - - if (from == address(0)) { - _addTokenToAllTokensEnumeration(tokenId); - } else if (from != to) { - _removeTokenFromOwnerEnumeration(from, tokenId); - } - if (to == address(0)) { - _removeTokenFromAllTokensEnumeration(tokenId); - } else if (to != from) { - _addTokenToOwnerEnumeration(to, tokenId); - } - } - - /** - * @dev Private function to add a token to this extension's ownership-tracking data structures. - * @param to address representing the new owner of the given token ID - * @param tokenId uint256 ID of the token to be added to the tokens list of the given address - */ - function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { - uint256 length = ERC721.balanceOf(to); - _ownedTokens[to][length] = tokenId; - _ownedTokensIndex[tokenId] = length; - } - - /** - * @dev Private function to add a token to this extension's token tracking data structures. - * @param tokenId uint256 ID of the token to be added to the tokens list - */ - function _addTokenToAllTokensEnumeration(uint256 tokenId) private { - _allTokensIndex[tokenId] = _allTokens.length; - _allTokens.push(tokenId); - } - - /** - * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that - * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for - * gas optimizations e.g. when performing a transfer operation (avoiding double writes). - * This has O(1) time complexity, but alters the order of the _ownedTokens array. - * @param from address representing the previous owner of the given token ID - * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address - */ - function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { - // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and - // then delete the last slot (swap and pop). - - uint256 lastTokenIndex = ERC721.balanceOf(from) - 1; - uint256 tokenIndex = _ownedTokensIndex[tokenId]; - - // When the token to delete is the last token, the swap operation is unnecessary - if (tokenIndex != lastTokenIndex) { - uint256 lastTokenId = _ownedTokens[from][lastTokenIndex]; - - _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token - _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index - } - - // This also deletes the contents at the last position of the array - delete _ownedTokensIndex[tokenId]; - delete _ownedTokens[from][lastTokenIndex]; - } - - /** - * @dev Private function to remove a token from this extension's token tracking data structures. - * This has O(1) time complexity, but alters the order of the _allTokens array. - * @param tokenId uint256 ID of the token to be removed from the tokens list - */ - function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { - // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and - // then delete the last slot (swap and pop). - - uint256 lastTokenIndex = _allTokens.length - 1; - uint256 tokenIndex = _allTokensIndex[tokenId]; - - // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so - // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding - // an 'if' statement (like in _removeTokenFromOwnerEnumeration) - uint256 lastTokenId = _allTokens[lastTokenIndex]; - - _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token - _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index - - // This also deletes the contents at the last position of the array - delete _allTokensIndex[tokenId]; - _allTokens.pop(); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721URIStorage.sol) - -pragma solidity ^0.8.0; - -import "../ERC721.sol"; - -/** - * @dev ERC721 token with storage based token URI management. - */ -abstract contract ERC721URIStorage is ERC721 { - using Strings for uint256; - - // Optional mapping for token URIs - mapping(uint256 => string) private _tokenURIs; - - /** - * @dev See {IERC721Metadata-tokenURI}. - */ - function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { - _requireMinted(tokenId); - - string memory _tokenURI = _tokenURIs[tokenId]; - string memory base = _baseURI(); - - // If there is no base URI, return the token URI. - if (bytes(base).length == 0) { - return _tokenURI; - } - // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). - if (bytes(_tokenURI).length > 0) { - return string(abi.encodePacked(base, _tokenURI)); - } - - return super.tokenURI(tokenId); - } - - /** - * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. - * - * Requirements: - * - * - `tokenId` must exist. - */ - function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { - require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token"); - _tokenURIs[tokenId] = _tokenURI; - } - - /** - * @dev See {ERC721-_burn}. This override additionally checks to see if a - * token-specific URI was set for the token, and if so, it deletes the token URI from - * the storage mapping. - */ - function _burn(uint256 tokenId) internal virtual override { - super._burn(tokenId); - - if (bytes(_tokenURIs[tokenId]).length != 0) { - delete _tokenURIs[tokenId]; - } - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol) - -pragma solidity ^0.8.0; - -import "../IERC721.sol"; - -/** - * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension - * @dev See https://eips.ethereum.org/EIPS/eip-721 - */ -interface IERC721Enumerable is IERC721 { - /** - * @dev Returns the total amount of tokens stored by the contract. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns a token ID owned by `owner` at a given `index` of its token list. - * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. - */ - function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); - - /** - * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. - * Use along with {totalSupply} to enumerate all tokens. - */ - function tokenByIndex(uint256 index) external view returns (uint256); -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) - -pragma solidity ^0.8.0; - -import "../IERC721.sol"; - -/** - * @title ERC-721 Non-Fungible Token Standard, optional metadata extension - * @dev See https://eips.ethereum.org/EIPS/eip-721 - */ -interface IERC721Metadata is IERC721 { - /** - * @dev Returns the token collection name. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the token collection symbol. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. - */ - function tokenURI(uint256 tokenId) external view returns (string memory); -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Address.sol +++ /dev/null @@ -1,244 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) - -pragma solidity ^0.8.1; - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResultFromTarget(target, success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResultFromTarget(target, success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResultFromTarget(target, success, returndata, errorMessage); - } - - /** - * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling - * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. - * - * _Available since v4.8._ - */ - function verifyCallResultFromTarget( - address target, - bool success, - bytes memory returndata, - string memory errorMessage - ) internal view returns (bytes memory) { - if (success) { - if (returndata.length == 0) { - // only check isContract if the call was successful and the return data is empty - // otherwise we already know that it was a contract - require(isContract(target), "Address: call to non-contract"); - } - return returndata; - } else { - _revert(returndata, errorMessage); - } - } - - /** - * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason or using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - _revert(returndata, errorMessage); - } - } - - function _revert(bytes memory returndata, string memory errorMessage) private pure { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Context.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Counters.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) - -pragma solidity ^0.8.0; - -/** - * @title Counters - * @author Matt Condon (@shrugs) - * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number - * of elements in a mapping, issuing ERC721 ids, or counting request ids. - * - * Include with `using Counters for Counters.Counter;` - */ -library Counters { - struct Counter { - // This variable should never be directly accessed by users of the library: interactions must be restricted to - // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add - // this feature: see https://github.com/ethereum/solidity/issues/4637 - uint256 _value; // default: 0 - } - - function current(Counter storage counter) internal view returns (uint256) { - return counter._value; - } - - function increment(Counter storage counter) internal { - unchecked { - counter._value += 1; - } - } - - function decrement(Counter storage counter) internal { - uint256 value = counter._value; - require(value > 0, "Counter: decrement overflow"); - unchecked { - counter._value = value - 1; - } - } - - function reset(Counter storage counter) internal { - counter._value = 0; - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Strings.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) - -pragma solidity ^0.8.0; - -import "./math/Math.sol"; - -/** - * @dev String operations. - */ -library Strings { - bytes16 private constant _SYMBOLS = "0123456789abcdef"; - uint8 private constant _ADDRESS_LENGTH = 20; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - unchecked { - uint256 length = Math.log10(value) + 1; - string memory buffer = new string(length); - uint256 ptr; - /// @solidity memory-safe-assembly - assembly { - ptr := add(buffer, add(32, length)) - } - while (true) { - ptr--; - /// @solidity memory-safe-assembly - assembly { - mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) - } - value /= 10; - if (value == 0) break; - } - return buffer; - } - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - unchecked { - return toHexString(value, Math.log256(value) + 1); - } - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } - - /** - * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. - */ - function toHexString(address addr) internal pure returns (string memory) { - return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/ERC165.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) - -pragma solidity ^0.8.0; - -import "./IERC165.sol"; - -/** - * @dev Implementation of the {IERC165} interface. - * - * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check - * for the additional interface id that will be supported. For example: - * - * ```solidity - * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); - * } - * ``` - * - * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. - */ -abstract contract ERC165 is IERC165 { - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == type(IERC165).interfaceId; - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/IERC165.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC165 standard, as defined in the - * https://eips.ethereum.org/EIPS/eip-165[EIP]. - * - * Implementers can declare support of contract interfaces, which can then be - * queried by others ({ERC165Checker}). - * - * For an implementation, see {ERC165}. - */ -interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/math/Math.sol +++ /dev/null @@ -1,345 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Standard math utilities missing in the Solidity language. - */ -library Math { - enum Rounding { - Down, // Toward negative infinity - Up, // Toward infinity - Zero // Toward zero - } - - /** - * @dev Returns the largest of two numbers. - */ - function max(uint256 a, uint256 b) internal pure returns (uint256) { - return a > b ? a : b; - } - - /** - * @dev Returns the smallest of two numbers. - */ - function min(uint256 a, uint256 b) internal pure returns (uint256) { - return a < b ? a : b; - } - - /** - * @dev Returns the average of two numbers. The result is rounded towards - * zero. - */ - function average(uint256 a, uint256 b) internal pure returns (uint256) { - // (a + b) / 2 can overflow. - return (a & b) + (a ^ b) / 2; - } - - /** - * @dev Returns the ceiling of the division of two numbers. - * - * This differs from standard division with `/` in that it rounds up instead - * of rounding down. - */ - function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { - // (a + b - 1) / b can overflow on addition, so we distribute. - return a == 0 ? 0 : (a - 1) / b + 1; - } - - /** - * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 - * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) - * with further edits by Uniswap Labs also under MIT license. - */ - function mulDiv( - uint256 x, - uint256 y, - uint256 denominator - ) internal pure returns (uint256 result) { - unchecked { - // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use - // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 - // variables such that product = prod1 * 2^256 + prod0. - uint256 prod0; // Least significant 256 bits of the product - uint256 prod1; // Most significant 256 bits of the product - assembly { - let mm := mulmod(x, y, not(0)) - prod0 := mul(x, y) - prod1 := sub(sub(mm, prod0), lt(mm, prod0)) - } - - // Handle non-overflow cases, 256 by 256 division. - if (prod1 == 0) { - return prod0 / denominator; - } - - // Make sure the result is less than 2^256. Also prevents denominator == 0. - require(denominator > prod1); - - /////////////////////////////////////////////// - // 512 by 256 division. - /////////////////////////////////////////////// - - // Make division exact by subtracting the remainder from [prod1 prod0]. - uint256 remainder; - assembly { - // Compute remainder using mulmod. - remainder := mulmod(x, y, denominator) - - // Subtract 256 bit number from 512 bit number. - prod1 := sub(prod1, gt(remainder, prod0)) - prod0 := sub(prod0, remainder) - } - - // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. - // See https://cs.stackexchange.com/q/138556/92363. - - // Does not overflow because the denominator cannot be zero at this stage in the function. - uint256 twos = denominator & (~denominator + 1); - assembly { - // Divide denominator by twos. - denominator := div(denominator, twos) - - // Divide [prod1 prod0] by twos. - prod0 := div(prod0, twos) - - // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. - twos := add(div(sub(0, twos), twos), 1) - } - - // Shift in bits from prod1 into prod0. - prod0 |= prod1 * twos; - - // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such - // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for - // four bits. That is, denominator * inv = 1 mod 2^4. - uint256 inverse = (3 * denominator) ^ 2; - - // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works - // in modular arithmetic, doubling the correct bits in each step. - inverse *= 2 - denominator * inverse; // inverse mod 2^8 - inverse *= 2 - denominator * inverse; // inverse mod 2^16 - inverse *= 2 - denominator * inverse; // inverse mod 2^32 - inverse *= 2 - denominator * inverse; // inverse mod 2^64 - inverse *= 2 - denominator * inverse; // inverse mod 2^128 - inverse *= 2 - denominator * inverse; // inverse mod 2^256 - - // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. - // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is - // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 - // is no longer required. - result = prod0 * inverse; - return result; - } - } - - /** - * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. - */ - function mulDiv( - uint256 x, - uint256 y, - uint256 denominator, - Rounding rounding - ) internal pure returns (uint256) { - uint256 result = mulDiv(x, y, denominator); - if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { - result += 1; - } - return result; - } - - /** - * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. - * - * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). - */ - function sqrt(uint256 a) internal pure returns (uint256) { - if (a == 0) { - return 0; - } - - // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. - // - // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have - // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. - // - // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` - // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` - // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` - // - // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. - uint256 result = 1 << (log2(a) >> 1); - - // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, - // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at - // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision - // into the expected uint128 result. - unchecked { - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - result = (result + a / result) >> 1; - return min(result, a / result); - } - } - - /** - * @notice Calculates sqrt(a), following the selected rounding direction. - */ - function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { - unchecked { - uint256 result = sqrt(a); - return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); - } - } - - /** - * @dev Return the log in base 2, rounded down, of a positive value. - * Returns 0 if given 0. - */ - function log2(uint256 value) internal pure returns (uint256) { - uint256 result = 0; - unchecked { - if (value >> 128 > 0) { - value >>= 128; - result += 128; - } - if (value >> 64 > 0) { - value >>= 64; - result += 64; - } - if (value >> 32 > 0) { - value >>= 32; - result += 32; - } - if (value >> 16 > 0) { - value >>= 16; - result += 16; - } - if (value >> 8 > 0) { - value >>= 8; - result += 8; - } - if (value >> 4 > 0) { - value >>= 4; - result += 4; - } - if (value >> 2 > 0) { - value >>= 2; - result += 2; - } - if (value >> 1 > 0) { - result += 1; - } - } - return result; - } - - /** - * @dev Return the log in base 2, following the selected rounding direction, of a positive value. - * Returns 0 if given 0. - */ - function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { - unchecked { - uint256 result = log2(value); - return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); - } - } - - /** - * @dev Return the log in base 10, rounded down, of a positive value. - * Returns 0 if given 0. - */ - function log10(uint256 value) internal pure returns (uint256) { - uint256 result = 0; - unchecked { - if (value >= 10**64) { - value /= 10**64; - result += 64; - } - if (value >= 10**32) { - value /= 10**32; - result += 32; - } - if (value >= 10**16) { - value /= 10**16; - result += 16; - } - if (value >= 10**8) { - value /= 10**8; - result += 8; - } - if (value >= 10**4) { - value /= 10**4; - result += 4; - } - if (value >= 10**2) { - value /= 10**2; - result += 2; - } - if (value >= 10**1) { - result += 1; - } - } - return result; - } - - /** - * @dev Return the log in base 10, following the selected rounding direction, of a positive value. - * Returns 0 if given 0. - */ - function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { - unchecked { - uint256 result = log10(value); - return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); - } - } - - /** - * @dev Return the log in base 256, rounded down, of a positive value. - * Returns 0 if given 0. - * - * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. - */ - function log256(uint256 value) internal pure returns (uint256) { - uint256 result = 0; - unchecked { - if (value >> 128 > 0) { - value >>= 128; - result += 16; - } - if (value >> 64 > 0) { - value >>= 64; - result += 8; - } - if (value >> 32 > 0) { - value >>= 32; - result += 4; - } - if (value >> 16 > 0) { - value >>= 16; - result += 2; - } - if (value >> 8 > 0) { - result += 1; - } - } - return result; - } - - /** - * @dev Return the log in base 10, following the selected rounding direction, of a positive value. - * Returns 0 if given 0. - */ - function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { - unchecked { - uint256 result = log256(value); - return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); - } - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/ZeppelinContract.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -import "./@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; -import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; -import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; -import "./@openzeppelin/contracts/access/Ownable.sol"; -import "./@openzeppelin/contracts/utils/Counters.sol"; - -contract ZeppelinContract is ERC721, ERC721Enumerable, ERC721URIStorage, ERC721Burnable, Ownable { - using Counters for Counters.Counter; - - Counters.Counter private _tokenIdCounter; - - constructor() ERC721("ZeppelinContract", "UNQ") {} - - function _baseURI() internal pure override returns (string memory) { - return "test"; - } - - function safeMint(address to, string memory uri) public onlyOwner { - uint256 tokenId = _tokenIdCounter.current(); - _tokenIdCounter.increment(); - _safeMint(to, tokenId); - _setTokenURI(tokenId, uri); - } - - // The following functions are overrides required by Solidity. - - function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) - internal - override(ERC721, ERC721Enumerable) - { - super._beforeTokenTransfer(from, to, tokenId, batchSize); - } - - function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) { - super._burn(tokenId); - } - - function tokenURI(uint256 tokenId) - public - view - override(ERC721, ERC721URIStorage) - returns (string memory) - { - return super.tokenURI(tokenId); - } - - function supportsInterface(bytes4 interfaceId) - public - view - override(ERC721, ERC721Enumerable) - returns (bool) - { - return super.supportsInterface(interfaceId); - } -} --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.abi +++ /dev/null @@ -1 +0,0 @@ -[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"string","name":"uri","type":"string"}],"name":"safeMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file --- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.bin +++ /dev/null @@ -1 +0,0 @@ -60806040523480156200001157600080fd5b506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525060405180604001604052806003815260200162554e5160e81b815250816000908162000068919062000195565b50600162000077828262000195565b505050620000946200008e6200009a60201b60201c565b6200009e565b62000261565b3390565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200011b57607f821691505b6020821081036200013c57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200019057600081815260208120601f850160051c810160208610156200016b5750805b601f850160051c820191505b818110156200018c5782815560010162000177565b5050505b505050565b81516001600160401b03811115620001b157620001b1620000f0565b620001c981620001c2845462000106565b8462000142565b602080601f831160018114620002015760008415620001e85750858301515b600019600386901b1c1916600185901b1785556200018c565b600085815260208120601f198616915b82811015620002325788860151825594840194600190910190840162000211565b5085821015620002515787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b611f0d80620002716000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c80636352211e116100b8578063a22cb4651161007c578063a22cb46514610271578063b88d4fde14610284578063c87b56dd14610297578063d204c45e146102aa578063e985e9c5146102bd578063f2fde38b146102f957600080fd5b80636352211e1461022a57806370a082311461023d578063715018a6146102505780638da5cb5b1461025857806395d89b411461026957600080fd5b806323b872dd116100ff57806323b872dd146101cb5780632f745c59146101de57806342842e0e146101f157806342966c68146102045780634f6ccce71461021757600080fd5b806301ffc9a71461013c57806306fdde0314610164578063081812fc14610179578063095ea7b3146101a457806318160ddd146101b9575b600080fd5b61014f61014a3660046118ab565b61030c565b60405190151581526020015b60405180910390f35b61016c61031d565b60405161015b9190611918565b61018c61018736600461192b565b6103af565b6040516001600160a01b03909116815260200161015b565b6101b76101b2366004611960565b6103d6565b005b6008545b60405190815260200161015b565b6101b76101d936600461198a565b6104f0565b6101bd6101ec366004611960565b610522565b6101b76101ff36600461198a565b6105b8565b6101b761021236600461192b565b6105d3565b6101bd61022536600461192b565b610604565b61018c61023836600461192b565b610697565b6101bd61024b3660046119c6565b6106f7565b6101b761077d565b600b546001600160a01b031661018c565b61016c610791565b6101b761027f3660046119e1565b6107a0565b6101b7610292366004611aa9565b6107af565b61016c6102a536600461192b565b6107e7565b6101b76102b8366004611b25565b6107f2565b61014f6102cb366004611b87565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6101b76103073660046119c6565b610829565b60006103178261089f565b92915050565b60606000805461032c90611bba565b80601f016020809104026020016040519081016040528092919081815260200182805461035890611bba565b80156103a55780601f1061037a576101008083540402835291602001916103a5565b820191906000526020600020905b81548152906001019060200180831161038857829003601f168201915b5050505050905090565b60006103ba826108c4565b506000908152600460205260409020546001600160a01b031690565b60006103e182610697565b9050806001600160a01b0316836001600160a01b0316036104535760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b038216148061046f575061046f81336102cb565b6104e15760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c000000606482015260840161044a565b6104eb8383610923565b505050565b6104fb335b82610991565b6105175760405162461bcd60e51b815260040161044a90611bf4565b6104eb838383610a10565b600061052d836106f7565b821061058f5760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b606482015260840161044a565b506001600160a01b03919091166000908152600660209081526040808320938352929052205490565b6104eb838383604051806020016040528060008152506107af565b6105dc336104f5565b6105f85760405162461bcd60e51b815260040161044a90611bf4565b61060181610b81565b50565b600061060f60085490565b82106106725760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b606482015260840161044a565b6008828154811061068557610685611c41565b90600052602060002001549050919050565b6000818152600260205260408120546001600160a01b0316806103175760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b604482015260640161044a565b60006001600160a01b0382166107615760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b606482015260840161044a565b506001600160a01b031660009081526003602052604090205490565b610785610b8a565b61078f6000610be4565b565b60606001805461032c90611bba565b6107ab338383610c36565b5050565b6107b93383610991565b6107d55760405162461bcd60e51b815260040161044a90611bf4565b6107e184848484610d04565b50505050565b606061031782610d37565b6107fa610b8a565b6000610805600c5490565b9050610815600c80546001019055565b61081f8382610e4b565b6104eb8183610e65565b610831610b8a565b6001600160a01b0381166108965760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161044a565b61060181610be4565b60006001600160e01b0319821663780e9d6360e01b1480610317575061031782610ef8565b6000818152600260205260409020546001600160a01b03166106015760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b604482015260640161044a565b600081815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061095882610697565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b60008061099d83610697565b9050806001600160a01b0316846001600160a01b031614806109e457506001600160a01b0380821660009081526005602090815260408083209388168352929052205460ff165b80610a085750836001600160a01b03166109fd846103af565b6001600160a01b0316145b949350505050565b826001600160a01b0316610a2382610697565b6001600160a01b031614610a495760405162461bcd60e51b815260040161044a90611c57565b6001600160a01b038216610aab5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b606482015260840161044a565b610ab88383836001610f48565b826001600160a01b0316610acb82610697565b6001600160a01b031614610af15760405162461bcd60e51b815260040161044a90611c57565b600081815260046020908152604080832080546001600160a01b03199081169091556001600160a01b0387811680865260038552838620805460001901905590871680865283862080546001019055868652600290945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b61060181610f54565b600b546001600160a01b0316331461078f5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161044a565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b816001600160a01b0316836001600160a01b031603610c975760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c657200000000000000604482015260640161044a565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b610d0f848484610a10565b610d1b84848484610f94565b6107e15760405162461bcd60e51b815260040161044a90611c9c565b6060610d42826108c4565b6000828152600a602052604081208054610d5b90611bba565b80601f0160208091040260200160405190810160405280929190818152602001828054610d8790611bba565b8015610dd45780601f10610da957610100808354040283529160200191610dd4565b820191906000526020600020905b815481529060010190602001808311610db757829003601f168201915b505050505090506000610dfe6040805180820190915260048152631d195cdd60e21b602082015290565b90508051600003610e10575092915050565b815115610e42578082604051602001610e2a929190611cee565b60405160208183030381529060405292505050919050565b610a0884611095565b6107ab828260405180602001604052806000815250611115565b6000828152600260205260409020546001600160a01b0316610ee05760405162461bcd60e51b815260206004820152602e60248201527f45524337323155524953746f726167653a2055524920736574206f66206e6f6e60448201526d32bc34b9ba32b73a103a37b5b2b760911b606482015260840161044a565b6000828152600a602052604090206104eb8282611d6b565b60006001600160e01b031982166380ac58cd60e01b1480610f2957506001600160e01b03198216635b5e139f60e01b145b8061031757506301ffc9a760e01b6001600160e01b0319831614610317565b6107e184848484611148565b610f5d81611288565b6000818152600a602052604090208054610f7690611bba565b159050610601576000818152600a6020526040812061060191611847565b60006001600160a01b0384163b1561108a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290610fd8903390899088908890600401611e2b565b6020604051808303816000875af1925050508015611013575060408051601f3d908101601f1916820190925261101091810190611e68565b60015b611070573d808015611041576040519150601f19603f3d011682016040523d82523d6000602084013e611046565b606091505b5080516000036110685760405162461bcd60e51b815260040161044a90611c9c565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610a08565b506001949350505050565b60606110a0826108c4565b60006110c36040805180820190915260048152631d195cdd60e21b602082015290565b905060008151116110e3576040518060200160405280600081525061110e565b806110ed8461132b565b6040516020016110fe929190611cee565b6040516020818303038152906040525b9392505050565b61111f83836113be565b61112c6000848484610f94565b6104eb5760405162461bcd60e51b815260040161044a90611c9c565b61115484848484611557565b60018111156111c35760405162461bcd60e51b815260206004820152603560248201527f455243373231456e756d657261626c653a20636f6e7365637574697665207472604482015274185b9cd9995c9cc81b9bdd081cdd5c1c1bdc9d1959605a1b606482015260840161044a565b816001600160a01b03851661121f5761121a81600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b611242565b836001600160a01b0316856001600160a01b0316146112425761124285826115df565b6001600160a01b03841661125e576112598161167c565b611281565b846001600160a01b0316846001600160a01b03161461128157611281848261172b565b5050505050565b600061129382610697565b90506112a3816000846001610f48565b6112ac82610697565b600083815260046020908152604080832080546001600160a01b03199081169091556001600160a01b0385168085526003845282852080546000190190558785526002909352818420805490911690555192935084927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b606060006113388361176f565b600101905060008167ffffffffffffffff81111561135857611358611a1d565b6040519080825280601f01601f191660200182016040528015611382576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461138c57509392505050565b6001600160a01b0382166114145760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015260640161044a565b6000818152600260205260409020546001600160a01b0316156114795760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640161044a565b611487600083836001610f48565b6000818152600260205260409020546001600160a01b0316156114ec5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640161044a565b6001600160a01b038216600081815260036020908152604080832080546001019055848352600290915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60018111156107e1576001600160a01b0384161561159d576001600160a01b03841660009081526003602052604081208054839290611597908490611e9b565b90915550505b6001600160a01b038316156107e1576001600160a01b038316600090815260036020526040812080548392906115d4908490611eae565b909155505050505050565b600060016115ec846106f7565b6115f69190611e9b565b600083815260076020526040902054909150808214611649576001600160a01b03841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b5060009182526007602090815260408084208490556001600160a01b039094168352600681528383209183525290812055565b60085460009061168e90600190611e9b565b600083815260096020526040812054600880549394509092849081106116b6576116b6611c41565b9060005260206000200154905080600883815481106116d7576116d7611c41565b600091825260208083209091019290925582815260099091526040808220849055858252812055600880548061170f5761170f611ec1565b6001900381819060005260206000200160009055905550505050565b6000611736836106f7565b6001600160a01b039093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106117ae5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef810000000083106117da576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106117f857662386f26fc10000830492506010015b6305f5e1008310611810576305f5e100830492506008015b612710831061182457612710830492506004015b60648310611836576064830492506002015b600a83106103175760010192915050565b50805461185390611bba565b6000825580601f10611863575050565b601f01602090049060005260206000209081019061060191905b80821115611891576000815560010161187d565b5090565b6001600160e01b03198116811461060157600080fd5b6000602082840312156118bd57600080fd5b813561110e81611895565b60005b838110156118e35781810151838201526020016118cb565b50506000910152565b600081518084526119048160208601602086016118c8565b601f01601f19169290920160200192915050565b60208152600061110e60208301846118ec565b60006020828403121561193d57600080fd5b5035919050565b80356001600160a01b038116811461195b57600080fd5b919050565b6000806040838503121561197357600080fd5b61197c83611944565b946020939093013593505050565b60008060006060848603121561199f57600080fd5b6119a884611944565b92506119b660208501611944565b9150604084013590509250925092565b6000602082840312156119d857600080fd5b61110e82611944565b600080604083850312156119f457600080fd5b6119fd83611944565b915060208301358015158114611a1257600080fd5b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff80841115611a4e57611a4e611a1d565b604051601f8501601f19908116603f01168101908282118183101715611a7657611a76611a1d565b81604052809350858152868686011115611a8f57600080fd5b858560208301376000602087830101525050509392505050565b60008060008060808587031215611abf57600080fd5b611ac885611944565b9350611ad660208601611944565b925060408501359150606085013567ffffffffffffffff811115611af957600080fd5b8501601f81018713611b0a57600080fd5b611b1987823560208401611a33565b91505092959194509250565b60008060408385031215611b3857600080fd5b611b4183611944565b9150602083013567ffffffffffffffff811115611b5d57600080fd5b8301601f81018513611b6e57600080fd5b611b7d85823560208401611a33565b9150509250929050565b60008060408385031215611b9a57600080fd5b611ba383611944565b9150611bb160208401611944565b90509250929050565b600181811c90821680611bce57607f821691505b602082108103611bee57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252602d908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526c1c881bdc88185c1c1c9bdd9959609a1b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b60208082526025908201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060408201526437bbb732b960d91b606082015260800190565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b60008351611d008184602088016118c8565b835190830190611d148183602088016118c8565b01949350505050565b601f8211156104eb57600081815260208120601f850160051c81016020861015611d445750805b601f850160051c820191505b81811015611d6357828155600101611d50565b505050505050565b815167ffffffffffffffff811115611d8557611d85611a1d565b611d9981611d938454611bba565b84611d1d565b602080601f831160018114611dce5760008415611db65750858301515b600019600386901b1c1916600185901b178555611d63565b600085815260208120601f198616915b82811015611dfd57888601518255948401946001909101908401611dde565b5085821015611e1b5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090611e5e908301846118ec565b9695505050505050565b600060208284031215611e7a57600080fd5b815161110e81611895565b634e487b7160e01b600052601160045260246000fd5b8181038181111561031757610317611e85565b8082018082111561031757610317611e85565b634e487b7160e01b600052603160045260246000fdfea26469706673582212207960bc65d7484ab1502ccdc352c91f0fb91535a525cf99e1b6392e242548279a64736f6c63430008110033 \ No newline at end of file --- a/js-packages/scripts/src/benchmarks/utils/types.ts +++ /dev/null @@ -1,62 +0,0 @@ -export interface IFeeGas { - fee?: number | bigint, - gas?: number | bigint, - substrate?: number, - zeppelin?: { - fee: number | bigint, - gas: number | bigint, - } - -} -export interface IFeeGasCsv extends IFeeGasVm { - function: string, -} -export interface IFeeGasVm{ - ethFee?: number | bigint, - ethGas?: number | bigint, - substrate?: number, - zeppelinFee?: number | bigint, - zeppelinGas?: number | bigint -} -export interface IFunctionFee { - [name: string]: IFeeGas -} - - -export abstract class FunctionFeeVM { - [name: string]: IFeeGasVm - - static toCsv(model: FunctionFeeVM): IFeeGasCsv[]{ - const res: IFeeGasCsv[] = []; - Object.keys(model).forEach(key => { - res.push({ - function: key, - ethFee: model[key].ethFee, - ethGas: model[key].ethGas, - substrate: model[key].substrate, - zeppelinFee: model[key].zeppelinFee, - zeppelinGas: model[key].zeppelinGas, - }); - }); - return res; - } - static fromModel(model: IFunctionFee): FunctionFeeVM { - const res: FunctionFeeVM = {}; - - Object.keys(model).forEach(key => { - res[key] = {}; - if(model[key].fee && model[key].gas) { - res[key].ethFee = model[key].fee; - res[key].ethGas = model[key].gas; - } - if(model[key].substrate) - res[key].substrate = model[key].substrate; - if(model[key].zeppelin) { - res[key].zeppelinFee = model[key].zeppelin?.fee; - res[key].zeppelinGas = model[key].zeppelin?.gas; - } - }); - - return res; - } -} \ No newline at end of file --- a/js-packages/scripts/src/calibrate.ts +++ /dev/null @@ -1,318 +0,0 @@ -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingEthPlaygrounds} from '@unique/tests/src/eth/util/index.js'; -import {EthUniqueHelper} from '@unique/tests/src//eth/util/playgrounds/unique.dev.js'; - -class Fract { - static ZERO = new Fract(0n); - constructor(public readonly a: bigint, public readonly b: bigint = 1n) { - if(b === 0n) throw new Error('division by zero'); - if(b < 0n) throw new Error('missing normalization'); - } - - mul(other: Fract) { - return new Fract(this.a * other.a, this.b * other.b).optimize(); - } - - div(other: Fract) { - return this.mul(other.inv()); - } - - plus(other: Fract) { - if(this.b === other.b) { - return new Fract(this.a + other.a, this.b); - } - return new Fract(this.a * other.b + other.a * this.b, this.b * other.b).optimize(); - } - - minus(other: Fract) { - return this.plus(other.neg()); - } - - neg() { - return new Fract(-this.a, this.b); - } - inv() { - if(this.a < 0) { - return new Fract(-this.b, -this.a); - } else { - return new Fract(this.b, this.a); - } - } - - optimize() { - function gcd(x: bigint, y: bigint) { - if(x < 0n) - x = -x; - if(y < 0n) - y = -y; - while(y) { - const t = y; - y = x % y; - x = t; - } - return x; - } - const v = gcd(this.a, this.b); - return new Fract(this.a / v, this.b / v); - } - - toBigInt() { - return this.a / this.b; - } - toNumber() { - const v = this.optimize(); - return Number(v.a) / Number(v.b); - } - toString() { - const v = this.optimize(); - return `${v.a} / ${v.b}`; - } - - lt(other: Fract) { - return this.a * other.b < other.a * this.b; - } - eq(other: Fract) { - return this.a * other.b === other.a * this.b; - } - - sqrt() { - if(this.a < 0n) { - throw new Error('square root of negative numbers is not supported'); - } - - if(this.lt(new Fract(2n))) { - return this; - } - - function newtonIteration(n: Fract, x0: Fract): Fract { - const x1 = rpn(n, x0, '/', x0, '+', new Fract(2n), '/'); - if(x0.eq(x1) || x0.eq(x1.minus(new Fract(1n)))) { - return x0; - } - return newtonIteration(n, x1); - } - - return newtonIteration(this, new Fract(1n)); - } -} - -type Op = Fract | '+' | '-' | '*' | '/' | 'dup' | Op[]; -function rpn(...ops: (Op)[]) { - const stack: Fract[] = []; - for(const op of ops) { - if(op instanceof Fract) { - stack.push(op); - } else if(op === '+') { - if(stack.length < 2) - throw new Error('stack underflow'); - const b = stack.pop()!; - const a = stack.pop()!; - stack.push(a.plus(b)); - } else if(op === '*') { - if(stack.length < 2) - throw new Error('stack underflow'); - const b = stack.pop()!; - const a = stack.pop()!; - stack.push(a.mul(b)); - } else if(op === '-') { - if(stack.length < 2) - throw new Error('stack underflow'); - const b = stack.pop()!; - const a = stack.pop()!; - stack.push(a.minus(b)); - } else if(op === '/') { - if(stack.length < 2) - throw new Error('stack underflow'); - const b = stack.pop()!; - const a = stack.pop()!; - stack.push(a.div(b)); - } else if(op === 'dup') { - if(stack.length < 1) - throw new Error('stack underflow'); - const a = stack.pop()!; - stack.push(a); - stack.push(a); - } else if(Array.isArray(op)) { - stack.push(rpn(...op)); - } else { - throw new Error(`unknown operand: ${op}`); - } - } - if(stack.length != 1) - throw new Error('one element should be left on stack'); - return stack[0]!; -} - -function linearRegression(points: { x: Fract, y: Fract }[]) { - let sumxy = Fract.ZERO; - let sumx = Fract.ZERO; - let sumy = Fract.ZERO; - let sumx2 = Fract.ZERO; - const n = points.length; - for(let i = 0; i < n; i++) { - const p = points[i]; - sumxy = rpn(p.x, p.y, '*', sumxy, '+'); - sumx = sumx.plus(p.x); - sumy = sumy.plus(p.y); - sumx2 = rpn(p.x, p.x, '*', sumx2, '+'); - } - - const nb = new Fract(BigInt(n)); - - const a = rpn( - [nb, sumxy, '*', sumx, sumy, '*', '-'], - [nb, sumx2, '*', sumx, sumx, '*', '-'], - '/', - ); - const b = rpn( - [sumy, a, sumx, '*', '-'], - nb, - '/', - ); - - return {a, b}; -} - -const hypothesisLinear = (a: Fract, b: Fract) => (x: Fract) => rpn(x, a, '*', b, '+'); - -// function error(points: { x: Fract, y: Fract }[], hypothesis: (a: Fract) => Fract) { -// return points.map(p => { -// const v = hypothesis(p.x); -// const vv = p.y; - -// return rpn(v, vv, '-', 'dup', '*'); -// }).reduce((a, b) => a.plus(b), Fract.ZERO).sqrt().div(new Fract(BigInt(points.length))); -// } - -async function calibrateWeightToFee(helper: EthUniqueHelper, privateKey: (account: string) => Promise) { - const alice = await privateKey('//Alice'); - const bob = await privateKey('//Bob'); - const dataPoints = []; - - { - const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}); - const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); - await token.transfer(alice, {Substrate: bob.address}); - const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); - - console.log(`\t[NFT transfer] Original price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`); - } - - const api = helper.getApi(); - const base = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt(); - for(let i = -5; i < 5; i++) { - await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(base + base / 1000n * BigInt(i)))); - - const coefficient = new Fract((await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt()); - const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}); - - const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); - await token.transfer(alice, {Substrate: bob.address}); - const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); - - const transferPrice = new Fract(aliceBalanceBefore - aliceBalanceAfter); - - dataPoints.push({x: transferPrice, y: coefficient}); - } - const {a, b} = linearRegression(dataPoints); - - const hyp = hypothesisLinear(a, b); - // console.log(`\t[NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`); - - // 0.1 UNQ - const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(1n, 10n), '*')); - await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(perfectValue.toBigInt()))); - - { - const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}); - const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); - await token.transfer(alice, {Substrate: bob.address}); - const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); - - console.log(`\t[NFT transfer] Calibrated price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`); - } -} - -async function calibrateMinGasPrice(helper: EthUniqueHelper, privateKey: (account: string) => Promise) { - const alice = await privateKey('//Alice'); - const caller = await helper.eth.createAccountWithBalance(alice); - const receiver = helper.eth.createAccount(); - const dataPoints = []; - - { - const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); - const token = await collection.mintToken(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft', caller); - - const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS})); - - console.log(`\t[ETH NFT transfer] Original price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`); - } - - const api = helper.getApi(); - // const defaultCoeff = (api.consts.configuration.defaultMinGasPrice as any).toBigInt(); - const base = (await api.query.configuration.minGasPriceOverride() as any).toBigInt(); - for(let i = -8; i < 8; i++) { - const gasPrice = base + base / 100000n * BigInt(i); - const gasPriceStr = '0x' + gasPrice.toString(16); - await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(gasPrice))); - - const coefficient = new Fract((await api.query.configuration.minGasPriceOverride() as any).toBigInt()); - const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); - const token = await collection.mintToken(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft', caller); - - const transferPrice = new Fract(await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gasPrice: gasPriceStr, gas: helper.eth.DEFAULT_GAS}))); - - dataPoints.push({x: transferPrice, y: coefficient}); - } - - const {a, b} = linearRegression(dataPoints); - - const hyp = hypothesisLinear(a, b); - // console.log(`\t[ETH NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`); - - // 0.15 UNQ - const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(15n, 100n), '*')); - await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(perfectValue.toBigInt()))); - - { - const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); - const token = await collection.mintToken(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft', caller); - - const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS})); - - console.log(`\t[ETH NFT transfer] Calibrated price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`); - } -} - -// eslint-disable-next-line @typescript-eslint/no-floating-promises -(async () => { - await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => { - // Subsequent runs reduce error, as price line is not actually straight, this is a curve - - const iterations = 3; - - console.log('[Calibrate WeightToFee]'); - for(let i = 0; i < iterations; i++) { - await calibrateWeightToFee(helper, privateKey); - } - - console.log(); - - console.log('[Calibrate MinGasPrice]'); - for(let i = 0; i < iterations; i++) { - await calibrateMinGasPrice(helper, privateKey); - } - }); -})(); --- a/js-packages/scripts/src/calibrateApply.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {readFile, writeFile} from 'fs/promises'; -import path from 'path'; -import {makeNames, usingPlaygrounds} from '@unique/tests/src/util/index.js'; - -const {dirname} = makeNames(import.meta.url); - -const formatNumber = (num: string): string => num.split('').reverse().join('').replace(/([0-9]{3})/g, '$1_').split('').reverse().join('').replace(/^_/, ''); - -(async () => { - let weightToFeeCoefficientOverride: string; - let minGasPriceOverride: string; - await usingPlaygrounds(async (helpers, _privateKey) => { - weightToFeeCoefficientOverride = (await helpers.getApi().query.configuration.weightToFeeCoefficientOverride() as any).toBigInt().toString(); - minGasPriceOverride = (await helpers.getApi().query.configuration.minGasPriceOverride() as any).toBigInt().toString(); - }); - const constantsFile = path.resolve(dirname, '../../primitives/common/src/constants.rs'); - let constants = (await readFile(constantsFile)).toString(); - - let weight2feeFound = false; - constants = constants.replace(/(\/\*\*\/)[0-9_]+(\/\*<\/weight2fee>\*\/)/, (_f, p, s) => { - weight2feeFound = true; - return p+formatNumber(weightToFeeCoefficientOverride)+s; - }); - if(!weight2feeFound) { - throw new Error('failed to find weight2fee marker in source code'); - } - - let minGasPriceFound = false; - constants = constants.replace(/(\/\*\*\/)[0-9_]+(\/\*<\/mingasprice>\*\/)/, (_f, p, s) => { - minGasPriceFound = true; - return p+formatNumber(minGasPriceOverride)+s; - }); - if(!minGasPriceFound) { - throw new Error('failed to find mingasprice marker in source code'); - } - - await writeFile(constantsFile, constants); -})().catch(e => { - console.log(e.stack); -}); --- a/js-packages/scripts/src/fetchMetadata.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {writeFile} from 'fs/promises'; -import {join} from 'path'; -import {exit} from 'process'; -import {fileURLToPath} from 'url'; - -const url = process.env.RPC_URL; -if(!url) throw new Error('RPC_URL is not set'); - -const srcDir = fileURLToPath(new URL('.', import.meta.url)); - -for(let i = 0; i < 10; i++) { - try { - console.log(`Trying to fetch metadata, retry ${i + 1}/${10}`); - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - redirect: 'follow', - body: JSON.stringify({ - jsonrpc: '2.0', - id: '1', - method: 'state_getMetadata', - params: [], - }), - }); - const json = await response.json(); - const output = join(srcDir, 'metadata.json'); - console.log(`Received response, saving to ${output}`); - await writeFile(output, JSON.stringify(json)); - exit(0); - } catch (e) { - console.error('Failed to request metadata:'); - console.error(e); - console.error('Waiting 1 minute'); - await new Promise(res => setTimeout(res, 60 * 1000)); - } -} -console.error('Out of retries'); -exit(1); --- a/js-packages/scripts/src/generateEnv.ts +++ /dev/null @@ -1,87 +0,0 @@ -import {ApiPromise, WsProvider} from '@polkadot/api'; -import {readFile} from 'fs/promises'; -import {join} from 'path'; -import {makeNames} from '@unique/tests/src/util/index.js'; - -const {dirname} = makeNames(import.meta.url); - -async function fetchVersion(chain: string): Promise { - const api = await ApiPromise.create({provider: new WsProvider(chain)}); - const last = (await api.query.system.lastRuntimeUpgrade()).toJSON(); - await api.disconnect(); - return (last as any).specVersion.toString(); -} - -function setVar(env: string, key: string, value: string): string { - let found = false; - const newEnv = env.replace(new RegExp(`${key}=.+?\n`), () => { - found = true; - return `${key}=${value}\n`; - }); - if(!found) throw new Error(`env key "${key}" is not found`); - return newEnv; -} - -// Fetch and format version string -async function ff(url: string, regex: RegExp, rep: string | ((substring: string, ...params:any[]) => string)): Promise { - const ver = await fetchVersion(url); - if(ver.match(regex) === null) - throw new Error(`bad regex for ${url}`); - return ver.replace(regex, rep as any); -} -function fixupUnique(version: string): string { - if(version === 'release-v930033') - return 'release-v930033-fix-gas-price'; - if(version === 'release-v930034') - return 'release-v930034-fix-gas-price'; - return version; -} - -(async () => { - let env = (await readFile(join(dirname, '../../.env'))).toString(); - await Promise.all([ - ff('wss://rpc.polkadot.io/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'POLKADOT_MAINNET_BRANCH', v)), - ff('wss://statemint-rpc.polkadot.io/', /^(....)$/, 'release-parachains-v$1').then(v => env = setVar(env, 'STATEMINT_BUILD_BRANCH', v)), - ff('wss://acala-rpc-0.aca-api.network/', /^(.)(..)(.)$/, '$1.$2.$3').then(v => env = setVar(env, 'ACALA_BUILD_BRANCH', v)), - ff('wss://wss.api.moonbeam.network/', /^(....)$/, 'runtime-$1').then(v => env = setVar(env, 'MOONBEAM_BUILD_BRANCH', v)), - ff('wss://ws.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'UNIQUE_MAINNET_BRANCH', fixupUnique(v))), - - ff('wss://kusama-rpc.polkadot.io/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'KUSAMA_MAINNET_BRANCH', v)), - ff('wss://statemine-rpc.polkadot.io/', /^(....)$/, 'release-parachains-v$1').then(v => env = setVar(env, 'STATEMINE_BUILD_BRANCH', v)), - ff('wss://karura-rpc-0.aca-api.network/', /^(.)(..)(.)$/, 'release-karura-$1.$2.$3').then(v => env = setVar(env, 'KARURA_BUILD_BRANCH', v)), - ff('wss://wss.api.moonriver.moonbeam.network/', /^(....)$/, 'runtime-$1').then(v => env = setVar(env, 'MOONRIVER_BUILD_BRANCH', v)), - ff('wss://ws-quartz.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'QUARTZ_MAINNET_BRANCH', fixupUnique(v))), - - ff('wss://ws-westend.unique.network/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'UNIQUEWEST_MAINNET_BRANCH', v)), - ff('wss://westmint-rpc.polkadot.io/', /^(....)$/, 'parachains-v$1').then(v => env = setVar(env, 'WESTMINT_BUILD_BRANCH', v)), - ff('wss://ws-opal.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'OPAL_MAINNET_BRANCH', fixupUnique(v))), - - ff('wss://ws-eastend.unique.network/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'UNIQUEEAST_MAINNET_BRANCH', v)), - ff('wss://ws-sapphire.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'SAPPHIRE_MAINNET_BRANCH', fixupUnique(v))), - - ff('wss://rpc.astar.network/', /^(.+)$/, (_, r) => { - switch (r) { - case '55': return 'v5.3.0'; - case '57': return 'v5.4.0'; - case '61': return 'v5.11.0'; - case '66': return 'v5.18.0'; - default: throw new Error('unknown astar branch for runtime ' + r); - } - }).then(v => env = setVar(env, 'ASTAR_BUILD_BRANCH', v)), - ff('wss://shiden.api.onfinality.io/public-ws', /^(.+)$/, (_, r) => { - switch (r) { - case '90': return 'v4.49.0'; - case '96': return 'v5.4.0'; - case '100': return 'v5.10.0'; - case '104': return 'v5.15.0'; - case '106': return 'v5.18.0'; - default: throw new Error('unknown shiden branch for runtime ' + r); - } - }).then(v => env = setVar(env, 'SHIDEN_BUILD_BRANCH', v)), - ]); - console.log(env); -})().catch(e => { - console.error('Fatal'); - console.error(e.stack); - process.exit(1); -}); --- a/js-packages/scripts/src/generate_types/functions.sh +++ /dev/null @@ -1,4 +0,0 @@ - -function do_rpc { - curl -s --header "Content-Type: application/json" -XPOST --data "{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"$1\",\"params\":[$2]}" $RPC_URL -} --- a/js-packages/scripts/src/generate_types/generate_types_package.sh +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env bash - -set -eu - -DIR=$(realpath $(dirname "$0")) -TEMPLATE=$DIR/types_template -GIT_REPO=git@github.com:UniqueNetwork/unique-types-js.git - -. $DIR/functions.sh - -usage() { - echo "Usage: [RPC_URL=http://localhost:9944] $0 <--rc|--release> [--force] [--push] [--rpc-url=http://localhost:9944]" 1>&2 - exit 1 -} - -rc= -release= -force= -push= - -for i in "$@"; do -case $i in - --rc) - rc=1 - if test "$release"; then usage; fi - ;; - --release) - release=1 - if test "$rc"; then usage; fi - ;; - --force) - force=1 - ;; - --push) - push=1 - ;; - --rpc-url=*) - RPC_URL=${i#*=} - ;; - *) - usage - ;; -esac -done - -if test \( ! \( "$rc" -o "$release" \) \) -o \( "${RPC_URL=}" = "" \); then - usage -elif test "$rc"; then - echo "Rc build" -else - echo "Release build" -fi - -cd $DIR/.. -yarn polkadot-types - -version=$(do_rpc state_getRuntimeVersion "") -spec_version=$(echo $version | jq -r .result.specVersion) -spec_name=$(echo $version | jq -r .result.specName) -echo "Spec version: $spec_version, name: $spec_name" - -case $spec_name in - opal) - package_name=@unique-nft/opal-testnet-types - repo_branch=opal-testnet - repo_tag=$repo_branch - ;; - sapphire) - package_name=@unique-nft/sapphire-mainnet-types - repo_branch=sapphire-mainnet - repo_tag=$repo_branch - ;; - quartz) - package_name=@unique-nft/quartz-mainnet-types - repo_branch=quartz-mainnet - repo_tag=$repo_branch - ;; - unique) - package_name=@unique-nft/unique-mainnet-types - repo_branch=master - repo_tag=unique-mainnet - ;; - *) - echo "unknown spec name: $spec_name" - exit 1 - ;; -esac - -if test "$rc" = 1; then - if "$spec_name" != opal; then - echo "rc types can only be based on opal spec" - exit 1 - fi - package_name=@unique-nft/rc-types - repo_branch=rc - repo_tag=$repo_branch -fi - -package_version=${spec_version:0:4}.$(echo ${spec_version:4:4} | sed 's/^0*//'). -last_patch=NEVER -for tag in $(git ls-remote -t --refs $GIT_REPO | cut -f 2 | sort -r); do - tag_prefix=refs/tags/$repo_tag-v$package_version - if [[ $tag == $tag_prefix* ]]; then - last_patch=${tag#$tag_prefix} - break; - fi -done -echo "Package version: ${package_version}X, name: $package_name" -echo "Last published: $package_version$last_patch" - -if test "$last_patch" = "NEVER"; then - new_package_version=${package_version}0 -else - new_package_version=${package_version}$((last_patch+1)) -fi -package_version=${package_version}$last_patch -echo "New package version: $new_package_version" - -pjsapi_ver=^$(cat $DIR/../package.json | jq -r '.dependencies."@polkadot/api"' | sed -e "s/^\^//") -tsnode_ver=^$(cat $DIR/../package.json | jq -r '.devDependencies."ts-node"' | sed -e "s/^\^//") -ts_ver=^$(cat $DIR/../package.json | jq -r '.devDependencies."typescript"' | sed -e "s/^\^//") - -gen=$(mktemp -d) -pushd $gen -git clone $GIT_REPO -b $repo_branch --depth 1 . -if test "$last_patch" != "NEVER"; then - git reset --hard $repo_tag-v$package_version -fi -git rm -r "*" -popd - -# Using old package_version here, becaue we first check if -# there is any difference between generated and already uplaoded types -cat $TEMPLATE/package.json \ -| jq '.private = false' - \ -| jq '.name = "'$package_name'"' - \ -| jq '.version = "'$package_version'"' - \ -| jq '.peerDependencies."@polkadot/api" = "'$pjsapi_ver'"' - \ -| jq '.peerDependencies."@polkadot/types" = "'$pjsapi_ver'"' - \ -| jq '.devDependencies."@polkadot/api" = "'$pjsapi_ver'"' - \ -| jq '.devDependencies."@polkadot/types" = "'$pjsapi_ver'"' - \ -| jq '.devDependencies."ts-node" = "'$tsnode_ver'"' - \ -| jq '.devDependencies."typescript" = "'$ts_ver'"' - \ -> $gen/package.json -for file in .gitignore .npmignore README.md tsconfig.json; do - cp $TEMPLATE/$file $gen/ -done -package_name_replacement=$(printf '%s\n' "$package_name" | sed -e 's/[\/&]/\\&/g') -sed -i 's/PKGNAME/'$package_name_replacement'/' $gen/README.md - -rsync -ar --exclude .gitignore src/interfaces/ $gen -for file in $gen/augment-* $gen/**/types.ts $gen/registry.ts; do - sed -i '1s;^;//@ts-nocheck\n;' $file -done - -pushd $gen -git add . -popd - -pushd $gen -if git diff --quiet HEAD && test ! "$force"; then - echo "no changes detected" - exit 0 -fi -popd - -mv $gen/package.json $gen/package.old.json -cat $gen/package.old.json \ -| jq '.version = "'$new_package_version'"' - \ -> $gen/package.json -rm $gen/package.old.json -pushd $gen -git add package.json -popd - -echo "package.json contents:" -cat $gen/package.json -echo "overall diff:" -pushd $gen -git status -git diff HEAD || true -popd - -# This check is only active if running in interactive terminal -if [ -t 0 ]; then - read -p "Is everything ok at $gen [y/n]? " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Aborting!" - exit 1 - fi -fi - -pushd $gen -yarn -yarn prepublish -git commit -m "chore: upgrade types to v$new_package_version" -git tag --force $repo_tag-v$new_package_version -if test "$push" = 1; then - git push --tags --force -u origin HEAD -else - echo "--push not given, origin repo left intact" - echo "To publish manually, go to $gen, and run \"git push --tags --force -u origin HEAD\"" -fi -popd --- a/js-packages/scripts/src/generate_types/readyness.js +++ /dev/null @@ -1,35 +0,0 @@ -import {ApiPromise, WsProvider} from '@polkadot/api'; - -const connect = async () => { - const wsEndpoint = 'ws://127.0.0.1:9944'; - const api = new ApiPromise({provider: new WsProvider(wsEndpoint)}); - await api.isReadyOrError; - - const head = (await api.rpc.chain.getHeader()).number.toNumber(); - await api.disconnect(); - if(head < 1) throw Error('No block #1'); - -}; - -const sleep = time => new Promise(resolve => { - setTimeout(() => resolve(), time); -}); - -const main = async () => { - // eslint-disable-next-line no-constant-condition - while(true) { - try { - await connect(); - break; - } - catch (e) { - await sleep(10000); - console.log(e); - } - } -}; - -main().then(() => process.exit(0)).catch(e => { - console.error(e); - process.exit(1); -}); --- a/js-packages/scripts/src/generate_types/types_template/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.js -*.map -*.d.ts -/node_modules -metadata.json --- a/js-packages/scripts/src/generate_types/types_template/.npmignore +++ /dev/null @@ -1 +0,0 @@ -/src --- a/js-packages/scripts/src/generate_types/types_template/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# PKGNAME - -Unique network api types - -Do not edit by hand, those types are generated automatically, and definitions are located in chain repo - -## Using types - -Install library: - -```bash -yarn add --dev PKGNAME -``` - -Replace polkadot.js types with our chain types adding corresponding path override to the tsconfig `compilerOptions.paths` section: - -```json -// in tsconfig.json -{ - "compilerOptions": { - "paths": { - "@polkadot/types/lookup": ["node_modules/PKGNAME/types-lookup"] - } - } -} -``` - -Since polkadot v7 api augmentations not loaded by default, in every file, where you need to access `api.tx`, `api.query`, `api.rpc`, etc; you should explicitly import corresponding augmentation before any other `polkadot.js` related import: -``` -import 'PKGNAME/augment-api'; -``` --- a/js-packages/scripts/src/generate_types/types_template/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "TODO", - "private": true, - "version": "TODO", - "main": "index.js", - "repository": "git@github.com:UniqueNetwork/unique-types-js.git", - "homepage": "https://unique.network/", - "license": "MIT", - "scripts": { - "prepublish": "tsc -d" - }, - "peerDependencies": { - "@polkadot/api": "TODO", - "@polkadot/types": "TODO" - }, - "devDependencies": { - "@polkadot/api": "TODO", - "@polkadot/types": "TODO", - "ts-node": "TODO", - "typescript": "TODO" - } -} --- a/js-packages/scripts/src/generate_types/types_template/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "exclude": [ - "node_modules", - "node_modules/**/*", - "../node_modules/**/*", - "**/node_modules/**/*" - ], - "compilerOptions": { - "target": "ES2020", - "moduleResolution": "node", - "esModuleInterop": true, - "resolveJsonModule": true, - "module": "commonjs", - "sourceMap": true, - "outDir": ".", - "rootDir": ".", - "strict": true, - "paths": { - }, - "skipLibCheck": true, - }, - "include": [ - "**/*", - ], - "lib": [ - "es2017" - ], -} --- a/js-packages/scripts/src/generate_types/wait_for_first_block.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -DIR=$(dirname "$0") - -. $DIR/functions.sh - -last_block_id=0 -block_id=0 -function get_block { - block_id_hex=$(do_rpc chain_getHeader | jq -r .result.number) - block_id=$((${block_id_hex})) - echo Id = $block_id -} - -function had_new_block { - last_block_id=$block_id - get_block - if (( last_block_id != 0 && block_id > last_block_id )); then - return 0 - fi - return 1 -} - -function reset_check { - last_block_id=0 - block_id=0 -} - -while ! had_new_block; do - echo "Waiting for next block..." - sleep 12 -done -reset_check - -echo "Chain is running, but lets wait for another block after a minute, to avoid startup flakiness." -sleep 60 - -while ! had_new_block; do - echo "Waiting for another block..." - sleep 12 -done - -echo "Chain is running!" --- a/js-packages/scripts/src/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"jsonrpc":"2.0","result":"0x6d6574610eed0a000c1c73705f636f72651863727970746f2c4163636f756e7449643332000004000401205b75383b2033325d0000040000032000000008000800000503000c08306672616d655f73797374656d2c4163636f756e74496e666f08144e6f6e636501102c4163636f756e74446174610114001401146e6f6e63651001144e6f6e6365000124636f6e73756d657273100120526566436f756e7400012470726f766964657273100120526566436f756e7400012c73756666696369656e7473100120526566436f756e740001106461746114012c4163636f756e74446174610000100000050500140c3c70616c6c65745f62616c616e6365731474797065732c4163636f756e7444617461041c42616c616e63650118001001106672656518011c42616c616e6365000120726573657276656418011c42616c616e636500011866726f7a656e18011c42616c616e6365000114666c6167731c01284578747261466c61677300001800000507001c0c3c70616c6c65745f62616c616e636573147479706573284578747261466c61677300000400180110753132380000200c346672616d655f737570706f7274206469737061746368405065724469737061746368436c6173730404540124000c01186e6f726d616c2401045400012c6f7065726174696f6e616c240104540001246d616e6461746f7279240104540000240c2873705f77656967687473247765696768745f76321857656967687400000801207265665f74696d6528010c75363400012870726f6f665f73697a6528010c7536340000280000062c002c000005060030083c7072696d69746976655f74797065731048323536000004000401205b75383b2033325d000034000002080038102873705f72756e74696d651c67656e65726963186469676573741844696765737400000401106c6f67733c013c5665633c4469676573744974656d3e00003c000002400040102873705f72756e74696d651c67656e6572696318646967657374284469676573744974656d0001142850726552756e74696d650800440144436f6e73656e737573456e67696e654964000034011c5665633c75383e00060024436f6e73656e7375730800440144436f6e73656e737573456e67696e654964000034011c5665633c75383e000400105365616c0800440144436f6e73656e737573456e67696e654964000034011c5665633c75383e000500144f74686572040034011c5665633c75383e0000006452756e74696d65456e7669726f6e6d656e74557064617465640008000044000003040000000800480000024c004c08306672616d655f73797374656d2c4576656e745265636f7264080445015004540130000c011470686173652106011450686173650001146576656e7450010445000118746f706963735d0501185665633c543e00005008306f70616c5f72756e74696d653052756e74696d654576656e740001981853797374656d04005401706672616d655f73797374656d3a3a4576656e743c52756e74696d653e000000485374617465547269654d6967726174696f6e04007801ac70616c6c65745f73746174655f747269655f6d6967726174696f6e3a3a4576656e743c52756e74696d653e0001003c50617261636861696e53797374656d04008401bc63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d3a3a4576656e743c52756e74696d653e00140044436f6c6c61746f7253656c656374696f6e04008c01a470616c6c65745f636f6c6c61746f725f73656c656374696f6e3a3a4576656e743c52756e74696d653e0017001c53657373696f6e040090015470616c6c65745f73657373696f6e3a3a4576656e740018002042616c616e636573040094017c70616c6c65745f62616c616e6365733a3a4576656e743c52756e74696d653e001e00485472616e73616374696f6e5061796d656e7404009c01a870616c6c65745f7472616e73616374696f6e5f7061796d656e743a3a4576656e743c52756e74696d653e0021002054726561737572790400a0017c70616c6c65745f74726561737572793a3a4576656e743c52756e74696d653e002200105375646f0400a4016c70616c6c65745f7375646f3a3a4576656e743c52756e74696d653e0023001c56657374696e670400b401706f726d6c5f76657374696e673a3a4576656e743c52756e74696d653e0025001c58546f6b656e730400c001706f726d6c5f78746f6b656e733a3a4576656e743c52756e74696d653e00260018546f6b656e7304000901016c6f726d6c5f746f6b656e733a3a4576656e743c52756e74696d653e002700204964656e7469747904001501017c70616c6c65745f6964656e746974793a3a4576656e743c52756e74696d653e00280020507265696d61676504001901017c70616c6c65745f707265696d6167653a3a4576656e743c52756e74696d653e0029002444656d6f637261637904001d01018070616c6c65745f64656d6f63726163793a3a4576656e743c52756e74696d653e002a001c436f756e63696c0400310101fc70616c6c65745f636f6c6c6563746976653a3a4576656e743c52756e74696d652c2070616c6c65745f636f6c6c6563746976653a3a496e7374616e6365313e002b0048546563686e6963616c436f6d6d69747465650400390101fc70616c6c65745f636f6c6c6563746976653a3a4576656e743c52756e74696d652c2070616c6c65745f636f6c6c6563746976653a3a496e7374616e6365323e002c0044436f756e63696c4d656d6265727368697004003d0101fc70616c6c65745f6d656d626572736869703a3a4576656e743c52756e74696d652c2070616c6c65745f6d656d626572736869703a3a496e7374616e6365313e002d0070546563686e6963616c436f6d6d69747465654d656d626572736869700400410101fc70616c6c65745f6d656d626572736869703a3a4576656e743c52756e74696d652c2070616c6c65745f6d656d626572736869703a3a496e7374616e6365323e002e005046656c6c6f7773686970436f6c6c6563746976650400450101a070616c6c65745f72616e6b65645f636f6c6c6563746976653a3a4576656e743c52756e74696d653e002f004c46656c6c6f77736869705265666572656e646104005501018070616c6c65745f7265666572656e64613a3a4576656e743c52756e74696d653e003000245363686564756c65720400b905018070616c6c65745f7363686564756c65723a3a4576656e743c52756e74696d653e0031002458636d7051756575650400bd0501a463756d756c75735f70616c6c65745f78636d705f71756575653a3a4576656e743c52756e74696d653e0032002c506f6c6b61646f7458636d0400c105016870616c6c65745f78636d3a3a4576656e743c52756e74696d653e0033002843756d756c757358636d0400c905018863756d756c75735f70616c6c65745f78636d3a3a4576656e743c52756e74696d653e00340020446d7051756575650400cd0501a063756d756c75735f70616c6c65745f646d705f71756575653a3a4576656e743c52756e74696d653e00350034436f6e66696775726174696f6e0400d105019070616c6c65745f636f6e66696775726174696f6e3a3a4576656e743c52756e74696d653e003f0018436f6d6d6f6e0400d505017470616c6c65745f636f6d6d6f6e3a3a4576656e743c52756e74696d653e004200245374727563747572650400d905018070616c6c65745f7374727563747572653a3a4576656e743c52756e74696d653e0046003041707050726f6d6f74696f6e0400dd05019070616c6c65745f6170705f70726f6d6f74696f6e3a3a4576656e743c52756e74696d653e00490034466f726569676e4173736574730400e105019470616c6c65745f666f726569676e5f6173736574733a3a4576656e743c52756e74696d653e0050000c45564d0400e505016870616c6c65745f65766d3a3a4576656e743c52756e74696d653e00640020457468657265756d0400e905015870616c6c65745f657468657265756d3a3a4576656e740065004845766d436f6e747261637448656c7065727304000d0601ac70616c6c65745f65766d5f636f6e74726163745f68656c706572733a3a4576656e743c52756e74696d653e0097003045766d4d6967726174696f6e04001106019070616c6c65745f65766d5f6d6967726174696f6e3a3a4576656e743c52756e74696d653e0099002c4d61696e74656e616e636504001506018870616c6c65745f6d61696e74656e616e63653a3a4576656e743c52756e74696d653e009a001c5574696c69747904001906015470616c6c65745f7574696c6974793a3a4576656e74009c0024546573745574696c7304001d06018470616c6c65745f746573745f7574696c733a3a4576656e743c52756e74696d653e00ff0000540c306672616d655f73797374656d1870616c6c6574144576656e740404540001184045787472696e7369635375636365737304013464697370617463685f696e666f5801304469737061746368496e666f00000490416e2065787472696e73696320636f6d706c65746564207375636365737366756c6c792e3c45787472696e7369634661696c656408013864697370617463685f6572726f7264013444697370617463684572726f7200013464697370617463685f696e666f5801304469737061746368496e666f00010450416e2065787472696e736963206661696c65642e2c436f64655570646174656400020450603a636f6465602077617320757064617465642e284e65774163636f756e7404011c6163636f756e74000130543a3a4163636f756e7449640003046841206e6577206163636f756e742077617320637265617465642e344b696c6c65644163636f756e7404011c6163636f756e74000130543a3a4163636f756e74496400040458416e206163636f756e7420776173207265617065642e2052656d61726b656408011873656e646572000130543a3a4163636f756e7449640001106861736830011c543a3a48617368000504704f6e206f6e2d636861696e2072656d61726b2068617070656e65642e04704576656e7420666f72207468652053797374656d2070616c6c65742e580c346672616d655f737570706f7274206469737061746368304469737061746368496e666f00000c0118776569676874240118576569676874000114636c6173735c01344469737061746368436c617373000120706179735f6665656001105061797300005c0c346672616d655f737570706f7274206469737061746368344469737061746368436c61737300010c184e6f726d616c0000002c4f7065726174696f6e616c000100244d616e6461746f727900020000600c346672616d655f737570706f727420646973706174636810506179730001080c596573000000084e6f0001000064082873705f72756e74696d653444697370617463684572726f72000138144f746865720000003043616e6e6f744c6f6f6b7570000100244261644f726967696e000200184d6f64756c65040068012c4d6f64756c654572726f7200030044436f6e73756d657252656d61696e696e670004002c4e6f50726f76696465727300050040546f6f4d616e79436f6e73756d65727300060014546f6b656e04006c0128546f6b656e4572726f720007002841726974686d65746963040070013c41726974686d657469634572726f72000800345472616e73616374696f6e616c04007401485472616e73616374696f6e616c4572726f7200090024457868617573746564000a0028436f7272757074696f6e000b002c556e617661696c61626c65000c0038526f6f744e6f74416c6c6f776564000d000068082873705f72756e74696d652c4d6f64756c654572726f720000080114696e64657808010875380001146572726f7244018c5b75383b204d41585f4d4f44554c455f4552524f525f454e434f4445445f53495a455d00006c082873705f72756e74696d6528546f6b656e4572726f720001284046756e6473556e617661696c61626c65000000304f6e6c7950726f76696465720001003042656c6f774d696e696d756d0002003043616e6e6f7443726561746500030030556e6b6e6f776e41737365740004001846726f7a656e0005002c556e737570706f727465640006004043616e6e6f74437265617465486f6c64000700344e6f74457870656e6461626c650008001c426c6f636b65640009000070083473705f61726974686d657469633c41726974686d657469634572726f7200010c24556e646572666c6f77000000204f766572666c6f77000100384469766973696f6e42795a65726f0002000074082873705f72756e74696d65485472616e73616374696f6e616c4572726f72000108304c696d6974526561636865640000001c4e6f4c6179657200010000780c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c6574144576656e74040454000110204d696772617465640c010c746f7010010c7533320001146368696c6410010c75333200011c636f6d707574657c01404d6967726174696f6e436f6d707574650000083901476976656e206e756d626572206f66206028746f702c206368696c642960206b6579732077657265206d6967726174656420726573706563746976656c792c20776974682074686520676976656e2860636f6d70757465602e1c536c617368656408010c77686f000130543a3a4163636f756e744964000118616d6f756e7418013042616c616e63654f663c543e000104b4536f6d65206163636f756e7420676f7420736c61736865642062792074686520676976656e20616d6f756e742e544175746f4d6967726174696f6e46696e697368656400020484546865206175746f206d6967726174696f6e207461736b2066696e69736865642e1848616c7465640401146572726f728001204572726f723c543e000304ec4d6967726174696f6e20676f742068616c7465642064756520746f20616e206572726f72206f72206d6973732d636f6e66696775726174696f6e2e0470496e6e6572206576656e7473206f6620746869732070616c6c65742e7c0c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c6574404d6967726174696f6e436f6d70757465000108185369676e6564000000104175746f00010000800c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c6574144572726f720404540001183c4d61785369676e65644c696d697473000004804d6178207369676e6564206c696d697473206e6f74207265737065637465642e284b6579546f6f4c6f6e6700011cb441206b657920776173206c6f6e676572207468616e2074686520636f6e66696775726564206d6178696d756d2e00110154686973206d65616e73207468617420746865206d6967726174696f6e2068616c746564206174207468652063757272656e74205b6050726f6772657373605d20616e64010163616e20626520726573756d656420776974682061206c6172676572205b6063726174653a3a436f6e6669673a3a4d61784b65794c656e605d2076616c75652e21015265747279696e672077697468207468652073616d65205b6063726174653a3a436f6e6669673a3a4d61784b65794c656e605d2076616c75652077696c6c206e6f7420776f726b2e45015468652076616c75652073686f756c64206f6e6c7920626520696e6372656173656420746f2061766f696420612073746f72616765206d6967726174696f6e20666f72207468652063757272656e746c799073746f726564205b6063726174653a3a50726f67726573733a3a4c6173744b6579605d2e384e6f74456e6f75676846756e6473000204947375626d697474657220646f6573206e6f74206861766520656e6f7567682066756e64732e284261645769746e65737300030468426164207769746e65737320646174612070726f76696465642e645369676e65644d6967726174696f6e4e6f74416c6c6f77656400040425015369676e6564206d6967726174696f6e206973206e6f7420616c6c6f776564206265636175736520746865206d6178696d756d206c696d6974206973206e6f7420736574207965742e304261644368696c64526f6f7400050460426164206368696c6420726f6f742070726f76696465642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e840c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c6574144576656e7404045400011c6056616c69646174696f6e46756e6374696f6e53746f726564000004d05468652076616c69646174696f6e2066756e6374696f6e20686173206265656e207363686564756c656420746f206170706c792e6456616c69646174696f6e46756e6374696f6e4170706c69656404015472656c61795f636861696e5f626c6f636b5f6e756d10015452656c6179436861696e426c6f636b4e756d62657200010445015468652076616c69646174696f6e2066756e6374696f6e20776173206170706c696564206173206f662074686520636f6e7461696e65642072656c617920636861696e20626c6f636b206e756d6265722e6c56616c69646174696f6e46756e6374696f6e446973636172646564000204b05468652072656c61792d636861696e2061626f727465642074686520757067726164652070726f636573732e4455706772616465417574686f72697a6564040124636f64655f6861736830011c543a3a486173680003047c416e207570677261646520686173206265656e20617574686f72697a65642e60446f776e776172644d657373616765735265636569766564040114636f756e7410010c7533320004040101536f6d6520646f776e77617264206d657373616765732068617665206265656e20726563656976656420616e642077696c6c2062652070726f6365737365642e64446f776e776172644d6573736167657350726f63657373656408012c7765696768745f75736564240118576569676874000120646d715f6865616430014472656c61795f636861696e3a3a48617368000504e0446f776e77617264206d6573736167657320776572652070726f636573736564207573696e672074686520676976656e207765696768742e445570776172644d65737361676553656e740401306d6573736167655f6861736888013c4f7074696f6e3c58636d486173683e000604b8416e20757077617264206d657373616765207761732073656e7420746f207468652072656c617920636861696e2e047c54686520604576656e746020656e756d206f6620746869732070616c6c65748804184f7074696f6e04045401040108104e6f6e6500000010536f6d6504000400000100008c0c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c6574144576656e7404045400011844496e76756c6e657261626c654164646564040130696e76756c6e657261626c65000130543a3a4163636f756e7449640000004c496e76756c6e657261626c6552656d6f766564040130696e76756c6e657261626c65000130543a3a4163636f756e7449640001003c4c6963656e73654f627461696e65640801286163636f756e745f6964000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e0002003c4c6963656e736552656c65617365640801286163636f756e745f6964000130543a3a4163636f756e7449640001406465706f7369745f72657475726e656418013042616c616e63654f663c543e0003003843616e64696461746541646465640401286163636f756e745f6964000130543a3a4163636f756e7449640004004043616e64696461746552656d6f7665640401286163636f756e745f6964000130543a3a4163636f756e744964000500047c54686520604576656e746020656e756d206f6620746869732070616c6c6574900c3870616c6c65745f73657373696f6e1870616c6c6574144576656e74000104284e657753657373696f6e04013473657373696f6e5f696e64657810013053657373696f6e496e64657800000839014e65772073657373696f6e206861732068617070656e65642e204e6f746520746861742074686520617267756d656e74206973207468652073657373696f6e20696e6465782c206e6f74207468659c626c6f636b206e756d626572206173207468652074797065206d6967687420737567676573742e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574940c3c70616c6c65745f62616c616e6365731870616c6c6574144576656e740804540004490001541c456e646f77656408011c6163636f756e74000130543a3a4163636f756e744964000130667265655f62616c616e6365180128543a3a42616c616e6365000004b8416e206163636f756e74207761732063726561746564207769746820736f6d6520667265652062616c616e63652e20447573744c6f737408011c6163636f756e74000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650001083d01416e206163636f756e74207761732072656d6f7665642077686f73652062616c616e636520776173206e6f6e2d7a65726f206275742062656c6f77204578697374656e7469616c4465706f7369742c78726573756c74696e6720696e20616e206f75747269676874206c6f73732e205472616e736665720c011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650002044c5472616e73666572207375636365656465642e2842616c616e636553657408010c77686f000130543a3a4163636f756e74496400011066726565180128543a3a42616c616e636500030468412062616c616e6365207761732073657420627920726f6f742e20526573657276656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000404e0536f6d652062616c616e63652077617320726573657276656420286d6f7665642066726f6d206672656520746f207265736572766564292e28556e726573657276656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000504e8536f6d652062616c616e63652077617320756e726573657276656420286d6f7665642066726f6d20726573657276656420746f2066726565292e4852657365727665526570617472696174656410011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500014864657374696e6174696f6e5f7374617475739801185374617475730006084d01536f6d652062616c616e636520776173206d6f7665642066726f6d207468652072657365727665206f6620746865206669727374206163636f756e7420746f20746865207365636f6e64206163636f756e742ed846696e616c20617267756d656e7420696e64696361746573207468652064657374696e6174696f6e2062616c616e636520747970652e1c4465706f73697408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000704d8536f6d6520616d6f756e7420776173206465706f73697465642028652e672e20666f72207472616e73616374696f6e2066656573292e20576974686472617708010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650008041d01536f6d6520616d6f756e74207761732077697468647261776e2066726f6d20746865206163636f756e742028652e672e20666f72207472616e73616374696f6e2066656573292e1c536c617368656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650009040101536f6d6520616d6f756e74207761732072656d6f7665642066726f6d20746865206163636f756e742028652e672e20666f72206d69736265686176696f72292e184d696e74656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000a049c536f6d6520616d6f756e7420776173206d696e74656420696e746f20616e206163636f756e742e184275726e656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000b049c536f6d6520616d6f756e7420776173206275726e65642066726f6d20616e206163636f756e742e2453757370656e64656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000c041501536f6d6520616d6f756e74207761732073757370656e6465642066726f6d20616e206163636f756e74202869742063616e20626520726573746f726564206c61746572292e20526573746f72656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000d04a4536f6d6520616d6f756e742077617320726573746f72656420696e746f20616e206163636f756e742e20557067726164656404010c77686f000130543a3a4163636f756e744964000e0460416e206163636f756e74207761732075706772616465642e18497373756564040118616d6f756e74180128543a3a42616c616e6365000f042d01546f74616c2069737375616e63652077617320696e637265617365642062792060616d6f756e74602c206372656174696e6720612063726564697420746f2062652062616c616e6365642e2452657363696e646564040118616d6f756e74180128543a3a42616c616e63650010042501546f74616c2069737375616e636520776173206465637265617365642062792060616d6f756e74602c206372656174696e672061206465627420746f2062652062616c616e6365642e184c6f636b656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500110460536f6d652062616c616e636520776173206c6f636b65642e20556e6c6f636b656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500120468536f6d652062616c616e63652077617320756e6c6f636b65642e1846726f7a656e08010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500130460536f6d652062616c616e6365207761732066726f7a656e2e1854686177656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500140460536f6d652062616c616e636520776173207468617765642e047c54686520604576656e746020656e756d206f6620746869732070616c6c65749814346672616d655f737570706f72741874726169747318746f6b656e73106d6973633442616c616e63655374617475730001081046726565000000205265736572766564000100009c0c6870616c6c65745f7472616e73616374696f6e5f7061796d656e741870616c6c6574144576656e74040454000104485472616e73616374696f6e466565506169640c010c77686f000130543a3a4163636f756e74496400012861637475616c5f66656518013042616c616e63654f663c543e00010c74697018013042616c616e63654f663c543e000008590141207472616e73616374696f6e20666565206061637475616c5f666565602c206f662077686963682060746970602077617320616464656420746f20746865206d696e696d756d20696e636c7573696f6e206665652c5c686173206265656e2070616964206279206077686f602e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574a00c3c70616c6c65745f74726561737572791870616c6c6574144576656e740804540004490001242050726f706f73656404013870726f706f73616c5f696e64657810013450726f706f73616c496e646578000004344e65772070726f706f73616c2e205370656e64696e670401406275646765745f72656d61696e696e6718013c42616c616e63654f663c542c20493e000104e45765206861766520656e6465642061207370656e6420706572696f6420616e642077696c6c206e6f7720616c6c6f636174652066756e64732e1c417761726465640c013870726f706f73616c5f696e64657810013450726f706f73616c496e646578000114617761726418013c42616c616e63654f663c542c20493e00011c6163636f756e74000130543a3a4163636f756e7449640002047c536f6d652066756e64732068617665206265656e20616c6c6f63617465642e2052656a656374656408013870726f706f73616c5f696e64657810013450726f706f73616c496e64657800011c736c617368656418013c42616c616e63654f663c542c20493e000304b0412070726f706f73616c207761732072656a65637465643b2066756e6473207765726520736c61736865642e144275726e7404012c6275726e745f66756e647318013c42616c616e63654f663c542c20493e00040488536f6d65206f66206f75722066756e64732068617665206265656e206275726e742e20526f6c6c6f766572040140726f6c6c6f7665725f62616c616e636518013c42616c616e63654f663c542c20493e0005042d015370656e64696e67206861732066696e69736865643b20746869732069732074686520616d6f756e74207468617420726f6c6c73206f76657220756e74696c206e657874207370656e642e1c4465706f73697404011476616c756518013c42616c616e63654f663c542c20493e0006047c536f6d652066756e64732068617665206265656e206465706f73697465642e345370656e64417070726f7665640c013870726f706f73616c5f696e64657810013450726f706f73616c496e646578000118616d6f756e7418013c42616c616e63654f663c542c20493e00012c62656e6566696369617279000130543a3a4163636f756e7449640007049c41206e6577207370656e642070726f706f73616c20686173206265656e20617070726f7665642e3c55706461746564496e61637469766508012c726561637469766174656418013c42616c616e63654f663c542c20493e00012c646561637469766174656418013c42616c616e63654f663c542c20493e000804cc54686520696e6163746976652066756e6473206f66207468652070616c6c65742068617665206265656e20757064617465642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574a40c2c70616c6c65745f7375646f1870616c6c6574144576656e7404045400010c14537564696404012c7375646f5f726573756c74a801384469737061746368526573756c740000048841207375646f206a75737420746f6f6b20706c6163652e205c5b726573756c745c5d284b65794368616e6765640401286f6c645f7375646f6572b001504f7074696f6e3c543a3a4163636f756e7449643e0001043901546865205c5b7375646f65725c5d206a757374207377697463686564206964656e746974793b20746865206f6c64206b657920697320737570706c696564206966206f6e6520657869737465642e285375646f4173446f6e6504012c7375646f5f726573756c74a801384469737061746368526573756c740002048841207375646f206a75737420746f6f6b20706c6163652e205c5b726573756c745c5d047c54686520604576656e746020656e756d206f6620746869732070616c6c6574a80418526573756c7408045401ac044501640108084f6b0400ac000000000c4572720400640000010000ac0000040000b004184f7074696f6e04045401000108104e6f6e6500000010536f6d650400000000010000b40c306f726d6c5f76657374696e67186d6f64756c65144576656e7404045400010c5056657374696e675363686564756c6541646465640c011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e74496400014076657374696e675f7363686564756c65b8015056657374696e675363686564756c654f663c543e0000046c4164646564206e65772076657374696e67207363686564756c652e1c436c61696d656408010c77686f000130543a3a4163636f756e744964000118616d6f756e7418013042616c616e63654f663c543e00010440436c61696d65642076657374696e672e5c56657374696e675363686564756c65735570646174656404010c77686f000130543a3a4163636f756e74496400020468557064617465642076657374696e67207363686564756c65732e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574b808306f726d6c5f76657374696e673c56657374696e675363686564756c65082c426c6f636b4e756d62657201101c42616c616e6365011800100114737461727410012c426c6f636b4e756d626572000118706572696f6410012c426c6f636b4e756d626572000130706572696f645f636f756e7410010c7533320001287065725f706572696f64bc011c42616c616e63650000bc0000061800c00c306f726d6c5f78746f6b656e73186d6f64756c65144576656e74040454000104585472616e736665727265644d756c746941737365747310011873656e646572000130543a3a4163636f756e744964000118617373657473c4012c4d756c746941737365747300010c666565cc01284d756c7469417373657400011064657374d401344d756c74694c6f636174696f6e000004885472616e7366657272656420604d756c74694173736574602077697468206665652e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574c4102c73746167696e675f78636d087633286d756c746961737365742c4d756c746941737365747300000400c8013c5665633c4d756c746941737365743e0000c8000002cc00cc102c73746167696e675f78636d087633286d756c74696173736574284d756c7469417373657400000801086964d0011c4173736574496400010c66756ef8012c46756e676962696c6974790000d0102c73746167696e675f78636d087633286d756c746961737365741c4173736574496400010820436f6e63726574650400d401344d756c74694c6f636174696f6e00000020416273747261637404000401205b75383b2033325d00010000d4102c73746167696e675f78636d087633346d756c74696c6f636174696f6e344d756c74694c6f636174696f6e000008011c706172656e74730801087538000120696e746572696f72d801244a756e6374696f6e730000d8102c73746167696e675f78636d087633246a756e6374696f6e73244a756e6374696f6e7300012410486572650000000858310400dc01204a756e6374696f6e0001000858320800dc01204a756e6374696f6e0000dc01204a756e6374696f6e0002000858330c00dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0003000858341000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0004000858351400dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0005000858361800dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0006000858371c00dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0007000858382000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e00080000dc102c73746167696e675f78636d087633206a756e6374696f6e204a756e6374696f6e0001282450617261636861696e0400e0010c7533320000002c4163636f756e744964333208011c6e6574776f726be401444f7074696f6e3c4e6574776f726b49643e00010869640401205b75383b2033325d000100384163636f756e74496e646578363408011c6e6574776f726be401444f7074696f6e3c4e6574776f726b49643e000114696e64657828010c753634000200304163636f756e744b6579323008011c6e6574776f726be401444f7074696f6e3c4e6574776f726b49643e00010c6b6579ec01205b75383b2032305d0003003850616c6c6574496e7374616e6365040008010875380004003047656e6572616c496e6465780400bc0110753132380005002847656e6572616c4b65790801186c656e6774680801087538000110646174610401205b75383b2033325d000600244f6e6c794368696c6400070024506c7572616c6974790801086964f00118426f6479496400011070617274f40120426f6479506172740008003c476c6f62616c436f6e73656e7375730400e801244e6574776f726b496400090000e00000061000e404184f7074696f6e04045401e80108104e6f6e6500000010536f6d650400e80000010000e8102c73746167696e675f78636d087633206a756e6374696f6e244e6574776f726b496400012824427947656e6573697304000401205b75383b2033325d000000184279466f726b080130626c6f636b5f6e756d6265722c010c753634000128626c6f636b5f686173680401205b75383b2033325d00010020506f6c6b61646f74000200184b7573616d610003001c57657374656e6400040018526f636f636f00050018576f636f636f00060020457468657265756d040120636861696e5f696428010c7536340007002c426974636f696e436f72650008002c426974636f696e4361736800090000ec000003140000000800f0102c73746167696e675f78636d087633206a756e6374696f6e18426f6479496400012810556e69740000001c4d6f6e696b6572040044011c5b75383b20345d00010014496e6465780400e0010c7533320002002445786563757469766500030024546563686e6963616c0004002c4c656769736c6174697665000500204a7564696369616c0006001c446566656e73650007003841646d696e697374726174696f6e00080020547265617375727900090000f4102c73746167696e675f78636d087633206a756e6374696f6e20426f64795061727400011414566f6963650000001c4d656d62657273040114636f756e74e0010c753332000100204672616374696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c7533320002004441744c6561737450726f706f7274696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c753332000300484d6f72655468616e50726f706f7274696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c75333200040000f8102c73746167696e675f78636d087633286d756c746961737365742c46756e676962696c6974790001082046756e6769626c650400bc0110753132380000002c4e6f6e46756e6769626c650400fc01344173736574496e7374616e636500010000fc102c73746167696e675f78636d087633286d756c74696173736574344173736574496e7374616e636500011824556e646566696e656400000014496e6465780400bc01107531323800010018417272617934040044011c5b75383b20345d0002001841727261793804000101011c5b75383b20385d0003001c417272617931360400050101205b75383b2031365d0004001c4172726179333204000401205b75383b2033325d000500000101000003080000000800050100000310000000080009010c2c6f726d6c5f746f6b656e73186d6f64756c65144576656e740404540001441c456e646f7765640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000004b8416e206163636f756e74207761732063726561746564207769746820736f6d6520667265652062616c616e63652e20447573744c6f73740c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000108ec416e206163636f756e74207761732072656d6f7665642077686f73652062616c616e636520776173206e6f6e2d7a65726f206275742062656c6f77c84578697374656e7469616c4465706f7369742c20726573756c74696e6720696e20616e206f75747269676874206c6f73732e205472616e7366657210012c63757272656e63795f69640d010134543a3a43757272656e6379496400011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650002044c5472616e73666572207375636365656465642e2052657365727665640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000304e0536f6d652062616c616e63652077617320726573657276656420286d6f7665642066726f6d206672656520746f207265736572766564292e28556e72657365727665640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000404e8536f6d652062616c616e63652077617320756e726573657276656420286d6f7665642066726f6d20726573657276656420746f2066726565292e4852657365727665526570617472696174656414012c63757272656e63795f69640d010134543a3a43757272656e6379496400011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500011873746174757398013442616c616e6365537461747573000508f4536f6d652072657365727665642062616c616e63652077617320726570617472696174656420286d6f7665642066726f6d20726573657276656420746f44616e6f74686572206163636f756e74292e2842616c616e636553657410012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e74496400011066726565180128543a3a42616c616e63650001207265736572766564180128543a3a42616c616e636500060468412062616c616e6365207761732073657420627920726f6f742e40546f74616c49737375616e636553657408012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74180128543a3a42616c616e6365000704b854686520746f74616c2069737375616e6365206f6620616e2063757272656e637920686173206265656e207365742457697468647261776e0c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000804ec536f6d652062616c616e63657320776572652077697468647261776e2028652e672e2070617920666f72207472616e73616374696f6e20666565291c536c617368656410012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e74496400012c667265655f616d6f756e74180128543a3a42616c616e636500013c72657365727665645f616d6f756e74180128543a3a42616c616e6365000904d4536f6d652062616c616e636573207765726520736c61736865642028652e672e2064756520746f206d69732d6265686176696f7229244465706f73697465640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000a04984465706f736974656420736f6d652062616c616e636520696e746f20616e206163636f756e741c4c6f636b53657410011c6c6f636b5f6964010101384c6f636b4964656e74696669657200012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000b0454536f6d652066756e647320617265206c6f636b65642c4c6f636b52656d6f7665640c011c6c6f636b5f6964010101384c6f636b4964656e74696669657200012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000c047c536f6d65206c6f636b65642066756e6473207765726520756e6c6f636b6564184c6f636b65640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000d0474536f6d6520667265652062616c616e636520776173206c6f636b65642e20556e6c6f636b65640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000e0478536f6d65206c6f636b65642062616c616e6365207761732066726565642e1849737375656408012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74180128543a3a42616c616e6365000f002452657363696e64656408012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74180128543a3a42616c616e6365001000047c54686520604576656e746020656e756d206f6620746869732070616c6c65740d01085470616c6c65745f666f726569676e5f6173736574731c4173736574496400010838466f726569676e417373657449640400100138466f726569676e41737365744964000000344e6174697665417373657449640400110101384e617469766543757272656e6379000100001101085470616c6c65745f666f726569676e5f617373657473384e617469766543757272656e6379000108104865726500000018506172656e740001000015010c3c70616c6c65745f6964656e746974791870616c6c6574144576656e740404540001342c4964656e7469747953657404010c77686f000130543a3a4163636f756e744964000004ec41206e616d652077617320736574206f72207265736574202877686963682077696c6c2072656d6f766520616c6c206a756467656d656e7473292e3c4964656e74697479436c656172656408010c77686f000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000104cc41206e616d652077617320636c65617265642c20616e642074686520676976656e2062616c616e63652072657475726e65642e384964656e746974794b696c6c656408010c77686f000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000204c441206e616d65207761732072656d6f76656420616e642074686520676976656e2062616c616e636520736c61736865642e484964656e746974696573496e736572746564040118616d6f756e7410010c753332000304090141206e756d626572206f66206964656e74697469657320616e64206173736f63696174656420696e666f207765726520666f726369626c7920696e7365727465642e444964656e74697469657352656d6f766564040118616d6f756e7410010c753332000404150141206e756d626572206f66206964656e74697469657320616e6420616c6c206173736f63696174656420696e666f207765726520666f726369626c792072656d6f7665642e484a756467656d656e7452657175657374656408010c77686f000130543a3a4163636f756e74496400013c7265676973747261725f696e646578100138526567697374726172496e6465780005049c41206a756467656d656e74207761732061736b65642066726f6d2061207265676973747261722e504a756467656d656e74556e72657175657374656408010c77686f000130543a3a4163636f756e74496400013c7265676973747261725f696e646578100138526567697374726172496e6465780006048841206a756467656d656e74207265717565737420776173207265747261637465642e384a756467656d656e74476976656e080118746172676574000130543a3a4163636f756e74496400013c7265676973747261725f696e646578100138526567697374726172496e6465780007049441206a756467656d656e742077617320676976656e2062792061207265676973747261722e38526567697374726172416464656404013c7265676973747261725f696e646578100138526567697374726172496e646578000804584120726567697374726172207761732061646465642e405375624964656e7469747941646465640c010c737562000130543a3a4163636f756e7449640001106d61696e000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000904f441207375622d6964656e746974792077617320616464656420746f20616e206964656e7469747920616e6420746865206465706f73697420706169642e485375624964656e7469747952656d6f7665640c010c737562000130543a3a4163636f756e7449640001106d61696e000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000a04090141207375622d6964656e74697479207761732072656d6f7665642066726f6d20616e206964656e7469747920616e6420746865206465706f7369742066726565642e485375624964656e746974795265766f6b65640c010c737562000130543a3a4163636f756e7449640001106d61696e000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000b08190141207375622d6964656e746974792077617320636c65617265642c20616e642074686520676976656e206465706f7369742072657061747269617465642066726f6d20746865c86d61696e206964656e74697479206163636f756e7420746f20746865207375622d6964656e74697479206163636f756e742e545375624964656e746974696573496e736572746564040118616d6f756e7410010c753332000c04150141206e756d626572206f66206964656e746974696573207765726520666f726369626c7920757064617465642077697468206e6577207375622d6964656e7469746965732e047c54686520604576656e746020656e756d206f6620746869732070616c6c657419010c3c70616c6c65745f707265696d6167651870616c6c6574144576656e7404045400010c144e6f7465640401106861736830011c543a3a48617368000004684120707265696d61676520686173206265656e206e6f7465642e245265717565737465640401106861736830011c543a3a48617368000104784120707265696d61676520686173206265656e207265717565737465642e1c436c65617265640401106861736830011c543a3a486173680002046c4120707265696d616765206861732062656e20636c65617265642e047c54686520604576656e746020656e756d206f6620746869732070616c6c65741d010c4070616c6c65745f64656d6f63726163791870616c6c6574144576656e740404540001442050726f706f73656408013870726f706f73616c5f696e64657810012450726f70496e64657800011c6465706f73697418013042616c616e63654f663c543e000004bc41206d6f74696f6e20686173206265656e2070726f706f7365642062792061207075626c6963206163636f756e742e185461626c656408013870726f706f73616c5f696e64657810012450726f70496e64657800011c6465706f73697418013042616c616e63654f663c543e000104d841207075626c69632070726f706f73616c20686173206265656e207461626c656420666f72207265666572656e64756d20766f74652e3845787465726e616c5461626c656400020494416e2065787465726e616c2070726f706f73616c20686173206265656e207461626c65642e1c537461727465640801247265665f696e64657810013c5265666572656e64756d496e6465780001247468726573686f6c6421010134566f74655468726573686f6c640003045c41207265666572656e64756d2068617320626567756e2e185061737365640401247265665f696e64657810013c5265666572656e64756d496e646578000404ac412070726f706f73616c20686173206265656e20617070726f766564206279207265666572656e64756d2e244e6f745061737365640401247265665f696e64657810013c5265666572656e64756d496e646578000504ac412070726f706f73616c20686173206265656e2072656a6563746564206279207265666572656e64756d2e2443616e63656c6c65640401247265665f696e64657810013c5265666572656e64756d496e6465780006048041207265666572656e64756d20686173206265656e2063616e63656c6c65642e2444656c65676174656408010c77686f000130543a3a4163636f756e744964000118746172676574000130543a3a4163636f756e744964000704dc416e206163636f756e74206861732064656c65676174656420746865697220766f746520746f20616e6f74686572206163636f756e742e2c556e64656c65676174656404011c6163636f756e74000130543a3a4163636f756e744964000804e4416e206163636f756e74206861732063616e63656c6c656420612070726576696f75732064656c65676174696f6e206f7065726174696f6e2e185665746f65640c010c77686f000130543a3a4163636f756e74496400013470726f706f73616c5f6861736830011048323536000114756e74696c100144426c6f636b4e756d626572466f723c543e00090494416e2065787465726e616c2070726f706f73616c20686173206265656e207665746f65642e2c426c61636b6c697374656404013470726f706f73616c5f6861736830011048323536000a04c4412070726f706f73616c5f6861736820686173206265656e20626c61636b6c6973746564207065726d616e656e746c792e14566f7465640c0114766f746572000130543a3a4163636f756e7449640001247265665f696e64657810013c5265666572656e64756d496e646578000110766f7465250101644163636f756e74566f74653c42616c616e63654f663c543e3e000b0490416e206163636f756e742068617320766f74656420696e2061207265666572656e64756d205365636f6e6465640801207365636f6e646572000130543a3a4163636f756e74496400012870726f705f696e64657810012450726f70496e646578000c048c416e206163636f756e742068617320736563636f6e64656420612070726f706f73616c4050726f706f73616c43616e63656c656404012870726f705f696e64657810012450726f70496e646578000d0460412070726f706f73616c20676f742063616e63656c65642e2c4d657461646174615365740801146f776e65722d0101344d657461646174614f776e6572043c4d65746164617461206f776e65722e011068617368300130507265696d616765486173680438507265696d61676520686173682e0e04d44d6574616461746120666f7220612070726f706f73616c206f722061207265666572656e64756d20686173206265656e207365742e3c4d65746164617461436c65617265640801146f776e65722d0101344d657461646174614f776e6572043c4d65746164617461206f776e65722e011068617368300130507265696d616765486173680438507265696d61676520686173682e0f04e44d6574616461746120666f7220612070726f706f73616c206f722061207265666572656e64756d20686173206265656e20636c65617265642e4c4d657461646174615472616e736665727265640c0128707265765f6f776e65722d0101344d657461646174614f776e6572046050726576696f7573206d65746164617461206f776e65722e01146f776e65722d0101344d657461646174614f776e6572044c4e6577206d65746164617461206f776e65722e011068617368300130507265696d616765486173680438507265696d61676520686173682e1004ac4d6574616461746120686173206265656e207472616e7366657272656420746f206e6577206f776e65722e047c54686520604576656e746020656e756d206f6620746869732070616c6c657421010c4070616c6c65745f64656d6f637261637938766f74655f7468726573686f6c6434566f74655468726573686f6c6400010c5053757065724d616a6f72697479417070726f76650000005053757065724d616a6f72697479416761696e73740001003853696d706c654d616a6f726974790002000025010c4070616c6c65745f64656d6f637261637910766f74652c4163636f756e74566f7465041c42616c616e636501180108205374616e64617264080110766f746529010110566f746500011c62616c616e636518011c42616c616e63650000001453706c697408010c61796518011c42616c616e636500010c6e617918011c42616c616e63650001000029010c4070616c6c65745f64656d6f637261637910766f746510566f746500000400080000002d010c4070616c6c65745f64656d6f6372616379147479706573344d657461646174614f776e657200010c2045787465726e616c0000002050726f706f73616c040010012450726f70496e646578000100285265666572656e64756d040010013c5265666572656e64756d496e6465780002000031010c4470616c6c65745f636f6c6c6563746976651870616c6c6574144576656e7408045400044900011c2050726f706f73656410011c6163636f756e74000130543a3a4163636f756e74496400013870726f706f73616c5f696e64657810013450726f706f73616c496e64657800013470726f706f73616c5f6861736830011c543a3a486173680001247468726573686f6c6410012c4d656d626572436f756e74000008490141206d6f74696f6e2028676976656e20686173682920686173206265656e2070726f706f7365642028627920676976656e206163636f756e742920776974682061207468726573686f6c642028676976656e3c604d656d626572436f756e7460292e14566f74656414011c6163636f756e74000130543a3a4163636f756e74496400013470726f706f73616c5f6861736830011c543a3a48617368000114766f74656435010110626f6f6c00010c79657310012c4d656d626572436f756e740001086e6f10012c4d656d626572436f756e74000108050141206d6f74696f6e2028676976656e20686173682920686173206265656e20766f746564206f6e20627920676976656e206163636f756e742c206c656176696e671501612074616c6c79202879657320766f74657320616e64206e6f20766f74657320676976656e20726573706563746976656c7920617320604d656d626572436f756e7460292e20417070726f76656404013470726f706f73616c5f6861736830011c543a3a48617368000204c041206d6f74696f6e2077617320617070726f76656420627920746865207265717569726564207468726573686f6c642e2c446973617070726f76656404013470726f706f73616c5f6861736830011c543a3a48617368000304d041206d6f74696f6e20776173206e6f7420617070726f76656420627920746865207265717569726564207468726573686f6c642e20457865637574656408013470726f706f73616c5f6861736830011c543a3a48617368000118726573756c74a801384469737061746368526573756c74000404210141206d6f74696f6e207761732065786563757465643b20726573756c742077696c6c20626520604f6b602069662069742072657475726e656420776974686f7574206572726f722e384d656d626572457865637574656408013470726f706f73616c5f6861736830011c543a3a48617368000118726573756c74a801384469737061746368526573756c740005044901412073696e676c65206d656d6265722064696420736f6d6520616374696f6e3b20726573756c742077696c6c20626520604f6b602069662069742072657475726e656420776974686f7574206572726f722e18436c6f7365640c013470726f706f73616c5f6861736830011c543a3a4861736800010c79657310012c4d656d626572436f756e740001086e6f10012c4d656d626572436f756e740006045501412070726f706f73616c2077617320636c6f736564206265636175736520697473207468726573686f6c64207761732072656163686564206f7220616674657220697473206475726174696f6e207761732075702e047c54686520604576656e746020656e756d206f6620746869732070616c6c65743501000005000039010c4470616c6c65745f636f6c6c6563746976651870616c6c6574144576656e7408045400044900011c2050726f706f73656410011c6163636f756e74000130543a3a4163636f756e74496400013870726f706f73616c5f696e64657810013450726f706f73616c496e64657800013470726f706f73616c5f6861736830011c543a3a486173680001247468726573686f6c6410012c4d656d626572436f756e74000008490141206d6f74696f6e2028676976656e20686173682920686173206265656e2070726f706f7365642028627920676976656e206163636f756e742920776974682061207468726573686f6c642028676976656e3c604d656d626572436f756e7460292e14566f74656414011c6163636f756e74000130543a3a4163636f756e74496400013470726f706f73616c5f6861736830011c543a3a48617368000114766f74656435010110626f6f6c00010c79657310012c4d656d626572436f756e740001086e6f10012c4d656d626572436f756e74000108050141206d6f74696f6e2028676976656e20686173682920686173206265656e20766f746564206f6e20627920676976656e206163636f756e742c206c656176696e671501612074616c6c79202879657320766f74657320616e64206e6f20766f74657320676976656e20726573706563746976656c7920617320604d656d626572436f756e7460292e20417070726f76656404013470726f706f73616c5f6861736830011c543a3a48617368000204c041206d6f74696f6e2077617320617070726f76656420627920746865207265717569726564207468726573686f6c642e2c446973617070726f76656404013470726f706f73616c5f6861736830011c543a3a48617368000304d041206d6f74696f6e20776173206e6f7420617070726f76656420627920746865207265717569726564207468726573686f6c642e20457865637574656408013470726f706f73616c5f6861736830011c543a3a48617368000118726573756c74a801384469737061746368526573756c74000404210141206d6f74696f6e207761732065786563757465643b20726573756c742077696c6c20626520604f6b602069662069742072657475726e656420776974686f7574206572726f722e384d656d626572457865637574656408013470726f706f73616c5f6861736830011c543a3a48617368000118726573756c74a801384469737061746368526573756c740005044901412073696e676c65206d656d6265722064696420736f6d6520616374696f6e3b20726573756c742077696c6c20626520604f6b602069662069742072657475726e656420776974686f7574206572726f722e18436c6f7365640c013470726f706f73616c5f6861736830011c543a3a4861736800010c79657310012c4d656d626572436f756e740001086e6f10012c4d656d626572436f756e740006045501412070726f706f73616c2077617320636c6f736564206265636175736520697473207468726573686f6c64207761732072656163686564206f7220616674657220697473206475726174696f6e207761732075702e047c54686520604576656e746020656e756d206f6620746869732070616c6c65743d010c4470616c6c65745f6d656d626572736869701870616c6c6574144576656e740804540004490001182c4d656d6265724164646564000004e054686520676976656e206d656d626572207761732061646465643b2073656520746865207472616e73616374696f6e20666f722077686f2e344d656d62657252656d6f766564000104e854686520676976656e206d656d626572207761732072656d6f7665643b2073656520746865207472616e73616374696f6e20666f722077686f2e384d656d6265727353776170706564000204d854776f206d656d62657273207765726520737761707065643b2073656520746865207472616e73616374696f6e20666f722077686f2e304d656d6265727352657365740003041501546865206d656d62657273686970207761732072657365743b2073656520746865207472616e73616374696f6e20666f722077686f20746865206e6577207365742069732e284b65794368616e676564000404844f6e65206f6620746865206d656d6265727327206b657973206368616e6765642e1444756d6d790005046c5068616e746f6d206d656d6265722c206e6576657220757365642e047c54686520604576656e746020656e756d206f6620746869732070616c6c657441010c4470616c6c65745f6d656d626572736869701870616c6c6574144576656e740804540004490001182c4d656d6265724164646564000004e054686520676976656e206d656d626572207761732061646465643b2073656520746865207472616e73616374696f6e20666f722077686f2e344d656d62657252656d6f766564000104e854686520676976656e206d656d626572207761732072656d6f7665643b2073656520746865207472616e73616374696f6e20666f722077686f2e384d656d6265727353776170706564000204d854776f206d656d62657273207765726520737761707065643b2073656520746865207472616e73616374696f6e20666f722077686f2e304d656d6265727352657365740003041501546865206d656d62657273686970207761732072657365743b2073656520746865207472616e73616374696f6e20666f722077686f20746865206e6577207365742069732e284b65794368616e676564000404844f6e65206f6620746865206d656d6265727327206b657973206368616e6765642e1444756d6d790005046c5068616e746f6d206d656d6265722c206e6576657220757365642e047c54686520604576656e746020656e756d206f6620746869732070616c6c657445010c6070616c6c65745f72616e6b65645f636f6c6c6563746976651870616c6c6574144576656e740804540004490001102c4d656d626572416464656404010c77686f000130543a3a4163636f756e7449640000047841206d656d626572206077686f6020686173206265656e2061646465642e2c52616e6b4368616e67656408010c77686f000130543a3a4163636f756e74496400011072616e6b4901011052616e6b000104f4546865206d656d626572206077686f6073652072616e6b20686173206265656e206368616e67656420746f2074686520676976656e206072616e6b602e344d656d62657252656d6f76656408010c77686f000130543a3a4163636f756e74496400011072616e6b4901011052616e6b0002041901546865206d656d626572206077686f60206f6620676976656e206072616e6b6020686173206265656e2072656d6f7665642066726f6d2074686520636f6c6c6563746976652e14566f74656410010c77686f000130543a3a4163636f756e744964000110706f6c6c100144506f6c6c496e6465784f663c542c20493e000110766f74654d010128566f74655265636f726400011474616c6c795101013454616c6c794f663c542c20493e0003085501546865206d656d626572206077686f602068617320766f74656420666f72207468652060706f6c6c6020776974682074686520676976656e2060766f746560206c656164696e6720746f20616e2075706461746564206074616c6c79602e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574490100000504004d01086070616c6c65745f72616e6b65645f636f6c6c65637469766528566f74655265636f72640001080c4179650400100114566f7465730000000c4e61790400100114566f746573000100005101086070616c6c65745f72616e6b65645f636f6c6c6563746976651454616c6c790c045400044900044d00000c0124626172655f6179657310012c4d656d626572496e64657800011061796573100114566f7465730001106e617973100114566f746573000055010c4070616c6c65745f7265666572656e64611870616c6c6574144576656e74080454000449000140245375626d69747465640c0114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e0114747261636b4901013c547261636b49644f663c542c20493e04250154686520747261636b2028616e6420627920657874656e73696f6e2070726f706f73616c206469737061746368206f726967696e29206f662074686973207265666572656e64756d2e012070726f706f73616c5901014c426f756e64656443616c6c4f663c542c20493e04805468652070726f706f73616c20666f7220746865207265666572656e64756d2e00048041207265666572656e64756d20686173206265656e207375626d69747465642e544465636973696f6e4465706f736974506c616365640c0114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e010c77686f000130543a3a4163636f756e744964048c546865206163636f756e742077686f20706c6163656420746865206465706f7369742e0118616d6f756e7418013c42616c616e63654f663c542c20493e048454686520616d6f756e7420706c6163656420627920746865206163636f756e742e010494546865206465636973696f6e206465706f73697420686173206265656e20706c616365642e5c4465636973696f6e4465706f736974526566756e6465640c0114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e010c77686f000130543a3a4163636f756e744964048c546865206163636f756e742077686f20706c6163656420746865206465706f7369742e0118616d6f756e7418013c42616c616e63654f663c542c20493e048454686520616d6f756e7420706c6163656420627920746865206163636f756e742e02049c546865206465636973696f6e206465706f73697420686173206265656e20726566756e6465642e384465706f736974536c617368656408010c77686f000130543a3a4163636f756e744964048c546865206163636f756e742077686f20706c6163656420746865206465706f7369742e0118616d6f756e7418013c42616c616e63654f663c542c20493e048454686520616d6f756e7420706c6163656420627920746865206163636f756e742e03047041206465706f73697420686173206265656e20736c6173686165642e3c4465636973696f6e53746172746564100114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e0114747261636b4901013c547261636b49644f663c542c20493e04250154686520747261636b2028616e6420627920657874656e73696f6e2070726f706f73616c206469737061746368206f726967696e29206f662074686973207265666572656e64756d2e012070726f706f73616c5901014c426f756e64656443616c6c4f663c542c20493e04805468652070726f706f73616c20666f7220746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b85468652063757272656e742074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0404bc41207265666572656e64756d20686173206d6f76656420696e746f20746865206465636964696e672070686173652e38436f6e6669726d53746172746564040114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e050038436f6e6669726d41626f72746564040114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e060024436f6e6669726d6564080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b05468652066696e616c2074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0704210141207265666572656e64756d2068617320656e6465642069747320636f6e6669726d6174696f6e20706861736520616e6420697320726561647920666f7220617070726f76616c2e20417070726f766564040114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e08040d0141207265666572656e64756d20686173206265656e20617070726f76656420616e64206974732070726f706f73616c20686173206265656e207363686564756c65642e2052656a6563746564080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b05468652066696e616c2074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0904ac412070726f706f73616c20686173206265656e2072656a6563746564206279207265666572656e64756d2e2054696d65644f7574080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b05468652066696e616c2074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0a04d841207265666572656e64756d20686173206265656e2074696d6564206f757420776974686f7574206265696e6720646563696465642e2443616e63656c6c6564080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b05468652066696e616c2074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0b048041207265666572656e64756d20686173206265656e2063616e63656c6c65642e184b696c6c6564080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b05468652066696e616c2074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0c047441207265666572656e64756d20686173206265656e206b696c6c65642e645375626d697373696f6e4465706f736974526566756e6465640c0114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e010c77686f000130543a3a4163636f756e744964048c546865206163636f756e742077686f20706c6163656420746865206465706f7369742e0118616d6f756e7418013c42616c616e63654f663c542c20493e048454686520616d6f756e7420706c6163656420627920746865206163636f756e742e0d04a4546865207375626d697373696f6e206465706f73697420686173206265656e20726566756e6465642e2c4d65746164617461536574080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011068617368300130507265696d616765486173680438507265696d61676520686173682e0e049c4d6574616461746120666f722061207265666572656e64756d20686173206265656e207365742e3c4d65746164617461436c6561726564080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011068617368300130507265696d616765486173680438507265696d61676520686173682e0f04ac4d6574616461746120666f722061207265666572656e64756d20686173206265656e20636c65617265642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574590110346672616d655f737570706f72741874726169747324707265696d616765731c426f756e646564040454015d01010c184c6567616379040110686173683001104861736800000018496e6c696e650400b5050134426f756e646564496e6c696e65000100184c6f6f6b7570080110686173683001104861736800010c6c656e10010c753332000200005d0108306f70616c5f72756e74696d652c52756e74696d6543616c6c0001a01853797374656d0400610101ad0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c53797374656d2c2052756e74696d653e000000485374617465547269654d6967726174696f6e0400710101dd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5374617465547269654d6967726174696f6e2c2052756e74696d653e0001003c50617261636861696e53797374656d0400890101d10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50617261636861696e53797374656d2c2052756e74696d653e0014003450617261636861696e496e666f0400c10101c90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50617261636861696e496e666f2c2052756e74696d653e00150044436f6c6c61746f7253656c656374696f6e0400c50101d90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c436f6c6c61746f7253656c656374696f6e2c2052756e74696d653e0017001c53657373696f6e0400c90101b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c53657373696f6e2c2052756e74696d653e0018002042616c616e6365730400d90101b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c42616c616e6365732c2052756e74696d653e001e002454696d657374616d700400e90101b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c54696d657374616d702c2052756e74696d653e0020002054726561737572790400ed0101b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c54726561737572792c2052756e74696d653e002200105375646f0400f10101a50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5375646f2c2052756e74696d653e0023001c56657374696e670400f50101b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c56657374696e672c2052756e74696d653e0025001c58546f6b656e730400fd0101b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c58546f6b656e732c2052756e74696d653e00260018546f6b656e7304004d0201ad0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c546f6b656e732c2052756e74696d653e002700204964656e746974790400510201b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c4964656e746974792c2052756e74696d653e00280020507265696d6167650400190301b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c507265696d6167652c2052756e74696d653e0029002444656d6f637261637904001d0301b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c44656d6f63726163792c2052756e74696d653e002a001c436f756e63696c04002d0301b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c436f756e63696c2c2052756e74696d653e002b0048546563686e6963616c436f6d6d69747465650400310301dd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c546563686e6963616c436f6d6d69747465652c2052756e74696d653e002c0044436f756e63696c4d656d626572736869700400350301d90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c436f756e63696c4d656d626572736869702c2052756e74696d653e002d0070546563686e6963616c436f6d6d69747465654d656d626572736869700400390301050273656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c546563686e6963616c436f6d6d69747465654d656d626572736869702c2052756e74696d653e002e005046656c6c6f7773686970436f6c6c65637469766504003d0301e50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c46656c6c6f7773686970436f6c6c6563746976652c2052756e74696d653e002f004c46656c6c6f77736869705265666572656e64610400410301e10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c46656c6c6f77736869705265666572656e64612c2052756e74696d653e003000245363686564756c65720400710301b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5363686564756c65722c2052756e74696d653e0031002458636d70517565756504007d0301b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c58636d7051756575652c2052756e74696d653e0032002c506f6c6b61646f7458636d0400810301c10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c506f6c6b61646f7458636d2c2052756e74696d653e0033002843756d756c757358636d0400250401bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c43756d756c757358636d2c2052756e74696d653e00340020446d7051756575650400290401b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c446d7051756575652c2052756e74696d653e00350024496e666c6174696f6e04002d0401b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c496e666c6174696f6e2c2052756e74696d653e003c0018556e697175650400310401ad0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c556e697175652c2052756e74696d653e003d0034436f6e66696775726174696f6e04000d0501c90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c436f6e66696775726174696f6e2c2052756e74696d653e003f00245374727563747572650400290501b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5374727563747572652c2052756e74696d653e0046003041707050726f6d6f74696f6e04002d0501c50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c41707050726f6d6f74696f6e2c2052756e74696d653e00490034466f726569676e4173736574730400350501c90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c466f726569676e4173736574732c2052756e74696d653e0050000c45564d0400450501a10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c45564d2c2052756e74696d653e00640020457468657265756d0400610501b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c457468657265756d2c2052756e74696d653e0065004845766d436f6e747261637448656c706572730400890501dd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c45766d436f6e747261637448656c706572732c2052756e74696d653e0097003045766d4d6967726174696f6e0400910501c50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c45766d4d6967726174696f6e2c2052756e74696d653e0099002c4d61696e74656e616e63650400a50501c10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c4d61696e74656e616e63652c2052756e74696d653e009a001c5574696c6974790400a90501b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5574696c6974792c2052756e74696d653e009c0024546573745574696c730400b10501b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c546573745574696c732c2052756e74696d653e00ff000061010c306672616d655f73797374656d1870616c6c65741043616c6c0404540001201872656d61726b04011872656d61726b34011c5665633c75383e0000045c536565205b6050616c6c65743a3a72656d61726b605d2e387365745f686561705f706167657304011470616765732c010c7536340001047c536565205b6050616c6c65743a3a7365745f686561705f7061676573605d2e207365745f636f6465040110636f646534011c5665633c75383e00020464536565205b6050616c6c65743a3a7365745f636f6465605d2e5c7365745f636f64655f776974686f75745f636865636b73040110636f646534011c5665633c75383e000304a0536565205b6050616c6c65743a3a7365745f636f64655f776974686f75745f636865636b73605d2e2c7365745f73746f726167650401146974656d73650101345665633c4b657956616c75653e00040470536565205b6050616c6c65743a3a7365745f73746f72616765605d2e306b696c6c5f73746f726167650401106b6579736d0101205665633c4b65793e00050474536565205b6050616c6c65743a3a6b696c6c5f73746f72616765605d2e2c6b696c6c5f70726566697808011870726566697834010c4b657900011c7375626b65797310010c75333200060470536565205b6050616c6c65743a3a6b696c6c5f707265666978605d2e4472656d61726b5f776974685f6576656e7404011872656d61726b34011c5665633c75383e00070488536565205b6050616c6c65743a3a72656d61726b5f776974685f6576656e74605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e65010000026901006901000004083434006d01000002340071010c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c65741043616c6c04045400011858636f6e74726f6c5f6175746f5f6d6967726174696f6e0401306d617962655f636f6e6669677501015c4f7074696f6e3c4d6967726174696f6e4c696d6974733e0000049c536565205b6050616c6c65743a3a636f6e74726f6c5f6175746f5f6d6967726174696f6e605d2e40636f6e74696e75655f6d6967726174650c01186c696d6974737901013c4d6967726174696f6e4c696d69747300013c7265616c5f73697a655f757070657210010c7533320001307769746e6573735f7461736b7d0101404d6967726174696f6e5461736b3c543e00010484536565205b6050616c6c65743a3a636f6e74696e75655f6d696772617465605d2e486d6967726174655f637573746f6d5f746f700801106b6579736d0101305665633c5665633c75383e3e0001307769746e6573735f73697a6510010c7533320002048c536565205b6050616c6c65743a3a6d6967726174655f637573746f6d5f746f70605d2e506d6967726174655f637573746f6d5f6368696c640c0110726f6f7434011c5665633c75383e0001286368696c645f6b6579736d0101305665633c5665633c75383e3e000128746f74616c5f73697a6510010c75333200030494536565205b6050616c6c65743a3a6d6967726174655f637573746f6d5f6368696c64605d2e547365745f7369676e65645f6d61785f6c696d6974730401186c696d6974737901013c4d6967726174696f6e4c696d69747300040498536565205b6050616c6c65743a3a7365745f7369676e65645f6d61785f6c696d697473605d2e48666f7263655f7365745f70726f677265737308013070726f67726573735f746f708101013450726f67726573734f663c543e00013870726f67726573735f6368696c648101013450726f67726573734f663c543e0005048c536565205b6050616c6c65743a3a666f7263655f7365745f70726f6772657373605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e750104184f7074696f6e0404540179010108104e6f6e6500000010536f6d6504007901000001000079010c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c65743c4d6967726174696f6e4c696d697473000008011073697a6510010c7533320001106974656d10010c75333200007d010c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c6574344d6967726174696f6e5461736b040454000014013070726f67726573735f746f708101013450726f67726573734f663c543e00013870726f67726573735f6368696c648101013450726f67726573734f663c543e00011073697a6510010c753332000124746f705f6974656d7310010c75333200012c6368696c645f6974656d7310010c753332000081010c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c65742050726f677265737304244d61784b65794c656e00010c1c546f53746172740000001c4c6173744b6579040085010164426f756e6465645665633c75382c204d61784b65794c656e3e00010020436f6d706c6574650002000085010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e000089010c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c65741043616c6c0404540001104c7365745f76616c69646174696f6e5f64617461040110646174618d01015450617261636861696e496e686572656e744461746100000490536565205b6050616c6c65743a3a7365745f76616c69646174696f6e5f64617461605d2e607375646f5f73656e645f7570776172645f6d65737361676504011c6d6573736167653401345570776172644d657373616765000104a4536565205b6050616c6c65743a3a7375646f5f73656e645f7570776172645f6d657373616765605d2e44617574686f72697a655f75706772616465080124636f64655f6861736830011c543a3a48617368000134636865636b5f76657273696f6e35010110626f6f6c00020488536565205b6050616c6c65743a3a617574686f72697a655f75706772616465605d2e60656e6163745f617574686f72697a65645f75706772616465040110636f646534011c5665633c75383e000304a4536565205b6050616c6c65743a3a656e6163745f617574686f72697a65645f75706772616465605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e8d01089463756d756c75735f7072696d6974697665735f70617261636861696e5f696e686572656e745450617261636861696e496e686572656e7444617461000010013c76616c69646174696f6e5f646174619101015c50657273697374656456616c69646174696f6e4461746100014472656c61795f636861696e5f73746174659901015473705f747269653a3a53746f7261676550726f6f66000144646f776e776172645f6d65737361676573a101016c5665633c496e626f756e64446f776e776172644d6573736167653e00014c686f72697a6f6e74616c5f6d65737361676573a90101a442547265654d61703c5061726149642c205665633c496e626f756e6448726d704d6573736167653e3e000091010c4c706f6c6b61646f745f7072696d6974697665730876355c50657273697374656456616c69646174696f6e446174610804480130044e01100010012c706172656e745f6865616495010120486561644461746100014c72656c61795f706172656e745f6e756d6265721001044e00016472656c61795f706172656e745f73746f726167655f726f6f74300104480001306d61785f706f765f73697a6510010c753332000095010c74706f6c6b61646f745f70617261636861696e5f7072696d697469766573287072696d6974697665732048656164446174610000040034011c5665633c75383e000099010c1c73705f747269653473746f726167655f70726f6f663053746f7261676550726f6f660000040128747269655f6e6f6465739d01014442547265655365743c5665633c75383e3e00009d010420425472656553657404045401340004006d01000000a101000002a50100a5010860706f6c6b61646f745f636f72655f7072696d69746976657358496e626f756e64446f776e776172644d657373616765042c426c6f636b4e756d62657201100008011c73656e745f617410012c426c6f636b4e756d62657200010c6d736734013c446f776e776172644d6573736167650000a901042042547265654d617008044b01ad01045601b101000400b901000000ad010c74706f6c6b61646f745f70617261636861696e5f7072696d697469766573287072696d6974697665730849640000040010010c7533320000b101000002b50100b5010860706f6c6b61646f745f636f72655f7072696d69746976657348496e626f756e6448726d704d657373616765042c426c6f636b4e756d62657201100008011c73656e745f617410012c426c6f636b4e756d6265720001106461746134015073705f7374643a3a7665633a3a5665633c75383e0000b901000002bd0100bd0100000408ad01b10100c1010c3870617261636861696e5f696e666f1870616c6c65741043616c6c040454000100040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ec5010c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c65741043616c6c04045400011c406164645f696e76756c6e657261626c6504010c6e6577000130543a3a4163636f756e74496400000484536565205b6050616c6c65743a3a6164645f696e76756c6e657261626c65605d2e4c72656d6f76655f696e76756c6e657261626c6504010c77686f000130543a3a4163636f756e74496400010490536565205b6050616c6c65743a3a72656d6f76655f696e76756c6e657261626c65605d2e2c6765745f6c6963656e736500020470536565205b6050616c6c65743a3a6765745f6c6963656e7365605d2e1c6f6e626f61726400030460536565205b6050616c6c65743a3a6f6e626f617264605d2e206f6666626f61726400040464536565205b6050616c6c65743a3a6f6666626f617264605d2e3c72656c656173655f6c6963656e736500050480536565205b6050616c6c65743a3a72656c656173655f6c6963656e7365605d2e54666f7263655f72656c656173655f6c6963656e736504010c77686f000130543a3a4163636f756e74496400060498536565205b6050616c6c65743a3a666f7263655f72656c656173655f6c6963656e7365605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ec9010c3870616c6c65745f73657373696f6e1870616c6c65741043616c6c040454000108207365745f6b6579730801106b657973cd01011c543a3a4b65797300011470726f6f6634011c5665633c75383e00000464536565205b6050616c6c65743a3a7365745f6b657973605d2e2870757267655f6b6579730001046c536565205b6050616c6c65743a3a70757267655f6b657973605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ecd010c306f70616c5f72756e74696d653872756e74696d655f636f6d6d6f6e2c53657373696f6e4b657973000004011061757261d10101c43c41757261206173202463726174653a3a426f756e64546f52756e74696d654170705075626c69633e3a3a5075626c69630000d101104473705f636f6e73656e7375735f617572611c737232353531392c6170705f73723235353139185075626c696300000400d501013c737232353531393a3a5075626c69630000d5010c1c73705f636f72651c73723235353139185075626c6963000004000401205b75383b2033325d0000d9010c3c70616c6c65745f62616c616e6365731870616c6c65741043616c6c080454000449000124507472616e736665725f616c6c6f775f646561746808011064657374dd0101504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565bc0128543a3a42616c616e636500000494536565205b6050616c6c65743a3a7472616e736665725f616c6c6f775f6465617468605d2e587365745f62616c616e63655f646570726563617465640c010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0001206e65775f66726565bc0128543a3a42616c616e63650001306f6c645f7265736572766564bc0128543a3a42616c616e63650001049c536565205b6050616c6c65743a3a7365745f62616c616e63655f64657072656361746564605d2e38666f7263655f7472616e736665720c0118736f75726365dd0101504163636f756e7449644c6f6f6b75704f663c543e00011064657374dd0101504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565bc0128543a3a42616c616e63650002047c536565205b6050616c6c65743a3a666f7263655f7472616e73666572605d2e4c7472616e736665725f6b6565705f616c69766508011064657374dd0101504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565bc0128543a3a42616c616e636500030490536565205b6050616c6c65743a3a7472616e736665725f6b6565705f616c697665605d2e307472616e736665725f616c6c08011064657374dd0101504163636f756e7449644c6f6f6b75704f663c543e0001286b6565705f616c69766535010110626f6f6c00040474536565205b6050616c6c65743a3a7472616e736665725f616c6c605d2e3c666f7263655f756e7265736572766508010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e000118616d6f756e74180128543a3a42616c616e636500050480536565205b6050616c6c65743a3a666f7263655f756e72657365727665605d2e40757067726164655f6163636f756e747304010c77686fe50101445665633c543a3a4163636f756e7449643e00060484536565205b6050616c6c65743a3a757067726164655f6163636f756e7473605d2e207472616e7366657208011064657374dd0101504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565bc0128543a3a42616c616e636500070464536565205b6050616c6c65743a3a7472616e73666572605d2e44666f7263655f7365745f62616c616e636508010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0001206e65775f66726565bc0128543a3a42616c616e636500080488536565205b6050616c6c65743a3a666f7263655f7365745f62616c616e6365605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732edd010c2873705f72756e74696d65306d756c746961646472657373304d756c74694164647265737308244163636f756e7449640100304163636f756e74496e64657801ac011408496404000001244163636f756e74496400000014496e6465780400e10101304163636f756e74496e6465780001000c526177040034011c5665633c75383e0002002441646472657373333204000401205b75383b2033325d000300244164647265737332300400ec01205b75383b2032305d00040000e101000006ac00e5010000020000e9010c4070616c6c65745f74696d657374616d701870616c6c65741043616c6c0404540001040c73657404010c6e6f77280124543a3a4d6f6d656e7400000450536565205b6050616c6c65743a3a736574605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732eed010c3c70616c6c65745f74726561737572791870616c6c65741043616c6c0804540004490001143470726f706f73655f7370656e6408011476616c7565bc013c42616c616e63654f663c542c20493e00012c62656e6566696369617279dd0101504163636f756e7449644c6f6f6b75704f663c543e00000478536565205b6050616c6c65743a3a70726f706f73655f7370656e64605d2e3c72656a6563745f70726f706f73616c04012c70726f706f73616c5f6964e0013450726f706f73616c496e64657800010480536565205b6050616c6c65743a3a72656a6563745f70726f706f73616c605d2e40617070726f76655f70726f706f73616c04012c70726f706f73616c5f6964e0013450726f706f73616c496e64657800020484536565205b6050616c6c65743a3a617070726f76655f70726f706f73616c605d2e147370656e64080118616d6f756e74bc013c42616c616e63654f663c542c20493e00012c62656e6566696369617279dd0101504163636f756e7449644c6f6f6b75704f663c543e00030458536565205b6050616c6c65743a3a7370656e64605d2e3c72656d6f76655f617070726f76616c04012c70726f706f73616c5f6964e0013450726f706f73616c496e64657800040480536565205b6050616c6c65743a3a72656d6f76655f617070726f76616c605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ef1010c2c70616c6c65745f7375646f1870616c6c65741043616c6c040454000110107375646f04011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00000454536565205b6050616c6c65743a3a7375646f605d2e547375646f5f756e636865636b65645f77656967687408011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00011877656967687424011857656967687400010498536565205b6050616c6c65743a3a7375646f5f756e636865636b65645f776569676874605d2e1c7365745f6b657904010c6e6577dd0101504163636f756e7449644c6f6f6b75704f663c543e00020460536565205b6050616c6c65743a3a7365745f6b6579605d2e1c7375646f5f617308010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00030460536565205b6050616c6c65743a3a7375646f5f6173605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ef5010c306f726d6c5f76657374696e67186d6f64756c651043616c6c04045400011014636c61696d00000458536565205b6050616c6c65743a3a636c61696d605d2e3c7665737465645f7472616e7366657208011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650001207363686564756c65b8015056657374696e675363686564756c654f663c543e00010480536565205b6050616c6c65743a3a7665737465645f7472616e73666572605d2e607570646174655f76657374696e675f7363686564756c657308010c77686fdd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500014476657374696e675f7363686564756c6573f90101645665633c56657374696e675363686564756c654f663c543e3e000204a4536565205b6050616c6c65743a3a7570646174655f76657374696e675f7363686564756c6573605d2e24636c61696d5f666f7204011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500030468536565205b6050616c6c65743a3a636c61696d5f666f72605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ef901000002b800fd010c306f726d6c5f78746f6b656e73186d6f64756c651043616c6c040454000118207472616e7366657210012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74180128543a3a42616c616e6365000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d697400000464536565205b6050616c6c65743a3a7472616e73666572605d2e4c7472616e736665725f6d756c746961737365740c0114617373657425020160426f783c56657273696f6e65644d756c746941737365743e000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d697400010490536565205b6050616c6c65743a3a7472616e736665725f6d756c74696173736574605d2e447472616e736665725f776974685f66656514012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74180128543a3a42616c616e636500010c666565180128543a3a42616c616e6365000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d697400020488536565205b6050616c6c65743a3a7472616e736665725f776974685f666565605d2e707472616e736665725f6d756c746961737365745f776974685f666565100114617373657425020160426f783c56657273696f6e65644d756c746941737365743e00010c66656525020160426f783c56657273696f6e65644d756c746941737365743e000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d6974000304b4536565205b6050616c6c65743a3a7472616e736665725f6d756c746961737365745f776974685f666565605d2e607472616e736665725f6d756c746963757272656e6369657310012863757272656e63696573390201805665633c28543a3a43757272656e637949642c20543a3a42616c616e6365293e0001206665655f6974656d10010c753332000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d6974000404a4536565205b6050616c6c65743a3a7472616e736665725f6d756c746963757272656e63696573605d2e507472616e736665725f6d756c746961737365747310011861737365747341020164426f783c56657273696f6e65644d756c74694173736574733e0001206665655f6974656d10010c753332000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d697400050494536565205b6050616c6c65743a3a7472616e736665725f6d756c7469617373657473605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e0102082c73746167696e675f78636d5856657273696f6e65644d756c74694c6f636174696f6e00010808563204000502014476323a3a4d756c74694c6f636174696f6e0001000856330400d4014476333a3a4d756c74694c6f636174696f6e000300000502102c73746167696e675f78636d087632346d756c74696c6f636174696f6e344d756c74694c6f636174696f6e000008011c706172656e74730801087538000120696e746572696f72090201244a756e6374696f6e7300000902102c73746167696e675f78636d087632346d756c74696c6f636174696f6e244a756e6374696f6e73000124104865726500000008583104000d0201204a756e6374696f6e00010008583208000d0201204a756e6374696f6e00000d0201204a756e6374696f6e0002000858330c000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00030008583410000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00040008583514000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00050008583618000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e0006000858371c000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00070008583820000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e000800000d02102c73746167696e675f78636d087632206a756e6374696f6e204a756e6374696f6e0001242450617261636861696e0400e0010c7533320000002c4163636f756e744964333208011c6e6574776f726b110201244e6574776f726b496400010869640401205b75383b2033325d000100384163636f756e74496e646578363408011c6e6574776f726b110201244e6574776f726b4964000114696e64657828010c753634000200304163636f756e744b6579323008011c6e6574776f726b110201244e6574776f726b496400010c6b6579ec01205b75383b2032305d0003003850616c6c6574496e7374616e6365040008010875380004003047656e6572616c496e6465780400bc0110753132380005002847656e6572616c4b65790400150201805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e000600244f6e6c794368696c6400070024506c7572616c697479080108696419020118426f64794964000110706172741d020120426f6479506172740008000011020c2c73746167696e675f78636d087632244e6574776f726b49640001100c416e79000000144e616d65640400150201805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e00010020506f6c6b61646f74000200184b7573616d610003000015020c4c626f756e6465645f636f6c6c656374696f6e73407765616b5f626f756e6465645f766563385765616b426f756e64656456656308045401080453000004003401185665633c543e000019020c2c73746167696e675f78636d08763218426f6479496400012810556e6974000000144e616d65640400150201805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e00010014496e6465780400e0010c7533320002002445786563757469766500030024546563686e6963616c0004002c4c656769736c6174697665000500204a7564696369616c0006001c446566656e73650007003841646d696e697374726174696f6e000800205472656173757279000900001d020c2c73746167696e675f78636d08763220426f64795061727400011414566f6963650000001c4d656d62657273040114636f756e74e0010c753332000100204672616374696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c7533320002004441744c6561737450726f706f7274696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c753332000300484d6f72655468616e50726f706f7274696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c7533320004000021020c2c73746167696e675f78636d0876332c5765696768744c696d697400010824556e6c696d697465640000001c4c696d697465640400240118576569676874000100002502082c73746167696e675f78636d4c56657273696f6e65644d756c7469417373657400010808563204002902013876323a3a4d756c746941737365740001000856330400cc013876333a3a4d756c74694173736574000300002902102c73746167696e675f78636d087632286d756c74696173736574284d756c74694173736574000008010869642d02011c4173736574496400010c66756e3102012c46756e676962696c69747900002d02102c73746167696e675f78636d087632286d756c746961737365741c4173736574496400010820436f6e63726574650400050201344d756c74694c6f636174696f6e000000204162737472616374040034011c5665633c75383e000100003102102c73746167696e675f78636d087632286d756c746961737365742c46756e676962696c6974790001082046756e6769626c650400bc0110753132380000002c4e6f6e46756e6769626c650400350201344173736574496e7374616e6365000100003502102c73746167696e675f78636d087632286d756c74696173736574344173736574496e7374616e636500011c24556e646566696e656400000014496e6465780400bc01107531323800010018417272617934040044011c5b75383b20345d0002001841727261793804000101011c5b75383b20385d0003001c417272617931360400050101205b75383b2031365d0004001c4172726179333204000401205b75383b2033325d00050010426c6f62040034011c5665633c75383e0006000039020000023d02003d02000004080d0118004102082c73746167696e675f78636d5056657273696f6e65644d756c746941737365747300010808563204004502013c76323a3a4d756c74694173736574730001000856330400c4013c76333a3a4d756c7469417373657473000300004502102c73746167696e675f78636d087632286d756c746961737365742c4d756c7469417373657473000004004902013c5665633c4d756c746941737365743e000049020000022902004d020c2c6f726d6c5f746f6b656e73186d6f64756c651043616c6c040454000114207472616e736665720c011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74bc0128543a3a42616c616e636500000464536565205b6050616c6c65743a3a7472616e73666572605d2e307472616e736665725f616c6c0c011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500012c63757272656e63795f69640d010134543a3a43757272656e637949640001286b6565705f616c69766535010110626f6f6c00010474536565205b6050616c6c65743a3a7472616e736665725f616c6c605d2e4c7472616e736665725f6b6565705f616c6976650c011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74bc0128543a3a42616c616e636500020490536565205b6050616c6c65743a3a7472616e736665725f6b6565705f616c697665605d2e38666f7263655f7472616e73666572100118736f75726365dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74bc0128543a3a42616c616e63650003047c536565205b6050616c6c65743a3a666f7263655f7472616e73666572605d2e2c7365745f62616c616e636510010c77686fdd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500012c63757272656e63795f69640d010134543a3a43757272656e637949640001206e65775f66726565bc0128543a3a42616c616e63650001306e65775f7265736572766564bc0128543a3a42616c616e636500040470536565205b6050616c6c65743a3a7365745f62616c616e6365605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e51020c3c70616c6c65745f6964656e746974791870616c6c65741043616c6c040454000148346164645f72656769737472617204011c6163636f756e74dd0101504163636f756e7449644c6f6f6b75704f663c543e00000478536565205b6050616c6c65743a3a6164645f726567697374726172605d2e307365745f6964656e74697479040110696e666f550201a4426f783c4964656e74697479496e666f3c543a3a4d61784164646974696f6e616c4669656c64733e3e00010474536565205b6050616c6c65743a3a7365745f6964656e74697479605d2e207365745f7375627304011073756273dd0201645665633c28543a3a4163636f756e7449642c2044617461293e00020464536565205b6050616c6c65743a3a7365745f73756273605d2e38636c6561725f6964656e746974790003047c536565205b6050616c6c65743a3a636c6561725f6964656e74697479605d2e44726571756573745f6a756467656d656e740801247265675f696e646578e00138526567697374726172496e64657800011c6d61785f666565bc013042616c616e63654f663c543e00040488536565205b6050616c6c65743a3a726571756573745f6a756467656d656e74605d2e3863616e63656c5f726571756573740401247265675f696e646578100138526567697374726172496e6465780005047c536565205b6050616c6c65743a3a63616e63656c5f72657175657374605d2e1c7365745f666565080114696e646578e00138526567697374726172496e64657800010c666565bc013042616c616e63654f663c543e00060460536565205b6050616c6c65743a3a7365745f666565605d2e387365745f6163636f756e745f6964080114696e646578e00138526567697374726172496e64657800010c6e6577dd0101504163636f756e7449644c6f6f6b75704f663c543e0007047c536565205b6050616c6c65743a3a7365745f6163636f756e745f6964605d2e287365745f6669656c6473080114696e646578e00138526567697374726172496e6465780001186669656c6473e50201384964656e746974794669656c64730008046c536565205b6050616c6c65743a3a7365745f6669656c6473605d2e4470726f766964655f6a756467656d656e741001247265675f696e646578e00138526567697374726172496e646578000118746172676574dd0101504163636f756e7449644c6f6f6b75704f663c543e0001246a756467656d656e74ed02015c4a756467656d656e743c42616c616e63654f663c543e3e0001206964656e7469747930011c543a3a4861736800090488536565205b6050616c6c65743a3a70726f766964655f6a756467656d656e74605d2e346b696c6c5f6964656e74697479040118746172676574dd0101504163636f756e7449644c6f6f6b75704f663c543e000a0478536565205b6050616c6c65743a3a6b696c6c5f6964656e74697479605d2e1c6164645f73756208010c737562dd0101504163636f756e7449644c6f6f6b75704f663c543e000110646174616102011044617461000b0460536565205b6050616c6c65743a3a6164645f737562605d2e2872656e616d655f73756208010c737562dd0101504163636f756e7449644c6f6f6b75704f663c543e000110646174616102011044617461000c046c536565205b6050616c6c65743a3a72656e616d655f737562605d2e2872656d6f76655f73756204010c737562dd0101504163636f756e7449644c6f6f6b75704f663c543e000d046c536565205b6050616c6c65743a3a72656d6f76655f737562605d2e20717569745f737562000e0464536565205b6050616c6c65743a3a717569745f737562605d2e5c666f7263655f696e736572745f6964656e7469746965730401286964656e746974696573f10201985665633c28543a3a4163636f756e7449642c20526567697374726174696f6e4f663c543e293e000f04a0536565205b6050616c6c65743a3a666f7263655f696e736572745f6964656e746974696573605d2e5c666f7263655f72656d6f76655f6964656e7469746965730401286964656e746974696573e50101445665633c543a3a4163636f756e7449643e001004a0536565205b6050616c6c65743a3a666f7263655f72656d6f76655f6964656e746974696573605d2e38666f7263655f7365745f7375627304011073756273090301785665633c5375624163636f756e747342794163636f756e7449643c543e3e0011047c536565205b6050616c6c65743a3a666f7263655f7365745f73756273605d2e04704964656e746974792070616c6c6574206465636c61726174696f6e2e55020c3c70616c6c65745f6964656e74697479147479706573304964656e74697479496e666f04284669656c644c696d697400002401286164646974696f6e616c59020190426f756e6465645665633c28446174612c2044617461292c204669656c644c696d69743e00011c646973706c617961020110446174610001146c6567616c610201104461746100010c776562610201104461746100011072696f746102011044617461000114656d61696c610201104461746100013c7067705f66696e6765727072696e74d90201404f7074696f6e3c5b75383b2032305d3e000114696d616765610201104461746100011c747769747465726102011044617461000059020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454015d02045300000400d50201185665633c543e00005d0200000408610261020061020c3c70616c6c65745f6964656e746974791474797065731044617461000198104e6f6e650000001052617730040065020000010010526177310400690200000200105261773204006d0200000300105261773304007102000004001052617734040044000005001052617735040075020000060010526177360400790200000700105261773704007d02000008001052617738040001010000090010526177390400810200000a001452617731300400850200000b001452617731310400890200000c0014526177313204008d0200000d001452617731330400910200000e001452617731340400950200000f001452617731350400990200001000145261773136040005010000110014526177313704009d02000012001452617731380400a102000013001452617731390400a502000014001452617732300400ec000015001452617732310400a902000016001452617732320400ad02000017001452617732330400b102000018001452617732340400b502000019001452617732350400b90200001a001452617732360400bd0200001b001452617732370400c10200001c001452617732380400c50200001d001452617732390400c90200001e001452617733300400cd0200001f001452617733310400d10200002000145261773332040004000021002c426c616b6554776f323536040004000022001853686132353604000400002300244b656363616b323536040004000024002c53686154687265653235360400040000250000650200000300000000080069020000030100000008006d020000030200000008007102000003030000000800750200000305000000080079020000030600000008007d02000003070000000800810200000309000000080085020000030a000000080089020000030b00000008008d020000030c000000080091020000030d000000080095020000030e000000080099020000030f00000008009d02000003110000000800a102000003120000000800a502000003130000000800a902000003150000000800ad02000003160000000800b102000003170000000800b502000003180000000800b902000003190000000800bd020000031a0000000800c1020000031b0000000800c5020000031c0000000800c9020000031d0000000800cd020000031e0000000800d1020000031f0000000800d5020000025d0200d90204184f7074696f6e04045401ec0108104e6f6e6500000010536f6d650400ec0000010000dd02000002e10200e1020000040800610200e5020c3c70616c6c65745f6964656e7469747914747970657320426974466c61677304045401e9020004002c01344964656e746974794669656c640000e9020c3c70616c6c65745f6964656e74697479147479706573344964656e746974794669656c640001201c446973706c6179000100144c6567616c0002000c5765620004001052696f7400080014456d61696c0010003850677046696e6765727072696e7400200014496d6167650040001c5477697474657200800000ed020c3c70616c6c65745f6964656e74697479147479706573244a756467656d656e74041c42616c616e63650118011c1c556e6b6e6f776e0000001c46656550616964040018011c42616c616e636500010028526561736f6e61626c65000200244b6e6f776e476f6f64000300244f75744f6644617465000400284c6f775175616c697479000500244572726f6e656f757300060000f102000002f50200f5020000040800f90200f9020c3c70616c6c65745f6964656e7469747914747970657330526567697374726174696f6e0c1c42616c616e63650118344d61784a756467656d656e7473004c4d61784164646974696f6e616c4669656c647300000c01286a756467656d656e7473fd0201fc426f756e6465645665633c28526567697374726172496e6465782c204a756467656d656e743c42616c616e63653e292c204d61784a756467656d656e74733e00011c6465706f73697418011c42616c616e6365000110696e666f550201844964656e74697479496e666f3c4d61784164646974696f6e616c4669656c64733e0000fd020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454010103045300000400050301185665633c543e000001030000040810ed0200050300000201030009030000020d03000d0300000408001103001103000004081815030015030c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401e102045300000400dd0201185665633c543e000019030c3c70616c6c65745f707265696d6167651870616c6c65741043616c6c040454000110346e6f74655f707265696d616765040114627974657334011c5665633c75383e00000478536565205b6050616c6c65743a3a6e6f74655f707265696d616765605d2e3c756e6e6f74655f707265696d6167650401106861736830011c543a3a4861736800010480536565205b6050616c6c65743a3a756e6e6f74655f707265696d616765605d2e40726571756573745f707265696d6167650401106861736830011c543a3a4861736800020484536565205b6050616c6c65743a3a726571756573745f707265696d616765605d2e48756e726571756573745f707265696d6167650401106861736830011c543a3a486173680003048c536565205b6050616c6c65743a3a756e726571756573745f707265696d616765605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e1d030c4070616c6c65745f64656d6f63726163791870616c6c65741043616c6c04045400014c1c70726f706f736508012070726f706f73616c59010140426f756e64656443616c6c4f663c543e00011476616c7565bc013042616c616e63654f663c543e00000460536565205b6050616c6c65743a3a70726f706f7365605d2e187365636f6e6404012070726f706f73616ce0012450726f70496e6465780001045c536565205b6050616c6c65743a3a7365636f6e64605d2e10766f74650801247265665f696e646578e0013c5265666572656e64756d496e646578000110766f7465250101644163636f756e74566f74653c42616c616e63654f663c543e3e00020454536565205b6050616c6c65743a3a766f7465605d2e40656d657267656e63795f63616e63656c0401247265665f696e64657810013c5265666572656e64756d496e64657800030484536565205b6050616c6c65743a3a656d657267656e63795f63616e63656c605d2e4065787465726e616c5f70726f706f736504012070726f706f73616c59010140426f756e64656443616c6c4f663c543e00040484536565205b6050616c6c65743a3a65787465726e616c5f70726f706f7365605d2e6465787465726e616c5f70726f706f73655f6d616a6f7269747904012070726f706f73616c59010140426f756e64656443616c6c4f663c543e000504a8536565205b6050616c6c65743a3a65787465726e616c5f70726f706f73655f6d616a6f72697479605d2e6065787465726e616c5f70726f706f73655f64656661756c7404012070726f706f73616c59010140426f756e64656443616c6c4f663c543e000604a4536565205b6050616c6c65743a3a65787465726e616c5f70726f706f73655f64656661756c74605d2e28666173745f747261636b0c013470726f706f73616c5f6861736830011048323536000134766f74696e675f706572696f64100144426c6f636b4e756d626572466f723c543e00011464656c6179100144426c6f636b4e756d626572466f723c543e0007046c536565205b6050616c6c65743a3a666173745f747261636b605d2e347665746f5f65787465726e616c04013470726f706f73616c5f686173683001104832353600080478536565205b6050616c6c65743a3a7665746f5f65787465726e616c605d2e4463616e63656c5f7265666572656e64756d0401247265665f696e646578e0013c5265666572656e64756d496e64657800090488536565205b6050616c6c65743a3a63616e63656c5f7265666572656e64756d605d2e2064656c65676174650c0108746fdd0101504163636f756e7449644c6f6f6b75704f663c543e000128636f6e76696374696f6e21030128436f6e76696374696f6e00011c62616c616e636518013042616c616e63654f663c543e000a0464536565205b6050616c6c65743a3a64656c6567617465605d2e28756e64656c6567617465000b046c536565205b6050616c6c65743a3a756e64656c6567617465605d2e58636c6561725f7075626c69635f70726f706f73616c73000c049c536565205b6050616c6c65743a3a636c6561725f7075626c69635f70726f706f73616c73605d2e18756e6c6f636b040118746172676574dd0101504163636f756e7449644c6f6f6b75704f663c543e000d045c536565205b6050616c6c65743a3a756e6c6f636b605d2e2c72656d6f76655f766f7465040114696e64657810013c5265666572656e64756d496e646578000e0470536565205b6050616c6c65743a3a72656d6f76655f766f7465605d2e4472656d6f76655f6f746865725f766f7465080118746172676574dd0101504163636f756e7449644c6f6f6b75704f663c543e000114696e64657810013c5265666572656e64756d496e646578000f0488536565205b6050616c6c65743a3a72656d6f76655f6f746865725f766f7465605d2e24626c61636b6c69737408013470726f706f73616c5f686173683001104832353600013c6d617962655f7265665f696e6465782503015c4f7074696f6e3c5265666572656e64756d496e6465783e00100468536565205b6050616c6c65743a3a626c61636b6c697374605d2e3c63616e63656c5f70726f706f73616c04012870726f705f696e646578e0012450726f70496e64657800110480536565205b6050616c6c65743a3a63616e63656c5f70726f706f73616c605d2e307365745f6d657461646174610801146f776e65722d0101344d657461646174614f776e65720001286d617962655f68617368290301504f7074696f6e3c507265696d616765486173683e00120474536565205b6050616c6c65743a3a7365745f6d65746164617461605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e21030c4070616c6c65745f64656d6f637261637928636f6e76696374696f6e28436f6e76696374696f6e00011c104e6f6e65000000204c6f636b65643178000100204c6f636b65643278000200204c6f636b65643378000300204c6f636b65643478000400204c6f636b65643578000500204c6f636b6564367800060000250304184f7074696f6e04045401100108104e6f6e6500000010536f6d650400100000010000290304184f7074696f6e04045401300108104e6f6e6500000010536f6d6504003000000100002d030c4470616c6c65745f636f6c6c6563746976651870616c6c65741043616c6c0804540004490001182c7365745f6d656d626572730c012c6e65775f6d656d62657273e50101445665633c543a3a4163636f756e7449643e0001147072696d65b001504f7074696f6e3c543a3a4163636f756e7449643e0001246f6c645f636f756e7410012c4d656d626572436f756e7400000470536565205b6050616c6c65743a3a7365745f6d656d62657273605d2e1c6578656375746508012070726f706f73616c5d01017c426f783c3c5420617320436f6e6669673c493e3e3a3a50726f706f73616c3e0001306c656e6774685f626f756e64e0010c75333200010460536565205b6050616c6c65743a3a65786563757465605d2e1c70726f706f73650c01247468726573686f6c64e0012c4d656d626572436f756e7400012070726f706f73616c5d01017c426f783c3c5420617320436f6e6669673c493e3e3a3a50726f706f73616c3e0001306c656e6774685f626f756e64e0010c75333200020460536565205b6050616c6c65743a3a70726f706f7365605d2e10766f74650c012070726f706f73616c30011c543a3a48617368000114696e646578e0013450726f706f73616c496e64657800011c617070726f766535010110626f6f6c00030454536565205b6050616c6c65743a3a766f7465605d2e4c646973617070726f76655f70726f706f73616c04013470726f706f73616c5f6861736830011c543a3a4861736800050490536565205b6050616c6c65743a3a646973617070726f76655f70726f706f73616c605d2e14636c6f736510013470726f706f73616c5f6861736830011c543a3a48617368000114696e646578e0013450726f706f73616c496e64657800015470726f706f73616c5f7765696768745f626f756e642401185765696768740001306c656e6774685f626f756e64e0010c75333200060458536565205b6050616c6c65743a3a636c6f7365605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e31030c4470616c6c65745f636f6c6c6563746976651870616c6c65741043616c6c0804540004490001182c7365745f6d656d626572730c012c6e65775f6d656d62657273e50101445665633c543a3a4163636f756e7449643e0001147072696d65b001504f7074696f6e3c543a3a4163636f756e7449643e0001246f6c645f636f756e7410012c4d656d626572436f756e7400000470536565205b6050616c6c65743a3a7365745f6d656d62657273605d2e1c6578656375746508012070726f706f73616c5d01017c426f783c3c5420617320436f6e6669673c493e3e3a3a50726f706f73616c3e0001306c656e6774685f626f756e64e0010c75333200010460536565205b6050616c6c65743a3a65786563757465605d2e1c70726f706f73650c01247468726573686f6c64e0012c4d656d626572436f756e7400012070726f706f73616c5d01017c426f783c3c5420617320436f6e6669673c493e3e3a3a50726f706f73616c3e0001306c656e6774685f626f756e64e0010c75333200020460536565205b6050616c6c65743a3a70726f706f7365605d2e10766f74650c012070726f706f73616c30011c543a3a48617368000114696e646578e0013450726f706f73616c496e64657800011c617070726f766535010110626f6f6c00030454536565205b6050616c6c65743a3a766f7465605d2e4c646973617070726f76655f70726f706f73616c04013470726f706f73616c5f6861736830011c543a3a4861736800050490536565205b6050616c6c65743a3a646973617070726f76655f70726f706f73616c605d2e14636c6f736510013470726f706f73616c5f6861736830011c543a3a48617368000114696e646578e0013450726f706f73616c496e64657800015470726f706f73616c5f7765696768745f626f756e642401185765696768740001306c656e6774685f626f756e64e0010c75333200060458536565205b6050616c6c65743a3a636c6f7365605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e35030c4470616c6c65745f6d656d626572736869701870616c6c65741043616c6c08045400044900011c286164645f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0000046c536565205b6050616c6c65743a3a6164645f6d656d626572605d2e3472656d6f76655f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00010478536565205b6050616c6c65743a3a72656d6f76655f6d656d626572605d2e2c737761705f6d656d62657208011872656d6f7665dd0101504163636f756e7449644c6f6f6b75704f663c543e00010c616464dd0101504163636f756e7449644c6f6f6b75704f663c543e00020470536565205b6050616c6c65743a3a737761705f6d656d626572605d2e3472657365745f6d656d6265727304011c6d656d62657273e50101445665633c543a3a4163636f756e7449643e00030478536565205b6050616c6c65743a3a72657365745f6d656d62657273605d2e286368616e67655f6b657904010c6e6577dd0101504163636f756e7449644c6f6f6b75704f663c543e0004046c536565205b6050616c6c65743a3a6368616e67655f6b6579605d2e247365745f7072696d6504010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00050468536565205b6050616c6c65743a3a7365745f7072696d65605d2e2c636c6561725f7072696d6500060470536565205b6050616c6c65743a3a636c6561725f7072696d65605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e39030c4470616c6c65745f6d656d626572736869701870616c6c65741043616c6c08045400044900011c286164645f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0000046c536565205b6050616c6c65743a3a6164645f6d656d626572605d2e3472656d6f76655f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00010478536565205b6050616c6c65743a3a72656d6f76655f6d656d626572605d2e2c737761705f6d656d62657208011872656d6f7665dd0101504163636f756e7449644c6f6f6b75704f663c543e00010c616464dd0101504163636f756e7449644c6f6f6b75704f663c543e00020470536565205b6050616c6c65743a3a737761705f6d656d626572605d2e3472657365745f6d656d6265727304011c6d656d62657273e50101445665633c543a3a4163636f756e7449643e00030478536565205b6050616c6c65743a3a72657365745f6d656d62657273605d2e286368616e67655f6b657904010c6e6577dd0101504163636f756e7449644c6f6f6b75704f663c543e0004046c536565205b6050616c6c65743a3a6368616e67655f6b6579605d2e247365745f7072696d6504010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00050468536565205b6050616c6c65743a3a7365745f7072696d65605d2e2c636c6561725f7072696d6500060470536565205b6050616c6c65743a3a636c6561725f7072696d65605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e3d030c6070616c6c65745f72616e6b65645f636f6c6c6563746976651870616c6c65741043616c6c080454000449000118286164645f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0000046c536565205b6050616c6c65743a3a6164645f6d656d626572605d2e3870726f6d6f74655f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0001047c536565205b6050616c6c65743a3a70726f6d6f74655f6d656d626572605d2e3464656d6f74655f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00020478536565205b6050616c6c65743a3a64656d6f74655f6d656d626572605d2e3472656d6f76655f6d656d62657208010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0001206d696e5f72616e6b4901011052616e6b00030478536565205b6050616c6c65743a3a72656d6f76655f6d656d626572605d2e10766f7465080110706f6c6c100144506f6c6c496e6465784f663c542c20493e00010c61796535010110626f6f6c00040454536565205b6050616c6c65743a3a766f7465605d2e30636c65616e75705f706f6c6c080128706f6c6c5f696e646578100144506f6c6c496e6465784f663c542c20493e00010c6d617810010c75333200050474536565205b6050616c6c65743a3a636c65616e75705f706f6c6c605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e41030c4070616c6c65745f7265666572656e64611870616c6c65741043616c6c080454000449000124187375626d69740c013c70726f706f73616c5f6f726967696e4503015c426f783c50616c6c6574734f726967696e4f663c543e3e00012070726f706f73616c5901014c426f756e64656443616c6c4f663c542c20493e000140656e6163746d656e745f6d6f6d656e746d03017c446973706174636854696d653c426c6f636b4e756d626572466f723c543e3e0000045c536565205b6050616c6c65743a3a7375626d6974605d2e58706c6163655f6465636973696f6e5f6465706f736974040114696e64657810013c5265666572656e64756d496e6465780001049c536565205b6050616c6c65743a3a706c6163655f6465636973696f6e5f6465706f736974605d2e5c726566756e645f6465636973696f6e5f6465706f736974040114696e64657810013c5265666572656e64756d496e646578000204a0536565205b6050616c6c65743a3a726566756e645f6465636973696f6e5f6465706f736974605d2e1863616e63656c040114696e64657810013c5265666572656e64756d496e6465780003045c536565205b6050616c6c65743a3a63616e63656c605d2e106b696c6c040114696e64657810013c5265666572656e64756d496e64657800040454536565205b6050616c6c65743a3a6b696c6c605d2e406e756467655f7265666572656e64756d040114696e64657810013c5265666572656e64756d496e64657800050484536565205b6050616c6c65743a3a6e756467655f7265666572656e64756d605d2e486f6e655f66657765725f6465636964696e67040114747261636b4901013c547261636b49644f663c542c20493e0006048c536565205b6050616c6c65743a3a6f6e655f66657765725f6465636964696e67605d2e64726566756e645f7375626d697373696f6e5f6465706f736974040114696e64657810013c5265666572656e64756d496e646578000704a8536565205b6050616c6c65743a3a726566756e645f7375626d697373696f6e5f6465706f736974605d2e307365745f6d65746164617461080114696e64657810013c5265666572656e64756d496e6465780001286d617962655f68617368290301504f7074696f6e3c507265696d616765486173683e00080474536565205b6050616c6c65743a3a7365745f6d65746164617461605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e450308306f70616c5f72756e74696d65304f726967696e43616c6c65720001201873797374656d0400490301746672616d655f73797374656d3a3a4f726967696e3c52756e74696d653e0000001c436f756e63696c04004d0301010170616c6c65745f636f6c6c6563746976653a3a4f726967696e3c52756e74696d652c2070616c6c65745f636f6c6c6563746976653a3a496e7374616e6365313e002b0048546563686e6963616c436f6d6d69747465650400510301010170616c6c65745f636f6c6c6563746976653a3a4f726967696e3c52756e74696d652c2070616c6c65745f636f6c6c6563746976653a3a496e7374616e6365323e002c001c4f726967696e7304005503016870616c6c65745f676f765f6f726967696e733a3a4f726967696e0063002c506f6c6b61646f7458636d04005903014870616c6c65745f78636d3a3a4f726967696e0033002843756d756c757358636d04005d03016863756d756c75735f70616c6c65745f78636d3a3a4f726967696e00340020457468657265756d04006103015c70616c6c65745f657468657265756d3a3a4f726967696e00650010566f69640400690301410173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a0a5f5f707269766174653a3a566f69640007000049030c346672616d655f737570706f7274206469737061746368245261774f726967696e04244163636f756e7449640100010c10526f6f74000000185369676e656404000001244163636f756e744964000100104e6f6e65000200004d03084470616c6c65745f636f6c6c656374697665245261774f726967696e08244163636f756e7449640100044900010c1c4d656d62657273080010012c4d656d626572436f756e74000010012c4d656d626572436f756e74000000184d656d62657204000001244163636f756e744964000100205f5068616e746f6d000200005103084470616c6c65745f636f6c6c656374697665245261774f726967696e08244163636f756e7449640100044900010c1c4d656d62657273080010012c4d656d626572436f756e74000010012c4d656d626572436f756e74000000184d656d62657204000001244163636f756e744964000100205f5068616e746f6d0002000055030c4870616c6c65745f676f765f6f726967696e731870616c6c6574184f726967696e0001045446656c6c6f777368697050726f706f736974696f6e0000000059030c2870616c6c65745f78636d1870616c6c6574184f726967696e0001080c58636d0400d401344d756c74694c6f636174696f6e00000020526573706f6e73650400d401344d756c74694c6f636174696f6e000100005d030c4863756d756c75735f70616c6c65745f78636d1870616c6c6574184f726967696e0001081452656c6179000000405369626c696e6750617261636861696e0400ad010118506172614964000100006103083c70616c6c65745f657468657265756d245261774f726967696e0001044c457468657265756d5472616e73616374696f6e04006503011048313630000000006503083c7072696d69746976655f7479706573104831363000000400ec01205b75383b2032305d00006903081c73705f636f726510566f6964000100006d0310346672616d655f737570706f727418747261697473207363686564756c6530446973706174636854696d65042c426c6f636b4e756d62657201100108084174040010012c426c6f636b4e756d626572000000144166746572040010012c426c6f636b4e756d6265720001000071030c4070616c6c65745f7363686564756c65721870616c6c65741043616c6c040454000118207363686564756c651001107768656e100144426c6f636b4e756d626572466f723c543e0001386d617962655f706572696f646963750301ac4f7074696f6e3c7363686564756c653a3a506572696f643c426c6f636b4e756d626572466f723c543e3e3e0001207072696f726974790801487363686564756c653a3a5072696f7269747900011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00000464536565205b6050616c6c65743a3a7363686564756c65605d2e1863616e63656c0801107768656e100144426c6f636b4e756d626572466f723c543e000114696e64657810010c7533320001045c536565205b6050616c6c65743a3a63616e63656c605d2e387363686564756c655f6e616d656414010869640401205461736b4e616d650001107768656e100144426c6f636b4e756d626572466f723c543e0001386d617962655f706572696f646963750301ac4f7074696f6e3c7363686564756c653a3a506572696f643c426c6f636b4e756d626572466f723c543e3e3e0001207072696f726974790801487363686564756c653a3a5072696f7269747900011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e0002047c536565205b6050616c6c65743a3a7363686564756c655f6e616d6564605d2e3063616e63656c5f6e616d656404010869640401205461736b4e616d6500030474536565205b6050616c6c65743a3a63616e63656c5f6e616d6564605d2e387363686564756c655f61667465721001146166746572100144426c6f636b4e756d626572466f723c543e0001386d617962655f706572696f646963750301ac4f7074696f6e3c7363686564756c653a3a506572696f643c426c6f636b4e756d626572466f723c543e3e3e0001207072696f726974790801487363686564756c653a3a5072696f7269747900011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e0004047c536565205b6050616c6c65743a3a7363686564756c655f6166746572605d2e507363686564756c655f6e616d65645f616674657214010869640401205461736b4e616d650001146166746572100144426c6f636b4e756d626572466f723c543e0001386d617962655f706572696f646963750301ac4f7074696f6e3c7363686564756c653a3a506572696f643c426c6f636b4e756d626572466f723c543e3e3e0001207072696f726974790801487363686564756c653a3a5072696f7269747900011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00050494536565205b6050616c6c65743a3a7363686564756c655f6e616d65645f6166746572605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e750304184f7074696f6e0404540179030108104e6f6e6500000010536f6d650400790300000100007903000004081010007d030c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c65741043616c6c04045400012448736572766963655f6f766572776569676874080114696e6465782c013c4f766572776569676874496e6465780001307765696768745f6c696d69742401185765696768740000048c536565205b6050616c6c65743a3a736572766963655f6f766572776569676874605d2e5473757370656e645f78636d5f657865637574696f6e00010498536565205b6050616c6c65743a3a73757370656e645f78636d5f657865637574696f6e605d2e50726573756d655f78636d5f657865637574696f6e00020494536565205b6050616c6c65743a3a726573756d655f78636d5f657865637574696f6e605d2e607570646174655f73757370656e645f7468726573686f6c6404010c6e657710010c753332000304a4536565205b6050616c6c65743a3a7570646174655f73757370656e645f7468726573686f6c64605d2e547570646174655f64726f705f7468726573686f6c6404010c6e657710010c75333200040498536565205b6050616c6c65743a3a7570646174655f64726f705f7468726573686f6c64605d2e5c7570646174655f726573756d655f7468726573686f6c6404010c6e657710010c753332000504a0536565205b6050616c6c65743a3a7570646174655f726573756d655f7468726573686f6c64605d2e5c7570646174655f7468726573686f6c645f77656967687404010c6e6577240118576569676874000604a0536565205b6050616c6c65743a3a7570646174655f7468726573686f6c645f776569676874605d2e707570646174655f7765696768745f72657374726963745f646563617904010c6e6577240118576569676874000704b4536565205b6050616c6c65743a3a7570646174655f7765696768745f72657374726963745f6465636179605d2e847570646174655f78636d705f6d61785f696e646976696475616c5f77656967687404010c6e6577240118576569676874000804c8536565205b6050616c6c65743a3a7570646174655f78636d705f6d61785f696e646976696475616c5f776569676874605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e81030c2870616c6c65745f78636d1870616c6c65741043616c6c04045400012c1073656e64080110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00011c6d65737361676585030154426f783c56657273696f6e656458636d3c28293e3e00000454536565205b6050616c6c65743a3a73656e64605d2e3c74656c65706f72745f617373657473100110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00012c62656e65666963696172790102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00011861737365747341020164426f783c56657273696f6e65644d756c74694173736574733e0001386665655f61737365745f6974656d10010c75333200010480536565205b6050616c6c65743a3a74656c65706f72745f617373657473605d2e5c726573657276655f7472616e736665725f617373657473100110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00012c62656e65666963696172790102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00011861737365747341020164426f783c56657273696f6e65644d756c74694173736574733e0001386665655f61737365745f6974656d10010c753332000204a0536565205b6050616c6c65743a3a726573657276655f7472616e736665725f617373657473605d2e1c6578656375746508011c6d657373616765050401c0426f783c56657273696f6e656458636d3c3c5420617320537973436f6e6669673e3a3a52756e74696d6543616c6c3e3e0001286d61785f77656967687424011857656967687400030460536565205b6050616c6c65743a3a65786563757465605d2e44666f7263655f78636d5f76657273696f6e0801206c6f636174696f6ed40148426f783c4d756c74694c6f636174696f6e3e00011c76657273696f6e10012858636d56657273696f6e00040488536565205b6050616c6c65743a3a666f7263655f78636d5f76657273696f6e605d2e64666f7263655f64656661756c745f78636d5f76657273696f6e0401446d617962655f78636d5f76657273696f6e250301484f7074696f6e3c58636d56657273696f6e3e000504a8536565205b6050616c6c65743a3a666f7263655f64656661756c745f78636d5f76657273696f6e605d2e78666f7263655f7375627363726962655f76657273696f6e5f6e6f746966790401206c6f636174696f6e0102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000604bc536565205b6050616c6c65743a3a666f7263655f7375627363726962655f76657273696f6e5f6e6f74696679605d2e80666f7263655f756e7375627363726962655f76657273696f6e5f6e6f746966790401206c6f636174696f6e0102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000704c4536565205b6050616c6c65743a3a666f7263655f756e7375627363726962655f76657273696f6e5f6e6f74696679605d2e7c6c696d697465645f726573657276655f7472616e736665725f617373657473140110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00012c62656e65666963696172790102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00011861737365747341020164426f783c56657273696f6e65644d756c74694173736574733e0001386665655f61737365745f6974656d10010c7533320001307765696768745f6c696d69742102012c5765696768744c696d6974000804c0536565205b6050616c6c65743a3a6c696d697465645f726573657276655f7472616e736665725f617373657473605d2e5c6c696d697465645f74656c65706f72745f617373657473140110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00012c62656e65666963696172790102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00011861737365747341020164426f783c56657273696f6e65644d756c74694173736574733e0001386665655f61737365745f6974656d10010c7533320001307765696768745f6c696d69742102012c5765696768744c696d6974000904a0536565205b6050616c6c65743a3a6c696d697465645f74656c65706f72745f617373657473605d2e40666f7263655f73757370656e73696f6e04012473757370656e64656435010110626f6f6c000a0484536565205b6050616c6c65743a3a666f7263655f73757370656e73696f6e605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e8503082c73746167696e675f78636d3056657273696f6e656458636d042c52756e74696d6543616c6c00010808563204008903015076323a3a58636d3c52756e74696d6543616c6c3e0002000856330400bd03015076333a3a58636d3c52756e74696d6543616c6c3e0003000089030c2c73746167696e675f78636d0876320c58636d042c52756e74696d6543616c6c000004008d0301745665633c496e737472756374696f6e3c52756e74696d6543616c6c3e3e00008d0300000291030091030c2c73746167696e675f78636d0876322c496e737472756374696f6e042c52756e74696d6543616c6c000170345769746864726177417373657404004502012c4d756c7469417373657473000000545265736572766541737365744465706f736974656404004502012c4d756c7469417373657473000100585265636569766554656c65706f72746564417373657404004502012c4d756c7469417373657473000200345175657279526573706f6e73650c012071756572795f696428011c51756572794964000120726573706f6e736595030120526573706f6e73650001286d61785f77656967687428010c753634000300345472616e7366657241737365740801186173736574734502012c4d756c746941737365747300012c62656e6566696369617279050201344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574734502012c4d756c746941737365747300011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f74797065a50301284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428010c75363400011063616c6ca9030168446f75626c65456e636f6465643c52756e74696d6543616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572e0010c7533320001406d61785f6d6573736167655f73697a65e0010c7533320001306d61785f6361706163697479e0010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74e0010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72e0010c75333200011873656e646572e0010c753332000124726563697069656e74e0010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e040009020154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720c012071756572795f696428011c5175657279496400011064657374050201344d756c74694c6f636174696f6e00014c6d61785f726573706f6e73655f77656967687428010c753634000c00304465706f73697441737365740c0118617373657473ad0301404d756c7469417373657446696c7465720001286d61785f617373657473e0010c75333200012c62656e6566696369617279050201344d756c74694c6f636174696f6e000d004c4465706f736974526573657276654173736574100118617373657473ad0301404d756c7469417373657446696c7465720001286d61785f617373657473e0010c75333200011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e000e003445786368616e6765417373657408011067697665ad0301404d756c7469417373657446696c74657200011c726563656976654502012c4d756c7469417373657473000f005c496e6974696174655265736572766557697468647261770c0118617373657473ad0301404d756c7469417373657446696c74657200011c72657365727665050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473ad0301404d756c7469417373657446696c74657200011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e001100305175657279486f6c64696e6710012071756572795f696428011c5175657279496400011064657374050201344d756c74694c6f636174696f6e000118617373657473ad0301404d756c7469417373657446696c74657200014c6d61785f726573706f6e73655f77656967687428010c75363400120030427579457865637574696f6e08011066656573290201284d756c746941737365740001307765696768745f6c696d6974b903012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c657204008903014058636d3c52756e74696d6543616c6c3e0015002c536574417070656e64697804008903014058636d3c52756e74696d6543616c6c3e00160028436c6561724572726f7200170028436c61696d41737365740801186173736574734502012c4d756c74694173736574730001187469636b6574050201344d756c74694c6f636174696f6e0018001054726170040028010c7536340019004053756273637269626556657273696f6e08012071756572795f696428011c5175657279496400014c6d61785f726573706f6e73655f77656967687428010c753634001a0048556e73756273637269626556657273696f6e001b000095030c2c73746167696e675f78636d08763220526573706f6e7365000110104e756c6c0000001841737365747304004502012c4d756c74694173736574730001003c457865637574696f6e526573756c740400990301504f7074696f6e3c287533322c204572726f72293e0002001c56657273696f6e040010013873757065723a3a56657273696f6e00030000990304184f7074696f6e040454019d030108104e6f6e6500000010536f6d6504009d0300000100009d030000040810a10300a103102c73746167696e675f78636d08763218747261697473144572726f72000168204f766572666c6f7700000034556e696d706c656d656e74656400010060556e74727573746564526573657276654c6f636174696f6e00020064556e7472757374656454656c65706f72744c6f636174696f6e000300444d756c74694c6f636174696f6e46756c6c000400684d756c74694c6f636174696f6e4e6f74496e7665727469626c65000500244261644f726967696e0006003c496e76616c69644c6f636174696f6e0007003441737365744e6f74466f756e64000800544661696c6564546f5472616e7361637441737365740009003c4e6f74576974686472617761626c65000a00484c6f636174696f6e43616e6e6f74486f6c64000b0054457863656564734d61784d65737361676553697a65000c005844657374696e6174696f6e556e737570706f72746564000d00245472616e73706f7274000e0028556e726f757461626c65000f0030556e6b6e6f776e436c61696d001000384661696c6564546f4465636f6465001100404d6178576569676874496e76616c6964001200384e6f74486f6c64696e674665657300130030546f6f457870656e73697665001400105472617004002c010c7536340015004c556e68616e646c656458636d56657273696f6e001600485765696768744c696d69745265616368656404002c01185765696768740017001c426172726965720018004c5765696768744e6f74436f6d70757461626c6500190000a5030c2c73746167696e675f78636d087632284f726967696e4b696e64000110184e617469766500000040536f7665726569676e4163636f756e74000100245375706572757365720002000c58636d00030000a9030c2c73746167696e675f78636d38646f75626c655f656e636f64656434446f75626c65456e636f646564040454000004011c656e636f64656434011c5665633c75383e0000ad03102c73746167696e675f78636d087632286d756c74696173736574404d756c7469417373657446696c74657200010820446566696e69746504004502012c4d756c74694173736574730000001057696c640400b103013857696c644d756c7469417373657400010000b103102c73746167696e675f78636d087632286d756c746961737365743857696c644d756c746941737365740001080c416c6c00000014416c6c4f6608010869642d02011c4173736574496400010c66756eb503013c57696c6446756e676962696c69747900010000b503102c73746167696e675f78636d087632286d756c746961737365743c57696c6446756e676962696c6974790001082046756e6769626c650000002c4e6f6e46756e6769626c6500010000b9030c2c73746167696e675f78636d0876322c5765696768744c696d697400010824556e6c696d697465640000001c4c696d69746564040028010c75363400010000bd030c2c73746167696e675f78636d0876330c58636d041043616c6c00000400c10301585665633c496e737472756374696f6e3c43616c6c3e3e0000c103000002c50300c5030c2c73746167696e675f78636d0876332c496e737472756374696f6e041043616c6c0001c034576974686472617741737365740400c4012c4d756c7469417373657473000000545265736572766541737365744465706f73697465640400c4012c4d756c7469417373657473000100585265636569766554656c65706f7274656441737365740400c4012c4d756c7469417373657473000200345175657279526573706f6e736510012071756572795f696428011c51756572794964000120726573706f6e7365c9030120526573706f6e73650001286d61785f77656967687424011857656967687400011c71756572696572f10301544f7074696f6e3c4d756c74694c6f636174696f6e3e000300345472616e736665724173736574080118617373657473c4012c4d756c746941737365747300012c62656e6566696369617279d401344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c0118617373657473c4012c4d756c746941737365747300011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64a50301284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737424011857656967687400011063616c6ca903014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572e0010c7533320001406d61785f6d6573736167655f73697a65e0010c7533320001306d61785f6361706163697479e0010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74e0010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72e0010c75333200011873656e646572e0010c753332000124726563697069656e74e0010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400d80154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720400f50301445175657279526573706f6e7365496e666f000c00304465706f7369744173736574080118617373657473f90301404d756c7469417373657446696c74657200012c62656e6566696369617279d401344d756c74694c6f636174696f6e000d004c4465706f7369745265736572766541737365740c0118617373657473f90301404d756c7469417373657446696c74657200011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e000e003445786368616e676541737365740c011067697665f90301404d756c7469417373657446696c74657200011077616e74c4012c4d756c746941737365747300011c6d6178696d616c35010110626f6f6c000f005c496e6974696174655265736572766557697468647261770c0118617373657473f90301404d756c7469417373657446696c74657200011c72657365727665d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473f90301404d756c7469417373657446696c74657200011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666ff50301445175657279526573706f6e7365496e666f000118617373657473f90301404d756c7469417373657446696c74657200120030427579457865637574696f6e08011066656573cc01284d756c746941737365740001307765696768745f6c696d69742102012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c65720400bd03012458636d3c43616c6c3e0015002c536574417070656e6469780400bd03012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473c4012c4d756c74694173736574730001187469636b6574d401344d756c74694c6f636174696f6e0018001054726170040028010c7536340019004053756273637269626556657273696f6e08012071756572795f696428011c5175657279496400014c6d61785f726573706f6e73655f776569676874240118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e41737365740400c4012c4d756c7469417373657473001c002c45787065637441737365740400c4012c4d756c7469417373657473001d00304578706563744f726967696e0400f10301544f7074696f6e3c4d756c74694c6f636174696f6e3e001e002c4578706563744572726f720400cd0301504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e736163745374617475730400e90301384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6534011c5665633c75383e000134726573706f6e73655f696e666ff50301445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578e0010c7533320001106e616d6534011c5665633c75383e00012c6d6f64756c655f6e616d6534011c5665633c75383e00012c63726174655f6d616a6f72e0010c75333200013c6d696e5f63726174655f6d696e6f72e0010c753332002200505265706f72745472616e736163745374617475730400f50301445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400dc01204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726be801244e6574776f726b496400012c64657374696e6174696f6ed80154496e746572696f724d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e002600244c6f636b41737365740801146173736574cc01284d756c74694173736574000120756e6c6f636b6572d401344d756c74694c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574cc01284d756c74694173736574000118746172676574d401344d756c74694c6f636174696f6e002800384e6f7465556e6c6f636b61626c650801146173736574cc01284d756c746941737365740001146f776e6572d401344d756c74694c6f636174696f6e0029003452657175657374556e6c6f636b0801146173736574cc01284d756c746941737365740001186c6f636b6572d401344d756c74694c6f636174696f6e002a002c536574466565734d6f64650401306a69745f776974686472617735010110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400d401344d756c74694c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69742102012c5765696768744c696d6974000130636865636b5f6f726967696ef10301544f7074696f6e3c4d756c74694c6f636174696f6e3e002f0000c9030c2c73746167696e675f78636d08763320526573706f6e7365000118104e756c6c000000184173736574730400c4012c4d756c74694173736574730001003c457865637574696f6e526573756c740400cd0301504f7074696f6e3c287533322c204572726f72293e0002001c56657273696f6e040010013873757065723a3a56657273696f6e0003002c50616c6c657473496e666f0400d9030198426f756e6465645665633c50616c6c6574496e666f2c204d617850616c6c657473496e666f3e000400384469737061746368526573756c740400e90301384d617962654572726f72436f646500050000cd0304184f7074696f6e04045401d1030108104e6f6e6500000010536f6d650400d1030000010000d1030000040810d50300d503102c73746167696e675f78636d08763318747261697473144572726f720001a0204f766572666c6f7700000034556e696d706c656d656e74656400010060556e74727573746564526573657276654c6f636174696f6e00020064556e7472757374656454656c65706f72744c6f636174696f6e000300304c6f636174696f6e46756c6c000400544c6f636174696f6e4e6f74496e7665727469626c65000500244261644f726967696e0006003c496e76616c69644c6f636174696f6e0007003441737365744e6f74466f756e64000800544661696c6564546f5472616e7361637441737365740009003c4e6f74576974686472617761626c65000a00484c6f636174696f6e43616e6e6f74486f6c64000b0054457863656564734d61784d65737361676553697a65000c005844657374696e6174696f6e556e737570706f72746564000d00245472616e73706f7274000e0028556e726f757461626c65000f0030556e6b6e6f776e436c61696d001000384661696c6564546f4465636f6465001100404d6178576569676874496e76616c6964001200384e6f74486f6c64696e674665657300130030546f6f457870656e73697665001400105472617004002c010c753634001500404578706563746174696f6e46616c73650016003850616c6c65744e6f74466f756e64001700304e616d654d69736d617463680018004c56657273696f6e496e636f6d70617469626c6500190050486f6c64696e67576f756c644f766572666c6f77001a002c4578706f72744572726f72001b00385265616e63686f724661696c6564001c00184e6f4465616c001d0028466565734e6f744d6574001e00244c6f636b4572726f72001f00304e6f5065726d697373696f6e00200028556e616e63686f726564002100384e6f744465706f73697461626c650022004c556e68616e646c656458636d56657273696f6e002300485765696768744c696d69745265616368656404002401185765696768740024001c426172726965720025004c5765696768744e6f74436f6d70757461626c650026004445786365656473537461636b4c696d697400270000d9030c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401dd03045300000400e50301185665633c543e0000dd030c2c73746167696e675f78636d0876332850616c6c6574496e666f0000180114696e646578e0010c7533320001106e616d65e1030180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e00012c6d6f64756c655f6e616d65e1030180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e0001146d616a6f72e0010c7533320001146d696e6f72e0010c7533320001147061746368e0010c7533320000e1030c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000e503000002dd0300e9030c2c73746167696e675f78636d087633384d617962654572726f72436f646500010c1c53756363657373000000144572726f720400ed03018c426f756e6465645665633c75382c204d617844697370617463684572726f724c656e3e000100385472756e63617465644572726f720400ed03018c426f756e6465645665633c75382c204d617844697370617463684572726f724c656e3e00020000ed030c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000f10304184f7074696f6e04045401d40108104e6f6e6500000010536f6d650400d40000010000f5030c2c73746167696e675f78636d087633445175657279526573706f6e7365496e666f00000c012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e00012071756572795f696428011c517565727949640001286d61785f7765696768742401185765696768740000f903102c73746167696e675f78636d087633286d756c74696173736574404d756c7469417373657446696c74657200010820446566696e6974650400c4012c4d756c74694173736574730000001057696c640400fd03013857696c644d756c7469417373657400010000fd03102c73746167696e675f78636d087633286d756c746961737365743857696c644d756c746941737365740001100c416c6c00000014416c6c4f660801086964d0011c4173736574496400010c66756e0104013c57696c6446756e676962696c69747900010028416c6c436f756e7465640400e0010c75333200020030416c6c4f66436f756e7465640c01086964d0011c4173736574496400010c66756e0104013c57696c6446756e676962696c697479000114636f756e74e0010c753332000300000104102c73746167696e675f78636d087633286d756c746961737365743c57696c6446756e676962696c6974790001082046756e6769626c650000002c4e6f6e46756e6769626c65000100000504082c73746167696e675f78636d3056657273696f6e656458636d042c52756e74696d6543616c6c00010808563204000904015076323a3a58636d3c52756e74696d6543616c6c3e00020008563304001904015076333a3a58636d3c52756e74696d6543616c6c3e0003000009040c2c73746167696e675f78636d0876320c58636d042c52756e74696d6543616c6c000004000d0401745665633c496e737472756374696f6e3c52756e74696d6543616c6c3e3e00000d0400000211040011040c2c73746167696e675f78636d0876322c496e737472756374696f6e042c52756e74696d6543616c6c000170345769746864726177417373657404004502012c4d756c7469417373657473000000545265736572766541737365744465706f736974656404004502012c4d756c7469417373657473000100585265636569766554656c65706f72746564417373657404004502012c4d756c7469417373657473000200345175657279526573706f6e73650c012071756572795f696428011c51756572794964000120726573706f6e736595030120526573706f6e73650001286d61785f77656967687428010c753634000300345472616e7366657241737365740801186173736574734502012c4d756c746941737365747300012c62656e6566696369617279050201344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574734502012c4d756c746941737365747300011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f74797065a50301284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428010c75363400011063616c6c15040168446f75626c65456e636f6465643c52756e74696d6543616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572e0010c7533320001406d61785f6d6573736167655f73697a65e0010c7533320001306d61785f6361706163697479e0010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74e0010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72e0010c75333200011873656e646572e0010c753332000124726563697069656e74e0010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e040009020154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720c012071756572795f696428011c5175657279496400011064657374050201344d756c74694c6f636174696f6e00014c6d61785f726573706f6e73655f77656967687428010c753634000c00304465706f73697441737365740c0118617373657473ad0301404d756c7469417373657446696c7465720001286d61785f617373657473e0010c75333200012c62656e6566696369617279050201344d756c74694c6f636174696f6e000d004c4465706f736974526573657276654173736574100118617373657473ad0301404d756c7469417373657446696c7465720001286d61785f617373657473e0010c75333200011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e000e003445786368616e6765417373657408011067697665ad0301404d756c7469417373657446696c74657200011c726563656976654502012c4d756c7469417373657473000f005c496e6974696174655265736572766557697468647261770c0118617373657473ad0301404d756c7469417373657446696c74657200011c72657365727665050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473ad0301404d756c7469417373657446696c74657200011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e001100305175657279486f6c64696e6710012071756572795f696428011c5175657279496400011064657374050201344d756c74694c6f636174696f6e000118617373657473ad0301404d756c7469417373657446696c74657200014c6d61785f726573706f6e73655f77656967687428010c75363400120030427579457865637574696f6e08011066656573290201284d756c746941737365740001307765696768745f6c696d6974b903012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c657204000904014058636d3c52756e74696d6543616c6c3e0015002c536574417070656e64697804000904014058636d3c52756e74696d6543616c6c3e00160028436c6561724572726f7200170028436c61696d41737365740801186173736574734502012c4d756c74694173736574730001187469636b6574050201344d756c74694c6f636174696f6e0018001054726170040028010c7536340019004053756273637269626556657273696f6e08012071756572795f696428011c5175657279496400014c6d61785f726573706f6e73655f77656967687428010c753634001a0048556e73756273637269626556657273696f6e001b000015040c2c73746167696e675f78636d38646f75626c655f656e636f64656434446f75626c65456e636f646564040454000004011c656e636f64656434011c5665633c75383e000019040c2c73746167696e675f78636d0876330c58636d041043616c6c000004001d0401585665633c496e737472756374696f6e3c43616c6c3e3e00001d0400000221040021040c2c73746167696e675f78636d0876332c496e737472756374696f6e041043616c6c0001c034576974686472617741737365740400c4012c4d756c7469417373657473000000545265736572766541737365744465706f73697465640400c4012c4d756c7469417373657473000100585265636569766554656c65706f7274656441737365740400c4012c4d756c7469417373657473000200345175657279526573706f6e736510012071756572795f696428011c51756572794964000120726573706f6e7365c9030120526573706f6e73650001286d61785f77656967687424011857656967687400011c71756572696572f10301544f7074696f6e3c4d756c74694c6f636174696f6e3e000300345472616e736665724173736574080118617373657473c4012c4d756c746941737365747300012c62656e6566696369617279d401344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c0118617373657473c4012c4d756c746941737365747300011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64a50301284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737424011857656967687400011063616c6c1504014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572e0010c7533320001406d61785f6d6573736167655f73697a65e0010c7533320001306d61785f6361706163697479e0010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74e0010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72e0010c75333200011873656e646572e0010c753332000124726563697069656e74e0010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400d80154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720400f50301445175657279526573706f6e7365496e666f000c00304465706f7369744173736574080118617373657473f90301404d756c7469417373657446696c74657200012c62656e6566696369617279d401344d756c74694c6f636174696f6e000d004c4465706f7369745265736572766541737365740c0118617373657473f90301404d756c7469417373657446696c74657200011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e000e003445786368616e676541737365740c011067697665f90301404d756c7469417373657446696c74657200011077616e74c4012c4d756c746941737365747300011c6d6178696d616c35010110626f6f6c000f005c496e6974696174655265736572766557697468647261770c0118617373657473f90301404d756c7469417373657446696c74657200011c72657365727665d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473f90301404d756c7469417373657446696c74657200011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666ff50301445175657279526573706f6e7365496e666f000118617373657473f90301404d756c7469417373657446696c74657200120030427579457865637574696f6e08011066656573cc01284d756c746941737365740001307765696768745f6c696d69742102012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c657204001904012458636d3c43616c6c3e0015002c536574417070656e64697804001904012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473c4012c4d756c74694173736574730001187469636b6574d401344d756c74694c6f636174696f6e0018001054726170040028010c7536340019004053756273637269626556657273696f6e08012071756572795f696428011c5175657279496400014c6d61785f726573706f6e73655f776569676874240118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e41737365740400c4012c4d756c7469417373657473001c002c45787065637441737365740400c4012c4d756c7469417373657473001d00304578706563744f726967696e0400f10301544f7074696f6e3c4d756c74694c6f636174696f6e3e001e002c4578706563744572726f720400cd0301504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e736163745374617475730400e90301384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6534011c5665633c75383e000134726573706f6e73655f696e666ff50301445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578e0010c7533320001106e616d6534011c5665633c75383e00012c6d6f64756c655f6e616d6534011c5665633c75383e00012c63726174655f6d616a6f72e0010c75333200013c6d696e5f63726174655f6d696e6f72e0010c753332002200505265706f72745472616e736163745374617475730400f50301445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400dc01204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726be801244e6574776f726b496400012c64657374696e6174696f6ed80154496e746572696f724d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e002600244c6f636b41737365740801146173736574cc01284d756c74694173736574000120756e6c6f636b6572d401344d756c74694c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574cc01284d756c74694173736574000118746172676574d401344d756c74694c6f636174696f6e002800384e6f7465556e6c6f636b61626c650801146173736574cc01284d756c746941737365740001146f776e6572d401344d756c74694c6f636174696f6e0029003452657175657374556e6c6f636b0801146173736574cc01284d756c746941737365740001186c6f636b6572d401344d756c74694c6f636174696f6e002a002c536574466565734d6f64650401306a69745f776974686472617735010110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400d401344d756c74694c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69742102012c5765696768744c696d6974000130636865636b5f6f726967696ef10301544f7074696f6e3c4d756c74694c6f636174696f6e3e002f000025040c4863756d756c75735f70616c6c65745f78636d1870616c6c65741043616c6c040454000100040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e29040c6063756d756c75735f70616c6c65745f646d705f71756575651870616c6c65741043616c6c04045400010448736572766963655f6f766572776569676874080114696e6465782c013c4f766572776569676874496e6465780001307765696768745f6c696d69742401185765696768740000048c536565205b6050616c6c65743a3a736572766963655f6f766572776569676874605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e2d040c4070616c6c65745f696e666c6174696f6e1870616c6c65741043616c6c0404540001043c73746172745f696e666c6174696f6e04016c696e666c6174696f6e5f73746172745f72656c61795f626c6f636b100144426c6f636b4e756d626572466f723c543e00000480536565205b6050616c6c65743a3a73746172745f696e666c6174696f6e605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e31040c3470616c6c65745f756e697175651870616c6c65741043616c6c040454000180446372656174655f636f6c6c656374696f6e10013c636f6c6c656374696f6e5f6e616d65350401d4426f756e6465645665633c7531362c20436f6e73745533323c4d41585f434f4c4c454354494f4e5f4e414d455f4c454e4754483e3e000158636f6c6c656374696f6e5f6465736372697074696f6e3d0401f0426f756e6465645665633c7531362c20436f6e73745533323c4d41585f434f4c4c454354494f4e5f4445534352495054494f4e5f4c454e4754483e3e000130746f6b656e5f707265666978410401c4426f756e6465645665633c75382c20436f6e73745533323c4d41585f544f4b454e5f5052454649585f4c454e4754483e3e0001106d6f646545040138436f6c6c656374696f6e4d6f646500000488536565205b6050616c6c65743a3a6372656174655f636f6c6c656374696f6e605d2e506372656174655f636f6c6c656374696f6e5f6578040110646174614904019c437265617465436f6c6c656374696f6e446174613c543a3a43726f73734163636f756e7449643e00010494536565205b6050616c6c65743a3a6372656174655f636f6c6c656374696f6e5f6578605d2e4864657374726f795f636f6c6c656374696f6e040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640002048c536565205b6050616c6c65743a3a64657374726f795f636f6c6c656374696f6e605d2e446164645f746f5f616c6c6f775f6c697374080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c616464726573734d040144543a3a43726f73734163636f756e74496400030488536565205b6050616c6c65743a3a6164645f746f5f616c6c6f775f6c697374605d2e5872656d6f76655f66726f6d5f616c6c6f775f6c697374080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c616464726573734d040144543a3a43726f73734163636f756e7449640004049c536565205b6050616c6c65743a3a72656d6f76655f66726f6d5f616c6c6f775f6c697374605d2e5c6368616e67655f636f6c6c656374696f6e5f6f776e6572080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001246e65775f6f776e6572000130543a3a4163636f756e744964000504a0536565205b6050616c6c65743a3a6368616e67655f636f6c6c656374696f6e5f6f776e6572605d2e506164645f636f6c6c656374696f6e5f61646d696e080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001306e65775f61646d696e5f69644d040144543a3a43726f73734163636f756e74496400060494536565205b6050616c6c65743a3a6164645f636f6c6c656374696f6e5f61646d696e605d2e5c72656d6f76655f636f6c6c656374696f6e5f61646d696e080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001286163636f756e745f69644d040144543a3a43726f73734163636f756e744964000704a0536565205b6050616c6c65743a3a72656d6f76655f636f6c6c656374696f6e5f61646d696e605d2e587365745f636f6c6c656374696f6e5f73706f6e736f72080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400012c6e65775f73706f6e736f72000130543a3a4163636f756e7449640008049c536565205b6050616c6c65743a3a7365745f636f6c6c656374696f6e5f73706f6e736f72605d2e4c636f6e6669726d5f73706f6e736f7273686970040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400090490536565205b6050616c6c65743a3a636f6e6669726d5f73706f6e736f7273686970605d2e6472656d6f76655f636f6c6c656374696f6e5f73706f6e736f72040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964000a04a8536565205b6050616c6c65743a3a72656d6f76655f636f6c6c656374696f6e5f73706f6e736f72605d2e2c6372656174655f6974656d0c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001146f776e65724d040144543a3a43726f73734163636f756e74496400011064617461c10401384372656174654974656d44617461000b0470536565205b6050616c6c65743a3a6372656174655f6974656d605d2e546372656174655f6d756c7469706c655f6974656d730c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001146f776e65724d040144543a3a43726f73734163636f756e7449640001286974656d735f64617461d104014c5665633c4372656174654974656d446174613e000c0498536565205b6050616c6c65743a3a6372656174655f6d756c7469706c655f6974656d73605d2e647365745f636f6c6c656374696f6e5f70726f70657274696573080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400012870726f70657274696573b50401345665633c50726f70657274793e000d04a8536565205b6050616c6c65743a3a7365745f636f6c6c656374696f6e5f70726f70657274696573605d2e7064656c6574655f636f6c6c656374696f6e5f70726f70657274696573080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400013470726f70657274795f6b657973d50401405665633c50726f70657274794b65793e000e04b4536565205b6050616c6c65743a3a64656c6574655f636f6c6c656374696f6e5f70726f70657274696573605d2e507365745f746f6b656e5f70726f706572746965730c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964000120746f6b656e5f6964d904011c546f6b656e496400012870726f70657274696573b50401345665633c50726f70657274793e000f0494536565205b6050616c6c65743a3a7365745f746f6b656e5f70726f70657274696573605d2e5c64656c6574655f746f6b656e5f70726f706572746965730c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964000120746f6b656e5f6964d904011c546f6b656e496400013470726f70657274795f6b657973d50401405665633c50726f70657274794b65793e001004a0536565205b6050616c6c65743a3a64656c6574655f746f6b656e5f70726f70657274696573605d2e787365745f746f6b656e5f70726f70657274795f7065726d697373696f6e73080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400015070726f70657274795f7065726d697373696f6e73a50401685665633c50726f70657274794b65795065726d697373696f6e3e001104bc536565205b6050616c6c65743a3a7365745f746f6b656e5f70726f70657274795f7065726d697373696f6e73605d2e606372656174655f6d756c7469706c655f6974656d735f6578080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011064617461dd04018c4372656174654974656d4578446174613c543a3a43726f73734163636f756e7449643e001204a4536565205b6050616c6c65743a3a6372656174655f6d756c7469706c655f6974656d735f6578605d2e687365745f7472616e73666572735f656e61626c65645f666c6167080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011476616c756535010110626f6f6c001304ac536565205b6050616c6c65743a3a7365745f7472616e73666572735f656e61626c65645f666c6167605d2e246275726e5f6974656d0c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e496400011476616c75651801107531323800140468536565205b6050616c6c65743a3a6275726e5f6974656d605d2e246275726e5f66726f6d100134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011066726f6d4d040144543a3a43726f73734163636f756e74496400011c6974656d5f6964d904011c546f6b656e496400011476616c75651801107531323800150468536565205b6050616c6c65743a3a6275726e5f66726f6d605d2e207472616e73666572100124726563697069656e744d040144543a3a43726f73734163636f756e744964000134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e496400011476616c75651801107531323800160464536565205b6050616c6c65743a3a7472616e73666572605d2e1c617070726f766510011c7370656e6465724d040144543a3a43726f73734163636f756e744964000134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e4964000118616d6f756e741801107531323800170460536565205b6050616c6c65743a3a617070726f7665605d2e30617070726f76655f66726f6d14011066726f6d4d040144543a3a43726f73734163636f756e744964000108746f4d040144543a3a43726f73734163636f756e744964000134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e4964000118616d6f756e741801107531323800180474536565205b6050616c6c65743a3a617070726f76655f66726f6d605d2e347472616e736665725f66726f6d14011066726f6d4d040144543a3a43726f73734163636f756e744964000124726563697069656e744d040144543a3a43726f73734163636f756e744964000134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e496400011476616c75651801107531323800190478536565205b6050616c6c65743a3a7472616e736665725f66726f6d605d2e547365745f636f6c6c656374696f6e5f6c696d697473080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001246e65775f6c696d69745d040140436f6c6c656374696f6e4c696d697473001a0498536565205b6050616c6c65743a3a7365745f636f6c6c656374696f6e5f6c696d697473605d2e687365745f636f6c6c656374696f6e5f7065726d697373696f6e73080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001386e65775f7065726d697373696f6e71040154436f6c6c656374696f6e5065726d697373696f6e73001b04ac536565205b6050616c6c65743a3a7365745f636f6c6c656374696f6e5f7065726d697373696f6e73605d2e2c7265706172746974696f6e0c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964000120746f6b656e5f6964d904011c546f6b656e4964000118616d6f756e7418011075313238001c0470536565205b6050616c6c65743a3a7265706172746974696f6e605d2e547365745f616c6c6f77616e63655f666f725f616c6c0c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001206f70657261746f724d040144543a3a43726f73734163636f756e74496400011c617070726f766535010110626f6f6c001d0498536565205b6050616c6c65743a3a7365745f616c6c6f77616e63655f666f725f616c6c605d2e5c666f7263655f7265706169725f636f6c6c656374696f6e040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964001e04a0536565205b6050616c6c65743a3a666f7263655f7265706169725f636f6c6c656374696f6e605d2e44666f7263655f7265706169725f6974656d080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e4964001f0488536565205b6050616c6c65743a3a666f7263655f7265706169725f6974656d605d2e04d85479706520616c69617320746f2050616c6c65742c20746f206265207573656420627920636f6e7374727563745f72756e74696d652e35040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454014901045300000400390401185665633c543e000039040000024901003d040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454014901045300000400390401185665633c543e000041040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e00004504083c75705f646174615f7374727563747338436f6c6c656374696f6e4d6f646500010c0c4e46540000002046756e6769626c650400080134446563696d616c506f696e747300010028526546756e6769626c65000200004904083c75705f646174615f7374727563747350437265617465436f6c6c656374696f6e44617461043843726f73734163636f756e744964014d04003001106d6f646545040138436f6c6c656374696f6e4d6f6465000118616363657373510401484f7074696f6e3c4163636573734d6f64653e0001106e616d6535040138436f6c6c656374696f6e4e616d6500012c6465736372697074696f6e3d040154436f6c6c656374696f6e4465736372697074696f6e000130746f6b656e5f70726566697841040154436f6c6c656374696f6e546f6b656e5072656669780001186c696d697473590401604f7074696f6e3c436f6c6c656374696f6e4c696d6974733e00012c7065726d697373696f6e736d0401744f7074696f6e3c436f6c6c656374696f6e5065726d697373696f6e733e000168746f6b656e5f70726f70657274795f7065726d697373696f6e7395040188436f6c6c656374696f6e50726f706572746965735065726d697373696f6e7356656300012870726f70657274696573a904015c436f6c6c656374696f6e50726f7065727469657356656300012861646d696e5f6c697374b904014c5665633c43726f73734163636f756e7449643e00013c70656e64696e675f73706f6e736f72bd0401584f7074696f6e3c43726f73734163636f756e7449643e000114666c6167736902013c436f6c6c656374696f6e466c61677300004d040c2870616c6c65745f65766d1c6163636f756e745c426173696343726f73734163636f756e7449645265707204244163636f756e744964010001082453756273747261746504000001244163636f756e74496400000020457468657265756d0400650301104831363000010000510404184f7074696f6e0404540155040108104e6f6e6500000010536f6d650400550400000100005504083c75705f646174615f73747275637473284163636573734d6f6465000108184e6f726d616c00000024416c6c6f774c69737400010000590404184f7074696f6e040454015d040108104e6f6e6500000010536f6d6504005d0400000100005d04083c75705f646174615f7374727563747340436f6c6c656374696f6e4c696d69747300002401746163636f756e745f746f6b656e5f6f776e6572736869705f6c696d69742503012c4f7074696f6e3c7533323e00014c73706f6e736f7265645f646174615f73697a652503012c4f7074696f6e3c7533323e00016473706f6e736f7265645f646174615f726174655f6c696d69746104016c4f7074696f6e3c53706f6e736f72696e67526174654c696d69743e00012c746f6b656e5f6c696d69742503012c4f7074696f6e3c7533323e00016073706f6e736f725f7472616e736665725f74696d656f75742503012c4f7074696f6e3c7533323e00015c73706f6e736f725f617070726f76655f74696d656f75742503012c4f7074696f6e3c7533323e0001486f776e65725f63616e5f7472616e73666572690401304f7074696f6e3c626f6f6c3e0001446f776e65725f63616e5f64657374726f79690401304f7074696f6e3c626f6f6c3e0001447472616e73666572735f656e61626c6564690401304f7074696f6e3c626f6f6c3e0000610404184f7074696f6e0404540165040108104e6f6e6500000010536f6d650400650400000100006504083c75705f646174615f737472756374734c53706f6e736f72696e67526174654c696d69740001084853706f6e736f72696e6744697361626c656400000018426c6f636b73040010010c75333200010000690404184f7074696f6e0404540135010108104e6f6e6500000010536f6d650400350100000100006d0404184f7074696f6e0404540171040108104e6f6e6500000010536f6d650400710400000100007104083c75705f646174615f7374727563747354436f6c6c656374696f6e5065726d697373696f6e7300000c0118616363657373510401484f7074696f6e3c4163636573734d6f64653e0001246d696e745f6d6f6465690401304f7074696f6e3c626f6f6c3e00011c6e657374696e67750401684f7074696f6e3c4e657374696e675065726d697373696f6e733e0000750404184f7074696f6e0404540179040108104e6f6e6500000010536f6d650400790400000100007904083c75705f646174615f73747275637473484e657374696e675065726d697373696f6e7300000c012c746f6b656e5f6f776e657235010110626f6f6c000140636f6c6c656374696f6e5f61646d696e35010110626f6f6c000128726573747269637465647d0401684f7074696f6e3c4f776e6572526573747269637465645365743e00007d0404184f7074696f6e0404540181040108104e6f6e6500000010536f6d650400810400000100008104083c75705f646174615f73747275637473484f776e657252657374726963746564536574000004008504015c4f776e657252657374726963746564536574496e6e6572000085040c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f7365743c426f756e64656442547265655365740804540189040453000004008d04012c42547265655365743c543e00008904083c75705f646174615f7374727563747330436f6c6c656374696f6e49640000040010010c75333200008d04042042547265655365740404540189040004009104000000910400000289040095040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454019904045300000400a50401185665633c543e00009904083c75705f646174615f737472756374735450726f70657274794b65795065726d697373696f6e000008010c6b65799d04012c50726f70657274794b65790001287065726d697373696f6ea104014850726f70657274795065726d697373696f6e00009d040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000a104083c75705f646174615f737472756374734850726f70657274795065726d697373696f6e00000c011c6d757461626c6535010110626f6f6c000140636f6c6c656374696f6e5f61646d696e35010110626f6f6c00012c746f6b656e5f6f776e657235010110626f6f6c0000a504000002990400a9040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401ad04045300000400b50401185665633c543e0000ad04083c75705f646174615f737472756374732050726f7065727479000008010c6b65799d04012c50726f70657274794b657900011476616c7565b104013450726f706572747956616c75650000b1040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000b504000002ad0400b9040000024d0400bd0404184f7074696f6e040454014d040108104e6f6e6500000010536f6d6504004d040000010000c104083c75705f646174615f73747275637473384372656174654974656d4461746100010c0c4e46540400c50401344372656174654e6674446174610000002046756e6769626c650400c904014843726561746546756e6769626c654461746100010028526546756e6769626c650400cd040150437265617465526546756e6769626c654461746100020000c504083c75705f646174615f73747275637473344372656174654e667444617461000004012870726f70657274696573a904015c436f6c6c656374696f6e50726f706572746965735665630000c904083c75705f646174615f737472756374734843726561746546756e6769626c6544617461000004011476616c7565180110753132380000cd04083c75705f646174615f7374727563747350437265617465526546756e6769626c654461746100000801187069656365731801107531323800012870726f70657274696573a904015c436f6c6c656374696f6e50726f706572746965735665630000d104000002c10400d5040000029d0400d904083c75705f646174615f737472756374731c546f6b656e49640000040010010c7533320000dd04083c75705f646174615f73747275637473404372656174654974656d457844617461043843726f73734163636f756e744964014d0401100c4e46540400e104012d01426f756e6465645665633c4372656174654e66744578446174613c43726f73734163636f756e7449643e2c20436f6e73745533323c0a4d41585f4954454d535f5045525f42415443483e3e0000002046756e6769626c650400ed04011101426f756e64656442547265654d61703c43726f73734163636f756e7449642c20753132382c20436f6e73745533323c4d41585f4954454d535f5045525f42415443483e3e0001005c526566756e6769626c654d756c7469706c654974656d730400fd04016501426f756e6465645665633c437265617465526566756e6769626c65457853696e676c654f776e65723c43726f73734163636f756e7449643e2c20436f6e73745533323c0a4d41585f4954454d535f5045525f42415443483e3e00020060526566756e6769626c654d756c7469706c654f776e6572730400090501c0437265617465526566756e6769626c6545784d756c7469706c654f776e6572733c43726f73734163636f756e7449643e00030000e1040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401e504045300000400e90401185665633c543e0000e504083c75705f646174615f737472756374733c4372656174654e6674457844617461043843726f73734163636f756e744964014d040008012870726f70657274696573a904015c436f6c6c656374696f6e50726f706572746965735665630001146f776e65724d04013843726f73734163636f756e7449640000e904000002e50400ed040c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f6d61703c426f756e64656442547265654d61700c044b014d0404560118045300000400f104013842547265654d61703c4b2c20563e0000f104042042547265654d617008044b014d0404560118000400f504000000f504000002f90400f904000004084d041800fd040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454010105045300000400050501185665633c543e00000105083c75705f646174615f7374727563747374437265617465526566756e6769626c65457853696e676c654f776e6572043843726f73734163636f756e744964014d04000c0110757365724d04013843726f73734163636f756e7449640001187069656365731801107531323800012870726f70657274696573a904015c436f6c6c656374696f6e50726f70657274696573566563000005050000020105000905083c75705f646174615f7374727563747380437265617465526566756e6769626c6545784d756c7469706c654f776e657273043843726f73734163636f756e744964014d04000801147573657273ed04011101426f756e64656442547265654d61703c43726f73734163636f756e7449642c20753132382c20436f6e73745533323c4d41585f4954454d535f5045525f42415443483e3e00012870726f70657274696573a904015c436f6c6c656374696f6e50726f7065727469657356656300000d050c5070616c6c65745f636f6e66696775726174696f6e1870616c6c65741043616c6c040454000118987365745f7765696768745f746f5f6665655f636f656666696369656e745f6f76657272696465040114636f6566661105012c4f7074696f6e3c7536343e000004dc536565205b6050616c6c65743a3a7365745f7765696768745f746f5f6665655f636f656666696369656e745f6f76657272696465605d2e687365745f6d696e5f6761735f70726963655f6f76657272696465040114636f6566661105012c4f7074696f6e3c7536343e000104ac536565205b6050616c6c65743a3a7365745f6d696e5f6761735f70726963655f6f76657272696465605d2ea07365745f6170705f70726f6d6f74696f6e5f636f6e66696775726174696f6e5f6f76657272696465040134636f6e66696775726174696f6e150501b041707050726f6d6f74696f6e436f6e66696775726174696f6e3c426c6f636b4e756d626572466f723c543e3e000304e4536565205b6050616c6c65743a3a7365745f6170705f70726f6d6f74696f6e5f636f6e66696775726174696f6e5f6f76657272696465605d2ea07365745f636f6c6c61746f725f73656c656374696f6e5f646573697265645f636f6c6c61746f727304010c6d61782503012c4f7074696f6e3c7533323e000404e4536565205b6050616c6c65743a3a7365745f636f6c6c61746f725f73656c656374696f6e5f646573697265645f636f6c6c61746f7273605d2e8c7365745f636f6c6c61746f725f73656c656374696f6e5f6c6963656e73655f626f6e64040118616d6f756e74250501784f7074696f6e3c3c5420617320436f6e6669673e3a3a42616c616e63653e000504d0536565205b6050616c6c65743a3a7365745f636f6c6c61746f725f73656c656374696f6e5f6c6963656e73655f626f6e64605d2e947365745f636f6c6c61746f725f73656c656374696f6e5f6b69636b5f7468726573686f6c640401247468726573686f6c64250301644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e000604d8536565205b6050616c6c65743a3a7365745f636f6c6c61746f725f73656c656374696f6e5f6b69636b5f7468726573686f6c64605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e110504184f7074696f6e040454012c0108104e6f6e6500000010536f6d6504002c00000100001505085070616c6c65745f636f6e66696775726174696f6e6441707050726f6d6f74696f6e436f6e66696775726174696f6e042c426c6f636b4e756d626572011000100158726563616c63756c6174696f6e5f696e74657276616c2503014c4f7074696f6e3c426c6f636b4e756d6265723e00014070656e64696e675f696e74657276616c2503014c4f7074696f6e3c426c6f636b4e756d6265723e00013c696e74657276616c5f696e636f6d651905013c4f7074696f6e3c50657262696c6c3e00016c6d61785f7374616b6572735f7065725f63616c63756c6174696f6e210501284f7074696f6e3c75383e0000190504184f7074696f6e040454011d050108104e6f6e6500000010536f6d6504001d0500000100001d050c3473705f61726974686d65746963287065725f7468696e67731c50657262696c6c0000040010010c7533320000210504184f7074696f6e04045401080108104e6f6e6500000010536f6d650400080000010000250504184f7074696f6e04045401180108104e6f6e6500000010536f6d65040018000001000029050c4070616c6c65745f7374727563747572651870616c6c65741043616c6c040454000100040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e2d050c5070616c6c65745f6170705f70726f6d6f74696f6e1870616c6c65741043616c6c040454000128447365745f61646d696e5f6164647265737304011461646d696e4d040144543a3a43726f73734163636f756e74496400000488536565205b6050616c6c65743a3a7365745f61646d696e5f61646472657373605d2e147374616b65040118616d6f756e7418013042616c616e63654f663c543e00010458536565205b6050616c6c65743a3a7374616b65605d2e2c756e7374616b655f616c6c00020470536565205b6050616c6c65743a3a756e7374616b655f616c6c605d2e3c756e7374616b655f7061727469616c040118616d6f756e7418013042616c616e63654f663c543e00080480536565205b6050616c6c65743a3a756e7374616b655f7061727469616c605d2e4873706f6e736f725f636f6c6c656374696f6e040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640003048c536565205b6050616c6c65743a3a73706f6e736f725f636f6c6c656374696f6e605d2e6873746f705f73706f6e736f72696e675f636f6c6c656374696f6e040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964000404ac536565205b6050616c6c65743a3a73746f705f73706f6e736f72696e675f636f6c6c656374696f6e605d2e4073706f6e736f725f636f6e747261637404012c636f6e74726163745f6964650301104831363000050484536565205b6050616c6c65743a3a73706f6e736f725f636f6e7472616374605d2e6073746f705f73706f6e736f72696e675f636f6e747261637404012c636f6e74726163745f69646503011048313630000604a4536565205b6050616c6c65743a3a73746f705f73706f6e736f72696e675f636f6e7472616374605d2e387061796f75745f7374616b6572730401387374616b6572735f6e756d626572210501284f7074696f6e3c75383e0007047c536565205b6050616c6c65743a3a7061796f75745f7374616b657273605d2e34666f7263655f756e7374616b6504013870656e64696e675f626c6f636b73310501585665633c426c6f636b4e756d626572466f723c543e3e00090478536565205b6050616c6c65743a3a666f7263655f756e7374616b65605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e3105000002100035050c5470616c6c65745f666f726569676e5f617373657473186d6f64756c651043616c6c0404540001085872656769737465725f666f726569676e5f61737365740c01146f776e6572000130543a3a4163636f756e7449640001206c6f636174696f6e0102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e0001206d6574616461746139050180426f783c41737365744d657461646174613c42616c616e63654f663c543e3e3e0000049c536565205b6050616c6c65743a3a72656769737465725f666f726569676e5f6173736574605d2e507570646174655f666f726569676e5f61737365740c0140666f726569676e5f61737365745f6964100138466f726569676e417373657449640001206c6f636174696f6e0102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e0001206d6574616461746139050180426f783c41737365744d657461646174613c42616c616e63654f663c543e3e3e00010494536565205b6050616c6c65743a3a7570646174655f666f726569676e5f6173736574605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e39050c5470616c6c65745f666f726569676e5f617373657473186d6f64756c653441737365744d65746164617461041c42616c616e63650118001001106e616d653d05012441737365744e616d6500011873796d626f6c4105012c417373657453796d626f6c000120646563696d616c73080108753800013c6d696e696d616c5f62616c616e636518011c42616c616e636500003d050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e000041050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e000045050c2870616c6c65745f65766d1870616c6c65741043616c6c04045400011020776974686472617708011c61646472657373650301104831363000011476616c756518013042616c616e63654f663c543e00000464536565205b6050616c6c65743a3a7769746864726177605d2e1063616c6c240118736f7572636565030110483136300001187461726765746503011048313630000114696e70757434011c5665633c75383e00011476616c756549050110553235360001246761735f6c696d69742c010c75363400013c6d61785f6665655f7065725f67617349050110553235360001606d61785f7072696f726974795f6665655f7065725f676173510501304f7074696f6e3c553235363e0001146e6f6e6365510501304f7074696f6e3c553235363e00012c6163636573735f6c697374550501585665633c28483136302c205665633c483235363e293e00010454536565205b6050616c6c65743a3a63616c6c605d2e18637265617465200118736f757263656503011048313630000110696e697434011c5665633c75383e00011476616c756549050110553235360001246761735f6c696d69742c010c75363400013c6d61785f6665655f7065725f67617349050110553235360001606d61785f7072696f726974795f6665655f7065725f676173510501304f7074696f6e3c553235363e0001146e6f6e6365510501304f7074696f6e3c553235363e00012c6163636573735f6c697374550501585665633c28483136302c205665633c483235363e293e0002045c536565205b6050616c6c65743a3a637265617465605d2e1c63726561746532240118736f757263656503011048313630000110696e697434011c5665633c75383e00011073616c743001104832353600011476616c756549050110553235360001246761735f6c696d69742c010c75363400013c6d61785f6665655f7065725f67617349050110553235360001606d61785f7072696f726974795f6665655f7065725f676173510501304f7074696f6e3c553235363e0001146e6f6e6365510501304f7074696f6e3c553235363e00012c6163636573735f6c697374550501585665633c28483136302c205665633c483235363e293e00030460536565205b6050616c6c65743a3a63726561746532605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e4905083c7072696d69746976655f74797065731055323536000004004d0501205b7536343b20345d00004d05000003040000002c00510504184f7074696f6e0404540149050108104e6f6e6500000010536f6d65040049050000010000550500000259050059050000040865035d05005d05000002300061050c3c70616c6c65745f657468657265756d1870616c6c65741043616c6c040454000104207472616e7361637404012c7472616e73616374696f6e6505012c5472616e73616374696f6e00000464536565205b6050616c6c65743a3a7472616e73616374605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e65050c20657468657265756d2c7472616e73616374696f6e345472616e73616374696f6e563200010c184c65676163790400690501444c65676163795472616e73616374696f6e0000001c45495032393330040079050148454950323933305472616e73616374696f6e0001001c45495031353539040085050148454950313535395472616e73616374696f6e0002000069050c20657468657265756d2c7472616e73616374696f6e444c65676163795472616e73616374696f6e00001c01146e6f6e636549050110553235360001246761735f707269636549050110553235360001246761735f6c696d69744905011055323536000118616374696f6e6d0501445472616e73616374696f6e416374696f6e00011476616c75654905011055323536000114696e70757434011442797465730001247369676e6174757265710501505472616e73616374696f6e5369676e617475726500006d050c20657468657265756d2c7472616e73616374696f6e445472616e73616374696f6e416374696f6e0001081043616c6c04006503011048313630000000184372656174650001000071050c20657468657265756d2c7472616e73616374696f6e505472616e73616374696f6e5369676e617475726500000c010476750501545472616e73616374696f6e5265636f76657279496400010472300110483235360001047330011048323536000075050c20657468657265756d2c7472616e73616374696f6e545472616e73616374696f6e5265636f766572794964000004002c010c753634000079050c20657468657265756d2c7472616e73616374696f6e48454950323933305472616e73616374696f6e00002c0120636861696e5f69642c010c7536340001146e6f6e636549050110553235360001246761735f707269636549050110553235360001246761735f6c696d69744905011055323536000118616374696f6e6d0501445472616e73616374696f6e416374696f6e00011476616c75654905011055323536000114696e707574340114427974657300012c6163636573735f6c6973747d0501284163636573734c6973740001306f64645f795f70617269747935010110626f6f6c0001047230011048323536000104733001104832353600007d0500000281050081050c20657468657265756d2c7472616e73616374696f6e384163636573734c6973744974656d000008011c616464726573736503011c4164647265737300013073746f726167655f6b6579735d0501245665633c483235363e000085050c20657468657265756d2c7472616e73616374696f6e48454950313535395472616e73616374696f6e0000300120636861696e5f69642c010c7536340001146e6f6e636549050110553235360001606d61785f7072696f726974795f6665655f7065725f676173490501105532353600013c6d61785f6665655f7065725f67617349050110553235360001246761735f6c696d69744905011055323536000118616374696f6e6d0501445472616e73616374696f6e416374696f6e00011476616c75654905011055323536000114696e707574340114427974657300012c6163636573735f6c6973747d0501284163636573734c6973740001306f64645f795f70617269747935010110626f6f6c00010472300110483235360001047330011048323536000089050c6c70616c6c65745f65766d5f636f6e74726163745f68656c706572731870616c6c65741043616c6c040454000104706d6967726174655f66726f6d5f73656c665f73706f6e736f72696e670401246164647265737365738d0501245665633c483136303e000004b4536565205b6050616c6c65743a3a6d6967726174655f66726f6d5f73656c665f73706f6e736f72696e67605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e8d0500000265030091050c5070616c6c65745f65766d5f6d6967726174696f6e1870616c6c65741043616c6c04045400011814626567696e04011c61646472657373650301104831363000000458536565205b6050616c6c65743a3a626567696e605d2e207365745f6461746108011c61646472657373650301104831363000011064617461950501445665633c28483235362c2048323536293e00010464536565205b6050616c6c65743a3a7365745f64617461605d2e1866696e69736808011c616464726573736503011048313630000110636f646534011c5665633c75383e0002045c536565205b6050616c6c65743a3a66696e697368605d2e3c696e736572745f6574685f6c6f67730401106c6f67739d0501485665633c657468657265756d3a3a4c6f673e00030480536565205b6050616c6c65743a3a696e736572745f6574685f6c6f6773605d2e34696e736572745f6576656e74730401186576656e74736d0101305665633c5665633c75383e3e00040478536565205b6050616c6c65743a3a696e736572745f6576656e7473605d2e4072656d6f76655f726d726b5f6461746100050484536565205b6050616c6c65743a3a72656d6f76655f726d726b5f64617461605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e95050000029905009905000004083030009d05000002a10500a1050c20657468657265756d0c6c6f670c4c6f6700000c011c616464726573736503011048313630000118746f706963735d0501245665633c483235363e0001106461746134011442797465730000a5050c4870616c6c65745f6d61696e74656e616e63651870616c6c65741043616c6c04045400010818656e61626c650000045c536565205b6050616c6c65743a3a656e61626c65605d2e1c64697361626c6500010460536565205b6050616c6c65743a3a64697361626c65605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ea9050c3870616c6c65745f7574696c6974791870616c6c65741043616c6c04045400011814626174636804011463616c6c73ad05017c5665633c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00000458536565205b6050616c6c65743a3a6261746368605d2e3461735f64657269766174697665080114696e6465784901010c75313600011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00010478536565205b6050616c6c65743a3a61735f64657269766174697665605d2e2462617463685f616c6c04011463616c6c73ad05017c5665633c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00020468536565205b6050616c6c65743a3a62617463685f616c6c605d2e2c64697370617463685f617308012461735f6f726967696e45030154426f783c543a3a50616c6c6574734f726967696e3e00011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00030470536565205b6050616c6c65743a3a64697370617463685f6173605d2e2c666f7263655f626174636804011463616c6c73ad05017c5665633c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00040470536565205b6050616c6c65743a3a666f7263655f6261746368605d2e2c776974685f77656967687408011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00011877656967687424011857656967687400050470536565205b6050616c6c65743a3a776974685f776569676874605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ead050000025d0100b1050c4470616c6c65745f746573745f7574696c731870616c6c65741043616c6c04045400011818656e61626c6500000454536565206050616c6c65743a3a656e61626c65602e387365745f746573745f76616c756504011476616c756510010c75333200010474536565206050616c6c65743a3a7365745f746573745f76616c7565602e6c7365745f746573745f76616c75655f616e645f726f6c6c6261636b04011476616c756510010c753332000204a8536565206050616c6c65743a3a7365745f746573745f76616c75655f616e645f726f6c6c6261636b602e38696e635f746573745f76616c756500030474536565206050616c6c65743a3a696e635f746573745f76616c7565602e346a7573745f74616b655f66656500040470536565206050616c6c65743a3a6a7573745f74616b655f666565602e2462617463685f616c6c04011463616c6c73ad05017c5665633c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00050460536565206050616c6c65743a3a62617463685f616c6c602e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732eb5050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000b9050c4070616c6c65745f7363686564756c65721870616c6c6574144576656e74040454000118245363686564756c65640801107768656e100144426c6f636b4e756d626572466f723c543e000114696e64657810010c753332000004505363686564756c656420736f6d65207461736b2e2043616e63656c65640801107768656e100144426c6f636b4e756d626572466f723c543e000114696e64657810010c7533320001044c43616e63656c656420736f6d65207461736b2e28446973706174636865640c01107461736b790301785461736b416464726573733c426c6f636b4e756d626572466f723c543e3e00010869648801404f7074696f6e3c5461736b4e616d653e000118726573756c74a801384469737061746368526573756c74000204544469737061746368656420736f6d65207461736b2e3c43616c6c556e617661696c61626c650801107461736b790301785461736b416464726573733c426c6f636b4e756d626572466f723c543e3e00010869648801404f7074696f6e3c5461736b4e616d653e00030429015468652063616c6c20666f72207468652070726f7669646564206861736820776173206e6f7420666f756e6420736f20746865207461736b20686173206265656e2061626f727465642e38506572696f6469634661696c65640801107461736b790301785461736b416464726573733c426c6f636b4e756d626572466f723c543e3e00010869648801404f7074696f6e3c5461736b4e616d653e0004043d0154686520676976656e207461736b2077617320756e61626c6520746f2062652072656e657765642073696e636520746865206167656e64612069732066756c6c206174207468617420626c6f636b2e545065726d616e656e746c794f7665727765696768740801107461736b790301785461736b416464726573733c426c6f636b4e756d626572466f723c543e3e00010869648801404f7074696f6e3c5461736b4e616d653e000504f054686520676976656e207461736b2063616e206e657665722062652065786563757465642073696e6365206974206973206f7665727765696768742e04304576656e747320747970652ebd050c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c6574144576656e7404045400011c1c537563636573730c01306d6573736167655f6861736804011c58636d486173680001286d6573736167655f696404011c58636d4861736800011877656967687424011857656967687400000464536f6d652058434d20776173206578656375746564206f6b2e104661696c1001306d6573736167655f6861736804011c58636d486173680001286d6573736167655f696404011c58636d486173680001146572726f72d503012058636d4572726f7200011877656967687424011857656967687400010440536f6d652058434d206661696c65642e2842616456657273696f6e0401306d6573736167655f6861736804011c58636d48617368000204544261642058434d2076657273696f6e20757365642e24426164466f726d61740401306d6573736167655f6861736804011c58636d48617368000304504261642058434d20666f726d617420757365642e3c58636d704d65737361676553656e740401306d6573736167655f6861736804011c58636d48617368000404c0416e2048524d50206d657373616765207761732073656e7420746f2061207369626c696e672070617261636861696e2e484f766572776569676874456e71756575656410011873656e646572ad01011850617261496400011c73656e745f617410014052656c6179426c6f636b4e756d626572000114696e6465782c013c4f766572776569676874496e6465780001207265717569726564240118576569676874000504d4416e2058434d2065786365656465642074686520696e646976696475616c206d65737361676520776569676874206275646765742e484f7665727765696768745365727669636564080114696e6465782c013c4f766572776569676874496e646578000110757365642401185765696768740006044101416e2058434d2066726f6d20746865206f7665727765696768742071756575652077617320657865637574656420776974682074686520676976656e2061637475616c2077656967687420757365642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574c1050c2870616c6c65745f78636d1870616c6c6574144576656e7404045400015c24417474656d7074656404011c6f7574636f6d65c505015078636d3a3a6c61746573743a3a4f7574636f6d65000004a8457865637574696f6e206f6620616e2058434d206d6573736167652077617320617474656d707465642e1053656e741001186f726967696ed401344d756c74694c6f636174696f6e00012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e00011c6d657373616765bd03011c58636d3c28293e0001286d6573736167655f696404011c58636d486173680001045c412058434d206d657373616765207761732073656e742e48556e6578706563746564526573706f6e73650801186f726967696ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c5175657279496400020c5901517565727920726573706f6e736520726563656976656420776869636820646f6573206e6f74206d61746368206120726567697374657265642071756572792e2054686973206d61792062652062656361757365206155016d61746368696e6720717565727920776173206e6576657220726567697374657265642c206974206d617920626520626563617573652069742069732061206475706c696361746520726573706f6e73652c206f727062656361757365207468652071756572792074696d6564206f75742e34526573706f6e7365526561647908012071756572795f69642c011c51756572794964000120726573706f6e7365c9030120526573706f6e73650003085d01517565727920726573706f6e736520686173206265656e20726563656976656420616e6420697320726561647920666f722074616b696e672077697468206074616b655f726573706f6e7365602e205468657265206973806e6f2072656769737465726564206e6f74696669636174696f6e2063616c6c2e204e6f7469666965640c012071756572795f69642c011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e64657808010875380004085901517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652072656769737465726564206e6f74696669636174696f6e20686173a86265656e206469737061746368656420616e64206578656375746564207375636365737366756c6c792e404e6f746966794f76657277656967687414012071756572795f69642c011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e646578080108753800013461637475616c5f77656967687424011857656967687400014c6d61785f62756467657465645f77656967687424011857656967687400050c4901517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652072656769737465726564206e6f74696669636174696f6e5901636f756c64206e6f742062652064697370617463686564206265636175736520746865206469737061746368207765696768742069732067726561746572207468616e20746865206d6178696d756d20776569676874e46f726967696e616c6c7920627564676574656420627920746869732072756e74696d6520666f722074686520717565727920726573756c742e4c4e6f7469667944697370617463684572726f720c012071756572795f69642c011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e64657808010875380006085501517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e2054686572652077617320612067656e6572616c206572726f722077697468886469737061746368696e6720746865206e6f74696669636174696f6e2063616c6c2e484e6f746966794465636f64654661696c65640c012071756572795f69642c011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e646578080108753800070c5101517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652064697370617463682077617320756e61626c6520746f20626559016465636f64656420696e746f2061206043616c6c603b2074686973206d696768742062652064756520746f2064697370617463682066756e6374696f6e20686176696e672061207369676e6174757265207768696368946973206e6f742060286f726967696e2c20517565727949642c20526573706f6e736529602e40496e76616c6964526573706f6e6465720c01186f726967696ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c5175657279496400014465787065637465645f6c6f636174696f6ef10301544f7074696f6e3c4d756c74694c6f636174696f6e3e00080c5901457870656374656420717565727920726573706f6e736520686173206265656e2072656365697665642062757420746865206f726967696e206c6f636174696f6e206f662074686520726573706f6e736520646f657355016e6f74206d6174636820746861742065787065637465642e205468652071756572792072656d61696e73207265676973746572656420666f722061206c617465722c2076616c69642c20726573706f6e736520746f6c626520726563656976656420616e642061637465642075706f6e2e5c496e76616c6964526573706f6e64657256657273696f6e0801186f726967696ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c5175657279496400091c5101457870656374656420717565727920726573706f6e736520686173206265656e2072656365697665642062757420746865206578706563746564206f726967696e206c6f636174696f6e20706c6163656420696e4d0173746f7261676520627920746869732072756e74696d652070726576696f75736c792063616e6e6f74206265206465636f6465642e205468652071756572792072656d61696e7320726567697374657265642e0041015468697320697320756e6578706563746564202873696e63652061206c6f636174696f6e20706c6163656420696e2073746f7261676520696e20612070726576696f75736c7920657865637574696e674d0172756e74696d652073686f756c64206265207265616461626c65207072696f7220746f2071756572792074696d656f75742920616e642064616e6765726f75732073696e63652074686520706f737369626c79590176616c696420726573706f6e73652077696c6c2062652064726f707065642e204d616e75616c20676f7665726e616e636520696e74657276656e74696f6e2069732070726f6261626c7920676f696e6720746f2062651c6e65656465642e34526573706f6e736554616b656e04012071756572795f69642c011c51756572794964000a04c8526563656976656420717565727920726573706f6e736520686173206265656e207265616420616e642072656d6f7665642e34417373657473547261707065640c011068617368300110483235360001186f726967696ed401344d756c74694c6f636174696f6e0001186173736574734102015056657273696f6e65644d756c7469417373657473000b04b8536f6d65206173736574732068617665206265656e20706c6163656420696e20616e20617373657420747261702e5456657273696f6e4368616e67654e6f74696669656410012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e000118726573756c7410012858636d56657273696f6e000110636f7374c4012c4d756c74694173736574730001286d6573736167655f696404011c58636d48617368000c0c2501416e2058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e206d65737361676520686173206265656e20617474656d7074656420746f2062652073656e742e00e054686520636f7374206f662073656e64696e672069742028626f726e652062792074686520636861696e2920697320696e636c756465642e5c537570706f7274656456657273696f6e4368616e6765640801206c6f636174696f6ed401344d756c74694c6f636174696f6e00011c76657273696f6e10012858636d56657273696f6e000d08390154686520737570706f727465642076657273696f6e206f662061206c6f636174696f6e20686173206265656e206368616e6765642e2054686973206d69676874206265207468726f75676820616ec06175746f6d61746963206e6f74696669636174696f6e206f722061206d616e75616c20696e74657276656e74696f6e2e504e6f7469667954617267657453656e644661696c0c01206c6f636174696f6ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c517565727949640001146572726f72d503012058636d4572726f72000e0859014120676976656e206c6f636174696f6e2077686963682068616420612076657273696f6e206368616e676520737562736372697074696f6e207761732064726f70706564206f77696e6720746f20616e206572726f727c73656e64696e6720746865206e6f74696669636174696f6e20746f2069742e644e6f746966795461726765744d6967726174696f6e4661696c0801206c6f636174696f6e0102015856657273696f6e65644d756c74694c6f636174696f6e00012071756572795f69642c011c51756572794964000f0859014120676976656e206c6f636174696f6e2077686963682068616420612076657273696f6e206368616e676520737562736372697074696f6e207761732064726f70706564206f77696e6720746f20616e206572726f72b46d6967726174696e6720746865206c6f636174696f6e20746f206f7572206e65772058434d20666f726d61742e54496e76616c69645175657269657256657273696f6e0801186f726967696ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c5175657279496400101c5501457870656374656420717565727920726573706f6e736520686173206265656e20726563656976656420627574207468652065787065637465642071756572696572206c6f636174696f6e20706c6163656420696e4d0173746f7261676520627920746869732072756e74696d652070726576696f75736c792063616e6e6f74206265206465636f6465642e205468652071756572792072656d61696e7320726567697374657265642e0041015468697320697320756e6578706563746564202873696e63652061206c6f636174696f6e20706c6163656420696e2073746f7261676520696e20612070726576696f75736c7920657865637574696e674d0172756e74696d652073686f756c64206265207265616461626c65207072696f7220746f2071756572792074696d656f75742920616e642064616e6765726f75732073696e63652074686520706f737369626c79590176616c696420726573706f6e73652077696c6c2062652064726f707065642e204d616e75616c20676f7665726e616e636520696e74657276656e74696f6e2069732070726f6261626c7920676f696e6720746f2062651c6e65656465642e38496e76616c6964517565726965721001186f726967696ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c5175657279496400014065787065637465645f71756572696572d401344d756c74694c6f636174696f6e0001506d617962655f61637475616c5f71756572696572f10301544f7074696f6e3c4d756c74694c6f636174696f6e3e00110c5d01457870656374656420717565727920726573706f6e736520686173206265656e20726563656976656420627574207468652071756572696572206c6f636174696f6e206f662074686520726573706f6e736520646f657351016e6f74206d61746368207468652065787065637465642e205468652071756572792072656d61696e73207265676973746572656420666f722061206c617465722c2076616c69642c20726573706f6e736520746f6c626520726563656976656420616e642061637465642075706f6e2e5056657273696f6e4e6f74696679537461727465640c012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e000110636f7374c4012c4d756c74694173736574730001286d6573736167655f696404011c58636d486173680012085901412072656d6f746520686173207265717565737465642058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e2066726f6d20757320616e64207765206861766520686f6e6f7265642069742e1d01412076657273696f6e20696e666f726d6174696f6e206d6573736167652069732073656e7420746f207468656d20616e642069747320636f737420697320696e636c756465642e5856657273696f6e4e6f746966795265717565737465640c012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e000110636f7374c4012c4d756c74694173736574730001286d6573736167655f696404011c58636d486173680013043d015765206861766520726571756573746564207468617420612072656d6f746520636861696e2073656e642075732058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e732e6056657273696f6e4e6f74696679556e7265717565737465640c012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e000110636f7374c4012c4d756c74694173736574730001286d6573736167655f696404011c58636d4861736800140825015765206861766520726571756573746564207468617420612072656d6f746520636861696e2073746f70732073656e64696e672075732058434d2076657273696f6e206368616e6765386e6f74696669636174696f6e732e204665657350616964080118706179696e67d401344d756c74694c6f636174696f6e00011066656573c4012c4d756c7469417373657473001504310146656573207765726520706169642066726f6d2061206c6f636174696f6e20666f7220616e206f7065726174696f6e20286f6674656e20666f72207573696e67206053656e6458636d60292e34417373657473436c61696d65640c011068617368300110483235360001186f726967696ed401344d756c74694c6f636174696f6e0001186173736574734102015056657273696f6e65644d756c7469417373657473001604c0536f6d65206173736574732068617665206265656e20636c61696d65642066726f6d20616e2061737365742074726170047c54686520604576656e746020656e756d206f6620746869732070616c6c6574c505102c73746167696e675f78636d087633187472616974731c4f7574636f6d6500010c20436f6d706c657465040024011857656967687400000028496e636f6d706c65746508002401185765696768740000d50301144572726f72000100144572726f720400d50301144572726f7200020000c9050c4863756d756c75735f70616c6c65745f78636d1870616c6c6574144576656e7404045400010c34496e76616c6964466f726d617404000401205b75383b2033325d00000880446f776e77617264206d65737361676520697320696e76616c69642058434d2e205c5b206964205c5d48556e737570706f7274656456657273696f6e04000401205b75383b2033325d000108bc446f776e77617264206d65737361676520697320756e737570706f727465642076657273696f6e206f662058434d2e205c5b206964205c5d404578656375746564446f776e7761726408000401205b75383b2033325d0000c505011c4f7574636f6d65000208c4446f776e77617264206d65737361676520657865637574656420776974682074686520676976656e206f7574636f6d652e445c5b2069642c206f7574636f6d65205c5d047c54686520604576656e746020656e756d206f6620746869732070616c6c6574cd050c6063756d756c75735f70616c6c65745f646d705f71756575651870616c6c6574144576656e7404045400011c34496e76616c6964466f726d61740401306d6573736167655f6861736804011c58636d4861736800000480446f776e77617264206d65737361676520697320696e76616c69642058434d2e48556e737570706f7274656456657273696f6e0401306d6573736167655f6861736804011c58636d48617368000104bc446f776e77617264206d65737361676520697320756e737570706f727465642076657273696f6e206f662058434d2e404578656375746564446f776e776172640c01306d6573736167655f6861736804011c58636d486173680001286d6573736167655f696404011c58636d4861736800011c6f7574636f6d65c505011c4f7574636f6d65000204c4446f776e77617264206d65737361676520657865637574656420776974682074686520676976656e206f7574636f6d652e3c5765696768744578686175737465641001306d6573736167655f6861736804011c58636d486173680001286d6573736167655f696404011c58636d4861736800014072656d61696e696e675f77656967687424011857656967687400013c72657175697265645f776569676874240118576569676874000304f054686520776569676874206c696d697420666f722068616e646c696e6720646f776e77617264206d657373616765732077617320726561636865642e484f766572776569676874456e7175657565641001306d6573736167655f6861736804011c58636d486173680001286d6573736167655f696404011c58636d486173680001406f7665727765696768745f696e6465782c013c4f766572776569676874496e64657800013c72657175697265645f7765696768742401185765696768740004041901446f776e77617264206d657373616765206973206f76657277656967687420616e642077617320706c6163656420696e20746865206f7665727765696768742071756575652e484f76657277656967687453657276696365640801406f7665727765696768745f696e6465782c013c4f766572776569676874496e64657800012c7765696768745f75736564240118576569676874000504e0446f776e77617264206d6573736167652066726f6d20746865206f766572776569676874207175657565207761732065786563757465642e504d61784d657373616765734578686175737465640401306d6573736167655f6861736804011c58636d48617368000604d0546865206d6178696d756d206e756d626572206f6620646f776e77617264206d657373616765732077617320726561636865642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574d1050c5070616c6c65745f636f6e66696775726174696f6e1870616c6c6574144576656e7404045400010c4c4e657744657369726564436f6c6c61746f7273040144646573697265645f636f6c6c61746f72732503012c4f7074696f6e3c7533323e000000584e6577436f6c6c61746f724c6963656e7365426f6e64040124626f6e645f636f7374250501484f7074696f6e3c543a3a42616c616e63653e000100604e6577436f6c6c61746f724b69636b5468726573686f6c640401406c656e6774685f696e5f626c6f636b73250301644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e000200047c54686520604576656e746020656e756d206f6620746869732070616c6c6574d5050c3470616c6c65745f636f6d6d6f6e1870616c6c6574144576656e7404045400015844436f6c6c656374696f6e437265617465640c0089040130436f6c6c656374696f6e496404dc476c6f62616c6c7920756e69717565206964656e746966696572206f66206e65776c79206372656174656420636f6c6c656374696f6e2e000801087538049c5b60436f6c6c656374696f6e4d6f6465605d20636f6e76657274656420696e746f205f75385f2e00000130543a3a4163636f756e7449640444436f6c6c656374696f6e206f776e65722e0004684e657720636f6c6c656374696f6e2077617320637265617465644c436f6c6c656374696f6e44657374726f796564040089040130436f6c6c656374696f6e496404a4476c6f62616c6c7920756e69717565206964656e746966696572206f6620636f6c6c656374696f6e2e0104704e657720636f6c6c656374696f6e207761732064657374726f7965642c4974656d43726561746564100089040130436f6c6c656374696f6e496404b04964206f662074686520636f6c6c656374696f6e207768657265206974656d2077617320637265617465642e00d904011c546f6b656e496404b04964206f6620616e206974656d2e20556e697175652077697468696e2074686520636f6c6c656374696f6e2e004d040144543a3a43726f73734163636f756e744964046c4f776e6572206f66206e65776c792063726561746564206974656d00180110753132380440416c77617973203120666f72204e46540204544e6577206974656d2077617320637265617465642e344974656d44657374726f796564100089040130436f6c6c656374696f6e496404b84964206f662074686520636f6c6c656374696f6e207768657265206974656d207761732064657374726f7965642e00d904011c546f6b656e496404644964656e746966696572206f66206275726e6564204e46542e004d040144543a3a43726f73734163636f756e744964049057686963682075736572206861732064657374726f7965642069747320746f6b656e732e001801107531323804c8416d6f756e74206f6620746f6b656e207069656365732064657374726f65642e20416c77617973203120666f72204e46542e03046c436f6c6c656374696f6e206974656d20776173206275726e65642e205472616e73666572140089040130436f6c6c656374696f6e496404a44964206f6620636f6c6c656374696f6e20746f207768696368206974656d2069732062656c6f6e672e00d904011c546f6b656e496404384964206f6620616e206974656d2e004d040144543a3a43726f73734163636f756e744964045c4f726967696e616c206f776e6572206f66206974656d2e004d040144543a3a43726f73734163636f756e74496404484e6577206f776e6572206f66206974656d2e001801107531323804d0416d6f756e74206f6620746f6b656e20706965636573207472616e7366657265642e20416c77617973203120666f72204e46542e0404504974656d20776173207472616e7366657272656420417070726f766564140089040130436f6c6c656374696f6e496404a44964206f6620636f6c6c656374696f6e20746f207768696368206974656d2069732062656c6f6e672e00d904011c546f6b656e496404384964206f6620616e206974656d2e004d040144543a3a43726f73734163636f756e744964045c4f726967696e616c206f776e6572206f66206974656d2e004d040144543a3a43726f73734163636f756e7449640498496420666f722077686963682074686520617070726f76616c20776173206772616e7465642e001801107531323804d0416d6f756e74206f6620746f6b656e20706965636573207472616e7366657265642e20416c77617973203120666f72204e46542e05041101416d6f756e7420706965636573206f6620746f6b656e206f776e6564206279206073656e646572602077617320617070726f76656420666f7220607370656e646572602e38417070726f766564466f72416c6c100089040130436f6c6c656374696f6e496404a44964206f6620636f6c6c656374696f6e20746f207768696368206974656d2069732062656c6f6e672e004d040144543a3a43726f73734163636f756e74496404484f776e6572206f6620612077616c6c65742e004d040144543a3a43726f73734163636f756e74496404d0496420666f72207768696368206f70657261746f722073746174757320776173206772616e746564206f72207265776f6b65642e0035010110626f6f6c04984973206f70657261746f7220737461747573206772616e746564206f72207265766f6b65643f0604050141206073656e6465726020617070726f766573206f7065726174696f6e73206f6e20616c6c206f776e656420746f6b656e7320666f7220607370656e646572602e54436f6c6c656374696f6e50726f7065727479536574080089040130436f6c6c656374696f6e496404c04964206f6620636f6c6c656374696f6e20746f2077686963682070726f706572747920686173206265656e207365742e009d04012c50726f70657274794b657904685468652070726f7065727479207468617420776173207365742e0704c054686520636f6c6c6574696f6e2070726f706572747920686173206265656e206164646564206f72206564697465642e64436f6c6c656374696f6e50726f706572747944656c65746564080089040130436f6c6c656374696f6e496404d04964206f6620636f6c6c656374696f6e20746f2077686963682070726f706572747920686173206265656e2064656c657465642e009d04012c50726f70657274794b657904785468652070726f70657274792074686174207761732064656c657465642e0804785468652070726f706572747920686173206265656e2064656c657465642e40546f6b656e50726f70657274795365740c0089040130436f6c6c656374696f6e496404f84964656e746966696572206f662074686520636f6c6c656374696f6e2077686f736520746f6b656e20686173207468652070726f7065727479207365742e00d904011c546f6b656e496404a454686520746f6b656e20666f72207768696368207468652070726f706572747920776173207365742e009d04012c50726f70657274794b657904685468652070726f7065727479207468617420776173207365742e0904b054686520746f6b656e2070726f706572747920686173206265656e206164646564206f72206564697465642e50546f6b656e50726f706572747944656c657465640c0089040130436f6c6c656374696f6e49640409014964656e746966696572206f662074686520636f6c6c656374696f6e2077686f736520746f6b656e20686173207468652070726f70657274792064656c657465642e00d904011c546f6b656e496404b454686520746f6b656e20666f72207768696368207468652070726f7065727479207761732064656c657465642e009d04012c50726f70657274794b657904785468652070726f70657274792074686174207761732064656c657465642e0a049054686520746f6b656e2070726f706572747920686173206265656e2064656c657465642e5450726f70657274795065726d697373696f6e536574080089040130436f6c6c656374696f6e496404ec4944206f6620636f6c6c656374696f6e20746f2077686963682070726f7065727479207065726d697373696f6e20686173206265656e207365742e009d04012c50726f70657274794b657904945468652070726f7065727479207065726d697373696f6e207468617420776173207365742e0b04ec54686520746f6b656e2070726f7065727479207065726d697373696f6e206f66206120636f6c6c656374696f6e20686173206265656e207365742e54416c6c6f774c697374416464726573734164646564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e004d040144543a3a43726f73734163636f756e744964047441646472657373206f6620746865206164646564206163636f756e742e0c0490416464726573732077617320616464656420746f2074686520616c6c6f77206c6973742e5c416c6c6f774c6973744164647265737352656d6f766564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e004d040144543a3a43726f73734163636f756e744964047c41646472657373206f66207468652072656d6f766564206163636f756e742e0d04a041646472657373207761732072656d6f7665642066726f6d2074686520616c6c6f77206c6973742e50436f6c6c656374696f6e41646d696e4164646564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e004d040144543a3a43726f73734163636f756e744964043841646d696e20616464726573732e0e046c436f6c6c656374696f6e2061646d696e207761732061646465642e58436f6c6c656374696f6e41646d696e52656d6f766564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e004d040144543a3a43726f73734163636f756e744964045852656d6f7665642061646d696e20616464726573732e0f0474436f6c6c656374696f6e2061646d696e207761732072656d6f7665642e48436f6c6c656374696f6e4c696d6974536574040089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e10046c436f6c6c656374696f6e206c696d6974732077657265207365742e58436f6c6c656374696f6e4f776e65724368616e676564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e00000130543a3a4163636f756e74496404484e6577206f776e657220616464726573732e110474436f6c6c656374696f6e206f776e656420776173206368616e6765642e5c436f6c6c656374696f6e5065726d697373696f6e536574040089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e120480436f6c6c656374696f6e207065726d697373696f6e732077657265207365742e50436f6c6c656374696f6e53706f6e736f72536574080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e00000130543a3a4163636f756e74496404504e65772073706f6e736f7220616464726573732e13046c436f6c6c656374696f6e2073706f6e736f7220776173207365742e5053706f6e736f7273686970436f6e6669726d6564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e00000130543a3a4163636f756e74496404504e65772073706f6e736f7220616464726573732e1404604e65772073706f6e736f722077617320636f6e6669726d2e60436f6c6c656374696f6e53706f6e736f7252656d6f766564040089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e15047c436f6c6c656374696f6e2073706f6e736f72207761732072656d6f7665642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574d9050c4070616c6c65745f7374727563747572651870616c6c6574144576656e740404540001042045786563757465640400a801384469737061746368526573756c740000049445786563757465642063616c6c206f6e20626568616c66206f662074686520746f6b656e2e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574dd050c5070616c6c65745f6170705f70726f6d6f74696f6e1870616c6c6574144576656e74040454000110505374616b696e67526563616c63756c6174696f6e0c00000130543a3a4163636f756e7449640458416e20726563616c63756c61746564207374616b65720018013042616c616e63654f663c543e049042617365206f6e20776869636820696e7465726573742069732063616c63756c617465640018013042616c616e63654f663c543e0468416d6f756e74206f66206163637275656420696e74657265737400188c5374616b696e6720726563616c63756c6174696f6e2077617320706572666f726d6564002c2320417267756d656e74738c2a204163636f756e7449643a206163636f756e74206f6620746865207374616b65722e782a2042616c616e6365203a20726563616c63756c6174696f6e2062617365602a2042616c616e6365203a20746f74616c20696e636f6d65145374616b650800000130543a3a4163636f756e744964000018013042616c616e63654f663c543e000114545374616b696e672077617320706572666f726d6564002c2320417267756d656e7473882a204163636f756e7449643a206163636f756e74206f6620746865207374616b6572682a2042616c616e6365203a207374616b696e6720616d6f756e741c556e7374616b650800000130543a3a4163636f756e744964000018013042616c616e63654f663c543e0002145c556e7374616b696e672077617320706572666f726d6564002c2320417267756d656e7473882a204163636f756e7449643a206163636f756e74206f6620746865207374616b6572702a2042616c616e6365203a20756e7374616b696e6720616d6f756e742053657441646d696e0400000130543a3a4163636f756e744964000310445468652061646d696e2077617320736574002c2320417267756d656e7473a42a204163636f756e7449643a206163636f756e742061646472657373206f66207468652061646d696e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574e1050c5470616c6c65745f666f726569676e5f617373657473186d6f64756c65144576656e7404045400011058466f726569676e4173736574526567697374657265640c012061737365745f6964100138466f726569676e4173736574496400013461737365745f61646472657373d401344d756c74694c6f636174696f6e0001206d657461646174613905016c41737365744d657461646174613c42616c616e63654f663c543e3e0000047454686520666f726569676e20617373657420726567697374657265642e4c466f726569676e4173736574557064617465640c012061737365745f6964100138466f726569676e4173736574496400013461737365745f61646472657373d401344d756c74694c6f636174696f6e0001206d657461646174613905016c41737365744d657461646174613c42616c616e63654f663c543e3e0001046854686520666f726569676e20617373657420757064617465642e3c41737365745265676973746572656408012061737365745f69640d01011c417373657449640001206d657461646174613905016c41737365744d657461646174613c42616c616e63654f663c543e3e0002045454686520617373657420726567697374657265642e3041737365745570646174656408012061737365745f69640d01011c417373657449640001206d657461646174613905016c41737365744d657461646174613c42616c616e63654f663c543e3e0003044854686520617373657420757064617465642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574e5050c2870616c6c65745f65766d1870616c6c6574144576656e740404540001140c4c6f6704010c6c6f67a105010c4c6f670000047c457468657265756d206576656e74732066726f6d20636f6e7472616374732e1c4372656174656404011c616464726573736503011048313630000104b44120636f6e747261637420686173206265656e206372656174656420617420676976656e20616464726573732e34437265617465644661696c656404011c61646472657373650301104831363000020405014120636f6e74726163742077617320617474656d7074656420746f20626520637265617465642c206275742074686520657865637574696f6e206661696c65642e20457865637574656404011c616464726573736503011048313630000304f84120636f6e747261637420686173206265656e206578656375746564207375636365737366756c6c79207769746820737461746573206170706c6965642e3845786563757465644661696c656404011c61646472657373650301104831363000040465014120636f6e747261637420686173206265656e2065786563757465642077697468206572726f72732e20537461746573206172652072657665727465642077697468206f6e6c79206761732066656573206170706c6965642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574e9050c3c70616c6c65745f657468657265756d1870616c6c6574144576656e7400010420457865637574656414011066726f6d6503011048313630000108746f65030110483136300001407472616e73616374696f6e5f686173683001104832353600012c657869745f726561736f6eed05012845786974526561736f6e00012865787472615f6461746134011c5665633c75383e000004c8416e20657468657265756d207472616e73616374696f6e20776173207375636365737366756c6c792065786563757465642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574ed050c2065766d5f636f7265146572726f722845786974526561736f6e0001101c537563636565640400f105012c4578697453756363656564000000144572726f720400f5050124457869744572726f72000100185265766572740400050601284578697452657665727400020014466174616c04000906012445786974466174616c00030000f1050c2065766d5f636f7265146572726f722c457869745375636365656400010c1c53746f707065640000002052657475726e656400010020537569636964656400020000f5050c2065766d5f636f7265146572726f7224457869744572726f7200014038537461636b556e646572666c6f7700000034537461636b4f766572666c6f770001002c496e76616c69644a756d7000020030496e76616c696452616e67650003004444657369676e61746564496e76616c69640004002c43616c6c546f6f446565700005003c437265617465436f6c6c6973696f6e0006004c437265617465436f6e74726163744c696d69740007002c496e76616c6964436f64650400f90501184f70636f6465000f002c4f75744f664f6666736574000800204f75744f66476173000900244f75744f6646756e64000a002c5043556e646572666c6f77000b002c437265617465456d707479000c00144f746865720400fd050144436f773c277374617469632c207374723e000d00204d61784e6f6e6365000e0000f9050c2065766d5f636f7265186f70636f6465184f70636f64650000040008010875380000fd05040c436f7704045401010600040001060000000106000005020005060c2065766d5f636f7265146572726f7228457869745265766572740001042052657665727465640000000009060c2065766d5f636f7265146572726f722445786974466174616c000110304e6f74537570706f7274656400000048556e68616e646c6564496e746572727570740001004043616c6c4572726f724173466174616c0400f5050124457869744572726f72000200144f746865720400fd050144436f773c277374617469632c207374723e000300000d060c6c70616c6c65745f65766d5f636f6e74726163745f68656c706572731870616c6c6574144576656e7404045400010c48436f6e747261637453706f6e736f725365740800650301104831363004b0436f6e74726163742061646472657373206f662074686520616666656374656420636f6c6c656374696f6e2e00000130543a3a4163636f756e74496404504e65772073706f6e736f7220616464726573732e000464436f6e74726163742073706f6e736f7220776173207365742e70436f6e747261637453706f6e736f7273686970436f6e6669726d65640800650301104831363004b0436f6e74726163742061646472657373206f662074686520616666656374656420636f6c6c656374696f6e2e00000130543a3a4163636f756e74496404504e65772073706f6e736f7220616464726573732e0104604e65772073706f6e736f722077617320636f6e6669726d2e58436f6e747261637453706f6e736f7252656d6f7665640400650301104831363004b0436f6e74726163742061646472657373206f662074686520616666656374656420636f6c6c656374696f6e2e02047c436f6c6c656374696f6e2073706f6e736f72207761732072656d6f7665642e047c54686520604576656e746020656e756d206f6620746869732070616c6c657411060c5070616c6c65745f65766d5f6d6967726174696f6e1870616c6c6574144576656e7404045400010424546573744576656e74000004f054686973206576656e74206973207573656420696e2062656e63686d61726b696e6720616e642063616e206265207573656420666f72207465737473047c54686520604576656e746020656e756d206f6620746869732070616c6c657415060c4870616c6c65745f6d61696e74656e616e63651870616c6c6574144576656e74040454000108484d61696e74656e616e6365456e61626c65640000004c4d61696e74656e616e636544697361626c6564000100047c54686520604576656e746020656e756d206f6620746869732070616c6c657419060c3870616c6c65745f7574696c6974791870616c6c6574144576656e74000118404261746368496e746572727570746564080114696e64657810010c7533320001146572726f7264013444697370617463684572726f7200000855014261746368206f66206469737061746368657320646964206e6f7420636f6d706c6574652066756c6c792e20496e646578206f66206669727374206661696c696e6720646973706174636820676976656e2c2061734877656c6c20617320746865206572726f722e384261746368436f6d706c65746564000104c84261746368206f66206469737061746368657320636f6d706c657465642066756c6c792077697468206e6f206572726f722e604261746368436f6d706c65746564576974684572726f7273000204b44261746368206f66206469737061746368657320636f6d706c657465642062757420686173206572726f72732e344974656d436f6d706c657465640003041d01412073696e676c65206974656d2077697468696e2061204261746368206f6620646973706174636865732068617320636f6d706c657465642077697468206e6f206572726f722e284974656d4661696c65640401146572726f7264013444697370617463684572726f720004041101412073696e676c65206974656d2077697468696e2061204261746368206f6620646973706174636865732068617320636f6d706c657465642077697468206572726f722e30446973706174636865644173040118726573756c74a801384469737061746368526573756c7400050458412063616c6c2077617320646973706174636865642e047c54686520604576656e746020656e756d206f6620746869732070616c6c65741d060c4470616c6c65745f746573745f7574696c731870616c6c6574144576656e7404045400010c2856616c756549735365740000003853686f756c64526f6c6c6261636b000100384261746368436f6d706c65746564000200047c54686520604576656e746020656e756d206f6620746869732070616c6c6574210608306672616d655f73797374656d14506861736500010c384170706c7945787472696e736963040010010c7533320000003046696e616c697a6174696f6e00010038496e697469616c697a6174696f6e000200002506000002790300290608306672616d655f73797374656d584c61737452756e74696d6555706772616465496e666f0000080130737065635f76657273696f6ee0014c636f6465633a3a436f6d706163743c7533323e000124737065635f6e616d650106016473705f72756e74696d653a3a52756e74696d65537472696e6700002d060c306672616d655f73797374656d186c696d69747330426c6f636b5765696768747300000c0128626173655f626c6f636b2401185765696768740001246d61785f626c6f636b2401185765696768740001247065725f636c617373310601845065724469737061746368436c6173733c57656967687473506572436c6173733e000031060c346672616d655f737570706f7274206469737061746368405065724469737061746368436c617373040454013506000c01186e6f726d616c350601045400012c6f7065726174696f6e616c35060104540001246d616e6461746f72793506010454000035060c306672616d655f73797374656d186c696d6974733c57656967687473506572436c6173730000100138626173655f65787472696e7369632401185765696768740001346d61785f65787472696e736963390601384f7074696f6e3c5765696768743e0001246d61785f746f74616c390601384f7074696f6e3c5765696768743e0001207265736572766564390601384f7074696f6e3c5765696768743e0000390604184f7074696f6e04045401240108104e6f6e6500000010536f6d6504002400000100003d060c306672616d655f73797374656d186c696d6974732c426c6f636b4c656e677468000004010c6d6178410601545065724469737061746368436c6173733c7533323e000041060c346672616d655f737570706f7274206469737061746368405065724469737061746368436c6173730404540110000c01186e6f726d616c1001045400012c6f7065726174696f6e616c100104540001246d616e6461746f72791001045400004506082873705f776569676874733c52756e74696d6544625765696768740000080110726561642c010c75363400011477726974652c010c75363400004906082873705f76657273696f6e3852756e74696d6556657273696f6e0000200124737065635f6e616d650106013452756e74696d65537472696e67000124696d706c5f6e616d650106013452756e74696d65537472696e67000144617574686f72696e675f76657273696f6e10010c753332000130737065635f76657273696f6e10010c753332000130696d706c5f76657273696f6e10010c753332000110617069734d06011c4170697356656300014c7472616e73616374696f6e5f76657273696f6e10010c75333200013473746174655f76657273696f6e080108753800004d06040c436f77040454015106000400510600000051060000025506005506000004080101100059060c306672616d655f73797374656d1870616c6c6574144572726f720404540001183c496e76616c6964537065634e616d650000081101546865206e616d65206f662073706563696669636174696f6e20646f6573206e6f74206d61746368206265747765656e207468652063757272656e742072756e74696d6550616e6420746865206e65772072756e74696d652e685370656356657273696f6e4e65656473546f496e63726561736500010841015468652073706563696669636174696f6e2076657273696f6e206973206e6f7420616c6c6f77656420746f206465637265617365206265747765656e207468652063757272656e742072756e74696d6550616e6420746865206e65772072756e74696d652e744661696c6564546f4578747261637452756e74696d6556657273696f6e00020cec4661696c656420746f2065787472616374207468652072756e74696d652076657273696f6e2066726f6d20746865206e65772072756e74696d652e0009014569746865722063616c6c696e672060436f72655f76657273696f6e60206f72206465636f64696e67206052756e74696d6556657273696f6e60206661696c65642e4c4e6f6e44656661756c74436f6d706f73697465000304fc537569636964652063616c6c6564207768656e20746865206163636f756e7420686173206e6f6e2d64656661756c7420636f6d706f7369746520646174612e3c4e6f6e5a65726f526566436f756e74000404350154686572652069732061206e6f6e2d7a65726f207265666572656e636520636f756e742070726576656e74696e6720746865206163636f756e742066726f6d206265696e67207075726765642e3043616c6c46696c7465726564000504d0546865206f726967696e2066696c7465722070726576656e74207468652063616c6c20746f20626520646973706174636865642e046c4572726f7220666f72207468652053797374656d2070616c6c65745d0600000261060061060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e7420416e636573746f720404480130000c0138757365645f62616e647769647468650601345573656442616e647769647468000138706172615f686561645f68617368290301244f7074696f6e3c483e000160636f6e73756d65645f676f5f61686561645f7369676e616c7906018c4f7074696f6e3c72656c61795f636861696e3a3a55706772616465476f41686561643e000065060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e74345573656442616e64776964746800000c0134756d705f6d73675f636f756e7410010c75333200013c756d705f746f74616c5f627974657310010c75333200013468726d705f6f7574676f696e676906018c42547265654d61703c5061726149642c2048726d704368616e6e656c5570646174653e00006906042042547265654d617008044b01ad010456016d0600040071060000006d060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e744448726d704368616e6e656c55706461746500000801246d73675f636f756e7410010c75333200012c746f74616c5f627974657310010c75333200007106000002750600750600000408ad016d0600790604184f7074696f6e040454017d060108104e6f6e6500000010536f6d6504007d0600000100007d060c4c706f6c6b61646f745f7072696d6974697665730876353855706772616465476f41686561640001081441626f72740000001c476f41686561640001000081060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e74385365676d656e74547261636b65720404480130000c0138757365645f62616e647769647468650601345573656442616e64776964746800013868726d705f77617465726d61726b250301804f7074696f6e3c72656c61795f636861696e3a3a426c6f636b4e756d6265723e000160636f6e73756d65645f676f5f61686561645f7369676e616c7906018c4f7074696f6e3c72656c61795f636861696e3a3a55706772616465476f41686561643e0000850604184f7074696f6e0404540189060108104e6f6e6500000010536f6d6504008906000001000089060c4c706f6c6b61646f745f7072696d69746976657308763548557067726164655265737472696374696f6e0001041c50726573656e74000000008d060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d5072656c61795f73746174655f736e617073686f74584d6573736167696e675374617465536e617073686f740000100130646d715f6d71635f6865616430014472656c61795f636861696e3a3a4861736800019c72656c61795f64697370617463685f71756575655f72656d61696e696e675f63617061636974799106018c52656c61794469737061746368517565756552656d61696e696e674361706163697479000140696e67726573735f6368616e6e656c73950601885665633c285061726149642c20416272696467656448726d704368616e6e656c293e00013c6567726573735f6368616e6e656c73950601885665633c285061726149642c20416272696467656448726d704368616e6e656c293e000091060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d5072656c61795f73746174655f736e617073686f748c52656c61794469737061746368517565756552656d61696e696e674361706163697479000008013c72656d61696e696e675f636f756e7410010c75333200013872656d61696e696e675f73697a6510010c75333200009506000002990600990600000408ad019d06009d060c4c706f6c6b61646f745f7072696d6974697665730876354c416272696467656448726d704368616e6e656c00001801306d61785f636170616369747910010c7533320001386d61785f746f74616c5f73697a6510010c7533320001406d61785f6d6573736167655f73697a6510010c7533320001246d73675f636f756e7410010c753332000128746f74616c5f73697a6510010c7533320001206d71635f68656164290301304f7074696f6e3c486173683e0000a1060c4c706f6c6b61646f745f7072696d697469766573087635644162726964676564486f7374436f6e66696775726174696f6e00002801346d61785f636f64655f73697a6510010c7533320001486d61785f686561645f646174615f73697a6510010c7533320001586d61785f7570776172645f71756575655f636f756e7410010c7533320001546d61785f7570776172645f71756575655f73697a6510010c75333200015c6d61785f7570776172645f6d6573736167655f73697a6510010c7533320001906d61785f7570776172645f6d6573736167655f6e756d5f7065725f63616e64696461746510010c75333200018868726d705f6d61785f6d6573736167655f6e756d5f7065725f63616e64696461746510010c75333200016c76616c69646174696f6e5f757067726164655f636f6f6c646f776e10012c426c6f636b4e756d62657200016076616c69646174696f6e5f757067726164655f64656c617910012c426c6f636b4e756d6265720001506173796e635f6261636b696e675f706172616d73a506018c73757065723a3a7673746167696e673a3a4173796e634261636b696e67506172616d730000a5060c4c706f6c6b61646f745f7072696d697469766573207673746167696e67484173796e634261636b696e67506172616d73000008014c6d61785f63616e6469646174655f646570746810010c753332000150616c6c6f7765645f616e6365737472795f6c656e10010c7533320000a906089463756d756c75735f7072696d6974697665735f70617261636861696e5f696e686572656e74444d6573736167655175657565436861696e0000040030012452656c6179486173680000ad06042042547265654d617008044b01ad01045601a906000400b106000000b106000002b50600b50600000408ad01a90600b906000002bd0600bd060860706f6c6b61646f745f636f72655f7072696d6974697665734c4f7574626f756e6448726d704d6573736167650408496401ad0100080124726563697069656e74ad01010849640001106461746134015073705f7374643a3a7665633a3a5665633c75383e0000c106087c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d60436f646555706772616465417574686f72697a6174696f6e0404540000080124636f64655f6861736830011c543a3a48617368000134636865636b5f76657273696f6e35010110626f6f6c0000c5060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c6574144572726f720404540001204c4f7665726c617070696e6755706772616465730000041901417474656d707420746f20757067726164652076616c69646174696f6e2066756e6374696f6e207768696c65206578697374696e6720757067726164652070656e64696e672e5050726f686962697465644279506f6c6b61646f740001044d01506f6c6b61646f742063757272656e746c792070726f68696269747320746869732070617261636861696e2066726f6d20757067726164696e67206974732076616c69646174696f6e2066756e6374696f6e2e18546f6f426967000208450154686520737570706c6965642076616c69646174696f6e2066756e6374696f6e2068617320636f6d70696c656420696e746f206120626c6f62206c6172676572207468616e20506f6c6b61646f742069733c77696c6c696e6720746f2072756e2e6856616c69646174696f6e446174614e6f74417661696c61626c650003041d0154686520696e686572656e7420776869636820737570706c696573207468652076616c69646174696f6e206461746120646964206e6f742072756e207468697320626c6f636b2e74486f7374436f6e66696775726174696f6e4e6f74417661696c61626c65000404290154686520696e686572656e7420776869636820737570706c6965732074686520686f737420636f6e66696775726174696f6e20646964206e6f742072756e207468697320626c6f636b2e304e6f745363686564756c6564000504d84e6f2076616c69646174696f6e2066756e6374696f6e20757067726164652069732063757272656e746c79207363686564756c65642e444e6f7468696e67417574686f72697a6564000604904e6f20636f6465207570677261646520686173206265656e20617574686f72697a65642e30556e617574686f72697a6564000704bc54686520676976656e20636f6465207570677261646520686173206e6f74206265656e20617574686f72697a65642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ec9060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400e50101185665633c543e0000cd060c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c6574144572726f7204045400013444546f6f4d616e7943616e646964617465730000044c546f6f206d616e792063616e646964617465731c556e6b6e6f776e00010434556e6b6e6f776e206572726f72285065726d697373696f6e000204405065726d697373696f6e20697373756554416c7265616479486f6c64696e674c6963656e7365000304945573657220616c726561647920686f6c6473206c6963656e736520746f20636f6c6c617465244e6f4c6963656e73650004049c5573657220646f6573206e6f7420686f6c642061206c6963656e736520746f20636f6c6c61746540416c726561647943616e6469646174650005046c5573657220697320616c726561647920612063616e646964617465304e6f7443616e6469646174650006045c55736572206973206e6f7420612063616e64696461746550546f6f4d616e79496e76756c6e657261626c657300070458546f6f206d616e7920696e76756c6e657261626c65734c546f6f466577496e76756c6e657261626c657300080454546f6f2066657720696e76756c6e657261626c65734c416c7265616479496e76756c6e657261626c650009047c5573657220697320616c726561647920616e20496e76756c6e657261626c653c4e6f74496e76756c6e657261626c65000a046c55736572206973206e6f7420616e20496e76756c6e657261626c655c4e6f4173736f63696174656456616c696461746f724964000b04984163636f756e7420686173206e6f206173736f6369617465642076616c696461746f722049445856616c696461746f724e6f7452656769737465726564000c048856616c696461746f72204944206973206e6f74207965742072656769737465726564048054686520604572726f726020656e756d206f6620746869732070616c6c65742ed106000002d50600d5060000040800cd0100d90600000408dd063400dd060c1c73705f636f72651863727970746f244b65795479706549640000040044011c5b75383b20345d0000e1060c3870616c6c65745f73657373696f6e1870616c6c6574144572726f7204045400011430496e76616c696450726f6f6600000460496e76616c6964206f776e6572736869702070726f6f662e5c4e6f4173736f63696174656456616c696461746f7249640001049c4e6f206173736f6369617465642076616c696461746f7220494420666f72206163636f756e742e344475706c6963617465644b65790002046452656769737465726564206475706c6963617465206b65792e184e6f4b657973000304a44e6f206b65797320617265206173736f63696174656420776974682074686973206163636f756e742e244e6f4163636f756e7400040419014b65792073657474696e67206163636f756e74206973206e6f74206c6976652c20736f206974277320696d706f737369626c6520746f206173736f6369617465206b6579732e04744572726f7220666f72207468652073657373696f6e2070616c6c65742ee5060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401d101045300000400e90601185665633c543e0000e906000002d10100ed06084873705f636f6e73656e7375735f736c6f747310536c6f74000004002c010c7536340000f10600000408ed061000f5060c4c626f756e6465645f636f6c6c656374696f6e73407765616b5f626f756e6465645f766563385765616b426f756e64656456656308045401f906045300000400010701185665633c543e0000f9060c3c70616c6c65745f62616c616e6365731474797065732c42616c616e63654c6f636b041c42616c616e63650118000c01086964010101384c6f636b4964656e746966696572000118616d6f756e7418011c42616c616e636500011c726561736f6e73fd06011c526561736f6e730000fd060c3c70616c6c65745f62616c616e6365731474797065731c526561736f6e7300010c0c466565000000104d6973630001000c416c6c000200000107000002f9060005070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540109070453000004000d0701185665633c543e000009070c3c70616c6c65745f62616c616e6365731474797065732c52657365727665446174610844526573657276654964656e7469666965720105011c42616c616e6365011800080108696405010144526573657276654964656e746966696572000118616d6f756e7418011c42616c616e636500000d0700000209070011070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454011507045300000400210701185665633c543e000015070c3c70616c6c65745f62616c616e636573147479706573204964416d6f756e74080849640119071c42616c616e63650118000801086964190701084964000118616d6f756e7418011c42616c616e63650000190708306f70616c5f72756e74696d654452756e74696d65486f6c64526561736f6e00010444436f6c6c61746f7253656c656374696f6e04001d07019470616c6c65745f636f6c6c61746f725f73656c656374696f6e3a3a486f6c64526561736f6e001700001d070c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c657428486f6c64526561736f6e0001042c4c6963656e7365426f6e6400000000210700000215070025070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540129070453000004002d0701185665633c543e000029070c3c70616c6c65745f62616c616e636573147479706573204964416d6f756e74080849640105011c42616c616e63650118000801086964050101084964000118616d6f756e7418011c42616c616e636500002d0700000229070031070c3c70616c6c65745f62616c616e6365731870616c6c6574144572726f720804540004490001283856657374696e6742616c616e63650000049c56657374696e672062616c616e636520746f6f206869676820746f2073656e642076616c75652e544c69717569646974795265737472696374696f6e73000104c84163636f756e74206c6971756964697479207265737472696374696f6e732070726576656e74207769746864726177616c2e4c496e73756666696369656e7442616c616e63650002047842616c616e636520746f6f206c6f7720746f2073656e642076616c75652e484578697374656e7469616c4465706f736974000304ec56616c756520746f6f206c6f7720746f20637265617465206163636f756e742064756520746f206578697374656e7469616c206465706f7369742e34457870656e646162696c697479000404905472616e736665722f7061796d656e7420776f756c64206b696c6c206163636f756e742e5c4578697374696e6756657374696e675363686564756c65000504cc412076657374696e67207363686564756c6520616c72656164792065786973747320666f722074686973206163636f756e742e2c446561644163636f756e740006048c42656e6566696369617279206163636f756e74206d757374207072652d65786973742e3c546f6f4d616e795265736572766573000704b84e756d626572206f66206e616d65642072657365727665732065786365656420604d61785265736572766573602e30546f6f4d616e79486f6c6473000804884e756d626572206f6620686f6c64732065786365656420604d6178486f6c6473602e38546f6f4d616e79467265657a6573000904984e756d626572206f6620667265657a65732065786365656420604d6178467265657a6573602e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e35070c3473705f61726974686d657469632c66697865645f706f696e7424466978656455313238000004001801107531323800003907086870616c6c65745f7472616e73616374696f6e5f7061796d656e742052656c6561736573000108245631416e6369656e74000000085632000100003d07083c70616c6c65745f74726561737572792050726f706f73616c08244163636f756e74496401001c42616c616e636501180010012070726f706f7365720001244163636f756e74496400011476616c756518011c42616c616e636500012c62656e65666963696172790001244163636f756e744964000110626f6e6418011c42616c616e6365000041070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540110045300000400310501185665633c543e000045070c3473705f61726974686d65746963287065725f7468696e67731c5065726d696c6c0000040010010c7533320000490708346672616d655f737570706f72742050616c6c65744964000004000101011c5b75383b20385d00004d070c3c70616c6c65745f74726561737572791870616c6c6574144572726f7208045400044900011470496e73756666696369656e7450726f706f7365727342616c616e63650000047850726f706f73657227732062616c616e636520697320746f6f206c6f772e30496e76616c6964496e646578000104904e6f2070726f706f73616c206f7220626f756e7479206174207468617420696e6465782e40546f6f4d616e79417070726f76616c7300020480546f6f206d616e7920617070726f76616c7320696e207468652071756575652e58496e73756666696369656e745065726d697373696f6e0003084501546865207370656e64206f726967696e2069732076616c6964206275742074686520616d6f756e7420697420697320616c6c6f77656420746f207370656e64206973206c6f776572207468616e207468654c616d6f756e7420746f206265207370656e742e4c50726f706f73616c4e6f74417070726f7665640004047c50726f706f73616c20686173206e6f74206265656e20617070726f7665642e04784572726f7220666f72207468652074726561737572792070616c6c65742e51070c2c70616c6c65745f7375646f1870616c6c6574144572726f720404540001042c526571756972655375646f0000047c53656e646572206d75737420626520746865205375646f206163636f756e7404644572726f7220666f7220746865205375646f2070616c6c657455070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401b8045300000400f90101185665633c543e000059070c306f726d6c5f76657374696e67186d6f64756c65144572726f72040454000118445a65726f56657374696e67506572696f640000045856657374696e6720706572696f64206973207a65726f585a65726f56657374696e67506572696f64436f756e740001045c4e756d626572206f66207665737473206973207a65726f64496e73756666696369656e7442616c616e6365546f4c6f636b00020498496e73756666696369656e7420616d6f756e74206f662062616c616e636520746f206c6f636b5c546f6f4d616e7956657374696e675363686564756c6573000304b054686973206163636f756e74206861766520746f6f206d616e792076657374696e67207363686564756c657324416d6f756e744c6f770004049454686520766573746564207472616e7366657220616d6f756e7420697320746f6f206c6f776c4d617856657374696e675363686564756c65734578636565646564000504e44661696c6564206265636175736520746865206d6178696d756d2076657374696e67207363686564756c657320776173206578636565646564048054686520604572726f726020656e756d206f6620746869732070616c6c65742e5d070c306f726d6c5f78746f6b656e73186d6f64756c65144572726f7204045400014c4441737365744861734e6f5265736572766500000478417373657420686173206e6f2072657365727665206c6f636174696f6e2e544e6f7443726f7373436861696e5472616e73666572000104644e6f742063726f73732d636861696e207472616e736665722e2c496e76616c69644465737400020474496e76616c6964207472616e736665722064657374696e6174696f6e2e844e6f7443726f7373436861696e5472616e7366657261626c6543757272656e6379000304a443757272656e6379206973206e6f742063726f73732d636861696e207472616e7366657261626c652e48556e776569676861626c654d657373616765000404b4546865206d65737361676527732077656967687420636f756c64206e6f742062652064657465726d696e65642e4858636d457865637574696f6e4661696c65640005045458434d20657865637574696f6e206661696c65642e3843616e6e6f745265616e63686f72000608e8436f756c64206e6f742072652d616e63686f72207468652061737365747320746f206465636c61726520746865206665657320666f72207468654864657374696e6174696f6e20636861696e2e3c496e76616c6964416e636573747279000704c4436f756c64206e6f742067657420616e636573747279206f662061737365742072657365727665206c6f636174696f6e2e30496e76616c6964417373657400080468546865204d756c7469417373657420697320696e76616c69642e6044657374696e6174696f6e4e6f74496e7665727469626c65000904f05468652064657374696e6174696f6e20604d756c74694c6f636174696f6e602070726f76696465642063616e6e6f7420626520696e7665727465642e2842616456657273696f6e000a08ec5468652076657273696f6e206f6620746865206056657273696f6e6564602076616c75652075736564206973206e6f742061626c6520746f20626530696e7465727072657465642e7444697374696e637452657365727665466f724173736574416e64466565000b08fc57652074726965642073656e64696e672064697374696e637420617373657420616e6420666565206275742074686579206861766520646966666572656e743c7265736572766520636861696e732e1c5a65726f466565000c044054686520666565206973207a65726f2e285a65726f416d6f756e74000d0494546865207472616e73666572696e6720617373657420616d6f756e74206973207a65726f2e58546f6f4d616e794173736574734265696e6753656e74000e04d0546865206e756d626572206f662061737365747320746f2062652073656e74206973206f76657220746865206d6178696d756d2e544173736574496e6465784e6f6e4578697374656e74000f04ec5468652073706563696669656420696e64657820646f6573206e6f7420657869737420696e2061204d756c7469417373657473207374727563742e304665654e6f74456e6f75676800100448466565206973206e6f7420656e6f7567682e644e6f74537570706f727465644d756c74694c6f636174696f6e0011046c4e6f7420737570706f72746564204d756c74694c6f636174696f6e4c4d696e58636d4665654e6f74446566696e6564001204d44d696e58636d466565206e6f74207265676973746572656420666f72206365727461696e2072657365727665206c6f636174696f6e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e610700000408000d010065070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540169070453000004006d0701185665633c543e00006907082c6f726d6c5f746f6b656e732c42616c616e63654c6f636b041c42616c616e63650118000801086964010101384c6f636b4964656e746966696572000118616d6f756e7418011c42616c616e636500006d070000026907007107082c6f726d6c5f746f6b656e732c4163636f756e7444617461041c42616c616e63650118000c01106672656518011c42616c616e6365000120726573657276656418011c42616c616e636500011866726f7a656e18011c42616c616e6365000075070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540179070453000004007d0701185665633c543e00007907082c6f726d6c5f746f6b656e732c52657365727665446174610844526573657276654964656e74696669657201ac1c42616c616e63650118000801086964ac0144526573657276654964656e746966696572000118616d6f756e7418011c42616c616e636500007d0700000279070081070c2c6f726d6c5f746f6b656e73186d6f64756c65144572726f720404540001203442616c616e6365546f6f4c6f77000004585468652062616c616e636520697320746f6f206c6f775c416d6f756e74496e746f42616c616e63654661696c65640001049c43616e6e6f7420636f6e7665727420416d6f756e7420696e746f2042616c616e63652074797065544c69717569646974795265737472696374696f6e73000204d04661696c65642062656361757365206c6971756964697479207265737472696374696f6e732064756520746f206c6f636b696e67404d61784c6f636b734578636565646564000304b44661696c6564206265636175736520746865206d6178696d756d206c6f636b7320776173206578636565646564244b656570416c6976650004048c5472616e736665722f7061796d656e7420776f756c64206b696c6c206163636f756e74484578697374656e7469616c4465706f736974000504e856616c756520746f6f206c6f7720746f20637265617465206163636f756e742064756520746f206578697374656e7469616c206465706f7369742c446561644163636f756e740006048842656e6566696369617279206163636f756e74206d757374207072652d65786973743c546f6f4d616e795265736572766573000700048054686520604572726f726020656e756d206f6620746869732070616c6c65742e8507000004081889070089070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400e50101185665633c543e00008d070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454019107045300000400990701185665633c543e0000910704184f7074696f6e0404540195070108104e6f6e6500000010536f6d6504009507000001000095070c3c70616c6c65745f6964656e7469747914747970657334526567697374726172496e666f081c42616c616e63650118244163636f756e7449640100000c011c6163636f756e740001244163636f756e74496400010c66656518011c42616c616e63650001186669656c6473e50201384964656e746974794669656c6473000099070000029107009d070c3c70616c6c65745f6964656e746974791870616c6c6574144572726f7204045400014848546f6f4d616e795375624163636f756e74730000045c546f6f206d616e7920737562732d6163636f756e74732e204e6f74466f756e64000104504163636f756e742069736e277420666f756e642e204e6f744e616d6564000204504163636f756e742069736e2774206e616d65642e28456d707479496e64657800030430456d70747920696e6465782e284665654368616e6765640004043c466565206973206368616e6765642e284e6f4964656e74697479000504484e6f206964656e7469747920666f756e642e3c537469636b794a756467656d656e7400060444537469636b79206a756467656d656e742e384a756467656d656e74476976656e000704404a756467656d656e7420676976656e2e40496e76616c69644a756467656d656e7400080448496e76616c6964206a756467656d656e742e30496e76616c6964496e6465780009045454686520696e64657820697320696e76616c69642e34496e76616c6964546172676574000a04585468652074617267657420697320696e76616c69642e34546f6f4d616e794669656c6473000b046c546f6f206d616e79206164646974696f6e616c206669656c64732e44546f6f4d616e7952656769737472617273000c04e84d6178696d756d20616d6f756e74206f66207265676973747261727320726561636865642e2043616e6e6f742061646420616e79206d6f72652e38416c7265616479436c61696d6564000d04704163636f756e7420494420697320616c7265616479206e616d65642e184e6f74537562000e047053656e646572206973206e6f742061207375622d6163636f756e742e204e6f744f776e6564000f04885375622d6163636f756e742069736e2774206f776e65642062792073656e6465722e744a756467656d656e74466f72446966666572656e744964656e74697479001004d05468652070726f7669646564206a756467656d656e742077617320666f72206120646966666572656e74206964656e746974792e584a756467656d656e745061796d656e744661696c6564001104f84572726f722074686174206f6363757273207768656e20746865726520697320616e20697373756520706179696e6720666f72206a756467656d656e742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ea107083c70616c6c65745f707265696d616765345265717565737453746174757308244163636f756e74496401001c42616c616e6365011801082c556e72657175657374656408011c6465706f736974a5070150284163636f756e7449642c2042616c616e63652900010c6c656e10010c753332000000245265717565737465640c011c6465706f736974a90701704f7074696f6e3c284163636f756e7449642c2042616c616e6365293e000114636f756e7410010c75333200010c6c656e2503012c4f7074696f6e3c7533323e00010000a50700000408001800a90704184f7074696f6e04045401a5070108104e6f6e6500000010536f6d650400a5070000010000ad0700000408301000b1070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000b5070c3c70616c6c65745f707265696d6167651870616c6c6574144572726f7204045400011818546f6f426967000004a0507265696d61676520697320746f6f206c6172676520746f2073746f7265206f6e2d636861696e2e30416c72656164794e6f746564000104a4507265696d6167652068617320616c7265616479206265656e206e6f746564206f6e2d636861696e2e344e6f74417574686f72697a6564000204c85468652075736572206973206e6f7420617574686f72697a656420746f20706572666f726d207468697320616374696f6e2e204e6f744e6f746564000304fc54686520707265696d6167652063616e6e6f742062652072656d6f7665642073696e636520697420686173206e6f7420796574206265656e206e6f7465642e2452657175657374656400040409014120707265696d616765206d6179206e6f742062652072656d6f766564207768656e20746865726520617265206f75747374616e64696e672072657175657374732e304e6f745265717565737465640005042d0154686520707265696d61676520726571756573742063616e6e6f742062652072656d6f7665642073696e6365206e6f206f75747374616e64696e672072657175657374732065786973742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742eb9070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401bd07045300000400c10701185665633c543e0000bd070000040c1059010000c107000002bd0700c50700000408c9071800c9070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400e50101185665633c543e0000cd070c4070616c6c65745f64656d6f6372616379147479706573385265666572656e64756d496e666f0c2c426c6f636b4e756d62657201102050726f706f73616c0159011c42616c616e6365011801081c4f6e676f696e670400d10701c05265666572656e64756d5374617475733c426c6f636b4e756d6265722c2050726f706f73616c2c2042616c616e63653e0000002046696e6973686564080120617070726f76656435010110626f6f6c00010c656e6410012c426c6f636b4e756d62657200010000d1070c4070616c6c65745f64656d6f6372616379147479706573405265666572656e64756d5374617475730c2c426c6f636b4e756d62657201102050726f706f73616c0159011c42616c616e636501180014010c656e6410012c426c6f636b4e756d62657200012070726f706f73616c5901012050726f706f73616c0001247468726573686f6c6421010134566f74655468726573686f6c6400011464656c617910012c426c6f636b4e756d62657200011474616c6c79d507013854616c6c793c42616c616e63653e0000d5070c4070616c6c65745f64656d6f63726163791474797065731454616c6c79041c42616c616e63650118000c01106179657318011c42616c616e63650001106e61797318011c42616c616e636500011c7475726e6f757418011c42616c616e63650000d9070c4070616c6c65745f64656d6f637261637910766f746518566f74696e67101c42616c616e63650118244163636f756e74496401002c426c6f636b4e756d6265720110204d6178566f746573000108184469726563740c0114766f746573dd0701f4426f756e6465645665633c285265666572656e64756d496e6465782c204163636f756e74566f74653c42616c616e63653e292c204d6178566f7465733e00012c64656c65676174696f6e73e907015044656c65676174696f6e733c42616c616e63653e0001147072696f72ed07017c5072696f724c6f636b3c426c6f636b4e756d6265722c2042616c616e63653e0000002844656c65676174696e6714011c62616c616e636518011c42616c616e63650001187461726765740001244163636f756e744964000128636f6e76696374696f6e21030128436f6e76696374696f6e00012c64656c65676174696f6e73e907015044656c65676174696f6e733c42616c616e63653e0001147072696f72ed07017c5072696f724c6f636b3c426c6f636b4e756d6265722c2042616c616e63653e00010000dd070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401e107045300000400e50701185665633c543e0000e1070000040810250100e507000002e10700e9070c4070616c6c65745f64656d6f63726163791474797065732c44656c65676174696f6e73041c42616c616e6365011800080114766f74657318011c42616c616e636500011c6361706974616c18011c42616c616e63650000ed070c4070616c6c65745f64656d6f637261637910766f7465245072696f724c6f636b082c426c6f636b4e756d62657201101c42616c616e6365011800080010012c426c6f636b4e756d626572000018011c42616c616e63650000f107000004085901210100f5070000040810c90700f9070c4070616c6c65745f64656d6f63726163791870616c6c6574144572726f720404540001602056616c75654c6f770000043456616c756520746f6f206c6f773c50726f706f73616c4d697373696e670001045c50726f706f73616c20646f6573206e6f742065786973743c416c726561647943616e63656c65640002049443616e6e6f742063616e63656c207468652073616d652070726f706f73616c207477696365444475706c696361746550726f706f73616c0003045450726f706f73616c20616c7265616479206d6164654c50726f706f73616c426c61636b6c69737465640004046850726f706f73616c207374696c6c20626c61636b6c6973746564444e6f7453696d706c654d616a6f72697479000504a84e6578742065787465726e616c2070726f706f73616c206e6f742073696d706c65206d616a6f726974792c496e76616c69644861736800060430496e76616c69642068617368284e6f50726f706f73616c000704504e6f2065787465726e616c2070726f706f73616c34416c72656164795665746f6564000804984964656e74697479206d6179206e6f74207665746f20612070726f706f73616c207477696365445265666572656e64756d496e76616c696400090484566f746520676976656e20666f7220696e76616c6964207265666572656e64756d2c4e6f6e6557616974696e67000a04504e6f2070726f706f73616c732077616974696e67204e6f74566f746572000b04c454686520676976656e206163636f756e7420646964206e6f7420766f7465206f6e20746865207265666572656e64756d2e304e6f5065726d697373696f6e000c04c8546865206163746f7220686173206e6f207065726d697373696f6e20746f20636f6e647563742074686520616374696f6e2e44416c726561647944656c65676174696e67000d0488546865206163636f756e7420697320616c72656164792064656c65676174696e672e44496e73756666696369656e7446756e6473000e04fc546f6f206869676820612062616c616e6365207761732070726f7669646564207468617420746865206163636f756e742063616e6e6f74206166666f72642e344e6f7444656c65676174696e67000f04a0546865206163636f756e74206973206e6f742063757272656e746c792064656c65676174696e672e28566f74657345786973740010085501546865206163636f756e742063757272656e746c792068617320766f74657320617474616368656420746f20697420616e6420746865206f7065726174696f6e2063616e6e6f74207375636365656420756e74696ce87468657365206172652072656d6f7665642c20656974686572207468726f7567682060756e766f746560206f722060726561705f766f7465602e44496e7374616e744e6f74416c6c6f776564001104d854686520696e7374616e74207265666572656e64756d206f726967696e2069732063757272656e746c7920646973616c6c6f7765642e204e6f6e73656e73650012049444656c65676174696f6e20746f206f6e6573656c66206d616b6573206e6f2073656e73652e3c57726f6e675570706572426f756e6400130450496e76616c696420757070657220626f756e642e3c4d6178566f74657352656163686564001404804d6178696d756d206e756d626572206f6620766f74657320726561636865642e1c546f6f4d616e79001504804d6178696d756d206e756d626572206f66206974656d7320726561636865642e3c566f74696e67506572696f644c6f7700160454566f74696e6720706572696f6420746f6f206c6f7740507265696d6167654e6f7445786973740017047054686520707265696d61676520646f6573206e6f742065786973742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742efd070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401300453000004005d0501185665633c543e00000108084470616c6c65745f636f6c6c65637469766514566f74657308244163636f756e74496401002c426c6f636b4e756d626572011000140114696e64657810013450726f706f73616c496e6465780001247468726573686f6c6410012c4d656d626572436f756e7400011061796573e50101385665633c4163636f756e7449643e0001106e617973e50101385665633c4163636f756e7449643e00010c656e6410012c426c6f636b4e756d626572000005080c4470616c6c65745f636f6c6c6563746976651870616c6c6574144572726f7208045400044900012c244e6f744d656d6265720000045c4163636f756e74206973206e6f742061206d656d626572444475706c696361746550726f706f73616c0001047c4475706c69636174652070726f706f73616c73206e6f7420616c6c6f7765643c50726f706f73616c4d697373696e670002044c50726f706f73616c206d7573742065786973742857726f6e67496e646578000304404d69736d61746368656420696e646578344475706c6963617465566f7465000404584475706c696361746520766f74652069676e6f72656448416c7265616479496e697469616c697a6564000504804d656d626572732061726520616c726561647920696e697469616c697a65642120546f6f4561726c79000604010154686520636c6f73652063616c6c20776173206d61646520746f6f206561726c792c206265666f72652074686520656e64206f662074686520766f74696e672e40546f6f4d616e7950726f706f73616c73000704fc54686572652063616e206f6e6c792062652061206d6178696d756d206f6620604d617850726f706f73616c7360206163746976652070726f706f73616c732e4c57726f6e6750726f706f73616c576569676874000804d054686520676976656e2077656967687420626f756e6420666f72207468652070726f706f73616c2077617320746f6f206c6f772e4c57726f6e6750726f706f73616c4c656e677468000904d054686520676976656e206c656e67746820626f756e6420666f72207468652070726f706f73616c2077617320746f6f206c6f772e545072696d654163636f756e744e6f744d656d626572000a04745072696d65206163636f756e74206973206e6f742061206d656d626572048054686520604572726f726020656e756d206f6620746869732070616c6c65742e09080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401300453000004005d0501185665633c543e00000d080c4470616c6c65745f636f6c6c6563746976651870616c6c6574144572726f7208045400044900012c244e6f744d656d6265720000045c4163636f756e74206973206e6f742061206d656d626572444475706c696361746550726f706f73616c0001047c4475706c69636174652070726f706f73616c73206e6f7420616c6c6f7765643c50726f706f73616c4d697373696e670002044c50726f706f73616c206d7573742065786973742857726f6e67496e646578000304404d69736d61746368656420696e646578344475706c6963617465566f7465000404584475706c696361746520766f74652069676e6f72656448416c7265616479496e697469616c697a6564000504804d656d626572732061726520616c726561647920696e697469616c697a65642120546f6f4561726c79000604010154686520636c6f73652063616c6c20776173206d61646520746f6f206561726c792c206265666f72652074686520656e64206f662074686520766f74696e672e40546f6f4d616e7950726f706f73616c73000704fc54686572652063616e206f6e6c792062652061206d6178696d756d206f6620604d617850726f706f73616c7360206163746976652070726f706f73616c732e4c57726f6e6750726f706f73616c576569676874000804d054686520676976656e2077656967687420626f756e6420666f72207468652070726f706f73616c2077617320746f6f206c6f772e4c57726f6e6750726f706f73616c4c656e677468000904d054686520676976656e206c656e67746820626f756e6420666f72207468652070726f706f73616c2077617320746f6f206c6f772e545072696d654163636f756e744e6f744d656d626572000a04745072696d65206163636f756e74206973206e6f742061206d656d626572048054686520604572726f726020656e756d206f6620746869732070616c6c65742e11080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400e50101185665633c543e000015080c4470616c6c65745f6d656d626572736869701870616c6c6574144572726f7208045400044900010c34416c72656164794d656d62657200000444416c72656164792061206d656d6265722e244e6f744d656d626572000104344e6f742061206d656d6265722e38546f6f4d616e794d656d6265727300020444546f6f206d616e79206d656d626572732e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e19080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400e50101185665633c543e00001d080c4470616c6c65745f6d656d626572736869701870616c6c6574144572726f7208045400044900010c34416c72656164794d656d62657200000444416c72656164792061206d656d6265722e244e6f744d656d626572000104344e6f742061206d656d6265722e38546f6f4d616e794d656d6265727300020444546f6f206d616e79206d656d626572732e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e2108086070616c6c65745f72616e6b65645f636f6c6c656374697665304d656d6265725265636f7264000004011072616e6b4901011052616e6b000025080000040849010000290800000408490110002d080000040810000031080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e000035080c6070616c6c65745f72616e6b65645f636f6c6c6563746976651870616c6c6574144572726f7208045400044900012434416c72656164794d656d626572000004704163636f756e7420697320616c72656164792061206d656d6265722e244e6f744d656d626572000104604163636f756e74206973206e6f742061206d656d6265722e284e6f74506f6c6c696e67000204b854686520676976656e20706f6c6c20696e64657820697320756e6b6e6f776e206f722068617320636c6f7365642e1c4f6e676f696e670003048054686520676976656e20706f6c6c206973207374696c6c206f6e676f696e672e344e6f6e6552656d61696e696e67000404ac546865726520617265206e6f2066757274686572207265636f72647320746f2062652072656d6f7665642e28436f7272757074696f6e00050468556e6578706563746564206572726f7220696e2073746174652e2852616e6b546f6f4c6f7700060494546865206d656d62657227732072616e6b20697320746f6f206c6f7720746f20766f74652e38496e76616c69645769746e6573730007049854686520696e666f726d6174696f6e2070726f766964656420697320696e636f72726563742e304e6f5065726d697373696f6e000804f8546865206f726967696e206973206e6f742073756666696369656e746c792070726976696c6567656420746f20646f20746865206f7065726174696f6e2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e39080c4070616c6c65745f7265666572656e6461147479706573385265666572656e64756d496e666f201c547261636b49640149013452756e74696d654f726967696e014503184d6f6d656e7401101043616c6c0159011c42616c616e636501181454616c6c79015101244163636f756e74496401003c5363686564756c654164647265737301790301181c4f6e676f696e6704003d08018d015265666572656e64756d5374617475733c547261636b49642c2052756e74696d654f726967696e2c204d6f6d656e742c2043616c6c2c2042616c616e63652c2054616c6c792c0a4163636f756e7449642c205363686564756c65416464726573732c3e00000020417070726f7665640c001001184d6f6d656e7400004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e00004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e0001002052656a65637465640c001001184d6f6d656e7400004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e00004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e0002002443616e63656c6c65640c001001184d6f6d656e7400004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e00004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e0003002054696d65644f75740c001001184d6f6d656e7400004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e00004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e000400184b696c6c656404001001184d6f6d656e74000500003d080c4070616c6c65745f7265666572656e6461147479706573405265666572656e64756d537461747573201c547261636b49640149013452756e74696d654f726967696e014503184d6f6d656e7401101043616c6c0159011c42616c616e636501181454616c6c79015101244163636f756e74496401003c5363686564756c6541646472657373017903002c0114747261636b4901011c547261636b49640001186f726967696e4503013452756e74696d654f726967696e00012070726f706f73616c5901011043616c6c000124656e6163746d656e746d030150446973706174636854696d653c4d6f6d656e743e0001247375626d69747465641001184d6f6d656e740001487375626d697373696f6e5f6465706f7369744108016c4465706f7369743c4163636f756e7449642c2042616c616e63653e0001406465636973696f6e5f6465706f7369744508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e0001206465636964696e67490801784f7074696f6e3c4465636964696e675374617475733c4d6f6d656e743e3e00011474616c6c795101011454616c6c79000120696e5f717565756535010110626f6f6c000114616c61726d510801844f7074696f6e3c284d6f6d656e742c205363686564756c6541646472657373293e000041080c4070616c6c65745f7265666572656e64611474797065731c4465706f73697408244163636f756e74496401001c42616c616e636501180008010c77686f0001244163636f756e744964000118616d6f756e7418011c42616c616e63650000450804184f7074696f6e0404540141080108104e6f6e6500000010536f6d65040041080000010000490804184f7074696f6e040454014d080108104e6f6e6500000010536f6d6504004d0800000100004d080c4070616c6c65745f7265666572656e6461147479706573384465636964696e67537461747573042c426c6f636b4e756d62657201100008011473696e636510012c426c6f636b4e756d626572000128636f6e6669726d696e672503014c4f7074696f6e3c426c6f636b4e756d6265723e0000510804184f7074696f6e0404540155080108104e6f6e6500000010536f6d650400550800000100005508000004081079030059080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454017903045300000400250601185665633c543e00005d08000002610800610800000408490165080065080c4070616c6c65745f7265666572656e646114747970657324547261636b496e666f081c42616c616e63650118184d6f6d656e740110002401106e616d65010601302627737461746963207374720001306d61785f6465636964696e6710010c7533320001406465636973696f6e5f6465706f73697418011c42616c616e6365000138707265706172655f706572696f641001184d6f6d656e7400013c6465636973696f6e5f706572696f641001184d6f6d656e74000138636f6e6669726d5f706572696f641001184d6f6d656e740001506d696e5f656e6163746d656e745f706572696f641001184d6f6d656e740001306d696e5f617070726f76616c69080114437572766500012c6d696e5f737570706f7274690801144375727665000069080c4070616c6c65745f7265666572656e646114747970657314437572766500010c404c696e65617244656372656173696e670c01186c656e6774681d05011c50657262696c6c000114666c6f6f721d05011c50657262696c6c0001106365696c1d05011c50657262696c6c000000445374657070656444656372656173696e67100114626567696e1d05011c50657262696c6c00010c656e641d05011c50657262696c6c000110737465701d05011c50657262696c6c000118706572696f641d05011c50657262696c6c000100285265636970726f63616c0c0118666163746f726d0801204669786564493634000120785f6f66667365746d0801204669786564493634000120795f6f66667365746d0801204669786564493634000200006d080c3473705f61726974686d657469632c66697865645f706f696e74204669786564493634000004007108010c693634000071080000050c0075080c4070616c6c65745f7265666572656e64611870616c6c6574144572726f72080454000449000134284e6f744f6e676f696e67000004685265666572656e64756d206973206e6f74206f6e676f696e672e284861734465706f736974000104b85265666572656e64756d2773206465636973696f6e206465706f73697420697320616c726561647920706169642e20426164547261636b0002049c54686520747261636b206964656e74696669657220676976656e2077617320696e76616c69642e1046756c6c000304310154686572652061726520616c726561647920612066756c6c20636f6d706c656d656e74206f66207265666572656e646120696e2070726f677265737320666f72207468697320747261636b2e285175657565456d70747900040480546865207175657565206f662074686520747261636b20697320656d7074792e344261645265666572656e64756d000504e4546865207265666572656e64756d20696e6465782070726f766964656420697320696e76616c696420696e207468697320636f6e746578742e2c4e6f7468696e67546f446f000604ac546865726520776173206e6f7468696e6720746f20646f20696e2074686520616476616e63656d656e742e1c4e6f547261636b000704a04e6f20747261636b2065786973747320666f72207468652070726f706f73616c206f726967696e2e28556e66696e69736865640008040101416e79206465706f7369742063616e6e6f7420626520726566756e64656420756e74696c20616674657220746865206465636973696f6e206973206f7665722e304e6f5065726d697373696f6e000904a8546865206465706f73697420726566756e646572206973206e6f7420746865206465706f7369746f722e244e6f4465706f736974000a04cc546865206465706f7369742063616e6e6f7420626520726566756e6465642073696e6365206e6f6e6520776173206d6164652e24426164537461747573000b04d0546865207265666572656e64756d2073746174757320697320696e76616c696420666f722074686973206f7065726174696f6e2e40507265696d6167654e6f744578697374000c047054686520707265696d61676520646f6573206e6f742065786973742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e79080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454017d08045300000400850801185665633c543e00007d0804184f7074696f6e0404540181080108104e6f6e6500000010536f6d650400810800000100008108084070616c6c65745f7363686564756c6572245363686564756c656414104e616d6501041043616c6c0159012c426c6f636b4e756d62657201103450616c6c6574734f726967696e014503244163636f756e7449640100001401206d617962655f69648801304f7074696f6e3c4e616d653e0001207072696f726974790801487363686564756c653a3a5072696f7269747900011063616c6c5901011043616c6c0001386d617962655f706572696f646963750301944f7074696f6e3c7363686564756c653a3a506572696f643c426c6f636b4e756d6265723e3e0001186f726967696e4503013450616c6c6574734f726967696e000085080000027d080089080c4070616c6c65745f7363686564756c65721870616c6c6574144572726f72040454000114404661696c6564546f5363686564756c65000004644661696c656420746f207363686564756c6520612063616c6c204e6f74466f756e640001047c43616e6e6f742066696e6420746865207363686564756c65642063616c6c2e5c546172676574426c6f636b4e756d626572496e50617374000204a4476976656e2074617267657420626c6f636b206e756d62657220697320696e2074686520706173742e4852657363686564756c654e6f4368616e6765000304f052657363686564756c65206661696c6564206265636175736520697420646f6573206e6f74206368616e6765207363686564756c65642074696d652e144e616d6564000404d0417474656d707420746f207573652061206e6f6e2d6e616d65642066756e6374696f6e206f6e2061206e616d6564207461736b2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e8d080000029108009108086463756d756c75735f70616c6c65745f78636d705f717565756554496e626f756e644368616e6e656c44657461696c7300000c011873656e646572ad010118506172614964000114737461746595080130496e626f756e6453746174650001406d6573736167655f6d65746164617461990801a85665633c2852656c6179426c6f636b4e756d6265722c2058636d704d657373616765466f726d6174293e00009508086463756d756c75735f70616c6c65745f78636d705f717565756530496e626f756e645374617465000108084f6b0000002453757370656e6465640001000099080000029d08009d080000040810a10800a1080c74706f6c6b61646f745f70617261636861696e5f7072696d697469766573287072696d6974697665734458636d704d657373616765466f726d617400010c60436f6e636174656e6174656456657273696f6e656458636d0000005c436f6e636174656e61746564456e636f646564426c6f620001001c5369676e616c7300020000a50800000408ad011000a908000002ad0800ad08086463756d756c75735f70616c6c65745f78636d705f7175657565584f7574626f756e644368616e6e656c44657461696c730000140124726563697069656e74ad0101185061726149640001147374617465b10801344f7574626f756e6453746174650001347369676e616c735f657869737435010110626f6f6c00012c66697273745f696e6465784901010c7531360001286c6173745f696e6465784901010c7531360000b108086463756d756c75735f70616c6c65745f78636d705f7175657565344f7574626f756e645374617465000108084f6b0000002453757370656e64656400010000b50800000408ad01490100b908086463756d756c75735f70616c6c65745f78636d705f71756575653c5175657565436f6e66696744617461000018014473757370656e645f7468726573686f6c6410010c75333200013864726f705f7468726573686f6c6410010c753332000140726573756d655f7468726573686f6c6410010c7533320001407468726573686f6c645f7765696768742401185765696768740001547765696768745f72657374726963745f646563617924011857656967687400016878636d705f6d61785f696e646976696475616c5f7765696768742401185765696768740000bd080000040cad01103400c1080c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c6574144572726f72040454000114304661696c6564546f53656e640000046c4661696c656420746f2073656e642058434d206d6573736167652e3042616458636d4f726967696e0001043c4261642058434d206f726967696e2e1842616458636d000204344261642058434d20646174612e484261644f766572776569676874496e64657800030454426164206f76657277656967687420696e6465782e3c5765696768744f7665724c696d6974000404f850726f76696465642077656967687420697320706f737369626c79206e6f7420656e6f75676820746f206578656375746520746865206d6573736167652e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ec5080c2870616c6c65745f78636d1870616c6c65742c5175657279537461747573042c426c6f636b4e756d6265720110010c1c50656e64696e67100124726573706f6e6465720102015856657273696f6e65644d756c74694c6f636174696f6e00014c6d617962655f6d617463685f71756572696572c90801784f7074696f6e3c56657273696f6e65644d756c74694c6f636174696f6e3e0001306d617962655f6e6f74696679cd0801404f7074696f6e3c2875382c207538293e00011c74696d656f757410012c426c6f636b4e756d6265720000003c56657273696f6e4e6f7469666965720801186f726967696e0102015856657273696f6e65644d756c74694c6f636174696f6e00012469735f61637469766535010110626f6f6c000100145265616479080120726573706f6e7365d508014456657273696f6e6564526573706f6e7365000108617410012c426c6f636b4e756d62657200020000c90804184f7074696f6e0404540101020108104e6f6e6500000010536f6d65040001020000010000cd0804184f7074696f6e04045401d1080108104e6f6e6500000010536f6d650400d1080000010000d10800000408080800d508082c73746167696e675f78636d4456657273696f6e6564526573706f6e736500010808563204009503013076323a3a526573706f6e73650002000856330400c903013076333a3a526573706f6e736500030000d9080000040810010200dd080000040c2c241000e1080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401e508045300000400e90801185665633c543e0000e5080000040801021000e908000002e50800ed080c2870616c6c65745f78636d1870616c6c65745456657273696f6e4d6967726174696f6e53746167650001105c4d696772617465537570706f7274656456657273696f6e0000005c4d69677261746556657273696f6e4e6f74696669657273000100504e6f7469667943757272656e74546172676574730400f108013c4f7074696f6e3c5665633c75383e3e000200684d696772617465416e644e6f746966794f6c645461726765747300030000f10804184f7074696f6e04045401340108104e6f6e6500000010536f6d650400340000010000f5080000040c1000f90800f908082c73746167696e675f78636d4056657273696f6e6564417373657449640001040856330400d0012c76333a3a4173736574496400030000fd080c2870616c6c65745f78636d1870616c6c65746852656d6f74654c6f636b656446756e6769626c655265636f72640848436f6e73756d65724964656e74696669657201ac304d6178436f6e73756d6572730000100118616d6f756e74180110753132380001146f776e65720102015856657273696f6e65644d756c74694c6f636174696f6e0001186c6f636b65720102015856657273696f6e65644d756c74694c6f636174696f6e000124636f6e73756d657273010901d0426f756e6465645665633c28436f6e73756d65724964656e7469666965722c2075313238292c204d6178436f6e73756d6572733e000001090c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454010509045300000400090901185665633c543e0000050900000408ac180009090000020509000d090c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454011109045300000400150901185665633c543e000011090000040818010200150900000211090019090c2870616c6c65745f78636d1870616c6c6574144572726f720404540001502c556e726561636861626c650000085d0154686520646573697265642064657374696e6174696f6e2077617320756e726561636861626c652c2067656e6572616c6c7920626563617573652074686572652069732061206e6f20776179206f6620726f7574696e6718746f2069742e2c53656e644661696c757265000108410154686572652077617320736f6d65206f746865722069737375652028692e652e206e6f7420746f20646f207769746820726f7574696e672920696e2073656e64696e6720746865206d6573736167652ec8506572686170732061206c61636b206f6620737061636520666f7220627566666572696e6720746865206d6573736167652e2046696c74657265640002049c546865206d65737361676520657865637574696f6e206661696c73207468652066696c7465722e48556e776569676861626c654d657373616765000304b4546865206d65737361676527732077656967687420636f756c64206e6f742062652064657465726d696e65642e6044657374696e6174696f6e4e6f74496e7665727469626c65000404f05468652064657374696e6174696f6e20604d756c74694c6f636174696f6e602070726f76696465642063616e6e6f7420626520696e7665727465642e14456d707479000504805468652061737365747320746f2062652073656e742061726520656d7074792e3843616e6e6f745265616e63686f720006043501436f756c64206e6f742072652d616e63686f72207468652061737365747320746f206465636c61726520746865206665657320666f72207468652064657374696e6174696f6e20636861696e2e34546f6f4d616e79417373657473000704c4546f6f206d616e79206173736574732068617665206265656e20617474656d7074656420666f72207472616e736665722e34496e76616c69644f726967696e000804784f726967696e20697320696e76616c696420666f722073656e64696e672e2842616456657273696f6e00090421015468652076657273696f6e206f6620746865206056657273696f6e6564602076616c75652075736564206973206e6f742061626c6520746f20626520696e7465727072657465642e2c4261644c6f636174696f6e000a08410154686520676976656e206c6f636174696f6e20636f756c64206e6f7420626520757365642028652e672e20626563617573652069742063616e6e6f742062652065787072657373656420696e2074686560646573697265642076657273696f6e206f662058434d292e384e6f537562736372697074696f6e000b04bc546865207265666572656e63656420737562736372697074696f6e20636f756c64206e6f7420626520666f756e642e44416c726561647953756273637269626564000c041101546865206c6f636174696f6e20697320696e76616c69642073696e636520697420616c726561647920686173206120737562736372697074696f6e2066726f6d2075732e30496e76616c69644173736574000d0480496e76616c696420617373657420666f7220746865206f7065726174696f6e2e284c6f7742616c616e6365000e044101546865206f776e657220646f6573206e6f74206f776e2028616c6c29206f662074686520617373657420746861742074686579207769736820746f20646f20746865206f7065726174696f6e206f6e2e30546f6f4d616e794c6f636b73000f04c0546865206173736574206f776e65722068617320746f6f206d616e79206c6f636b73206f6e207468652061737365742e4c4163636f756e744e6f74536f7665726569676e001004310154686520676976656e206163636f756e74206973206e6f7420616e206964656e7469666961626c6520736f7665726569676e206163636f756e7420666f7220616e79206c6f636174696f6e2e28466565734e6f744d65740011042901546865206f7065726174696f6e207265717569726564206665657320746f20626520706169642077686963682074686520696e69746961746f7220636f756c64206e6f74206d6565742e304c6f636b4e6f74466f756e64001204f4412072656d6f7465206c6f636b20776974682074686520636f72726573706f6e64696e67206461746120636f756c64206e6f7420626520666f756e642e14496e557365001304490154686520756e6c6f636b206f7065726174696f6e2063616e6e6f742073756363656564206265636175736520746865726520617265207374696c6c20636f6e73756d657273206f6620746865206c6f636b2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e1d090c4863756d756c75735f70616c6c65745f78636d1870616c6c6574144572726f72040454000100048054686520604572726f726020656e756d206f6620746869732070616c6c65742e2109086063756d756c75735f70616c6c65745f646d705f717565756528436f6e6669674461746100000401386d61785f696e646976696475616c24011857656967687400002509086063756d756c75735f70616c6c65745f646d705f71756575653450616765496e6465784461746100000c0128626567696e5f7573656410012c50616765436f756e746572000120656e645f7573656410012c50616765436f756e7465720001406f7665727765696768745f636f756e742c013c4f766572776569676874496e646578000029090000022d09002d090000040810340031090c6063756d756c75735f70616c6c65745f646d705f71756575651870616c6c6574144572726f720404540001081c556e6b6e6f776e0000048c546865206d65737361676520696e64657820676976656e20697320756e6b6e6f776e2e244f7665724c696d6974000104310154686520616d6f756e74206f662077656967687420676976656e20697320706f737369626c79206e6f7420656e6f75676820666f7220657865637574696e6720746865206d6573736167652e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e350900000408890400003909000004088904d904003d090000040c8904d904000041090c3470616c6c65745f756e697175651870616c6c6574144572726f7204045400010c8c436f6c6c656374696f6e446563696d616c506f696e744c696d697445786365656465640000045101446563696d616c5f706f696e747320706172616d65746572206d757374206265206c6f776572207468616e205b6075705f646174615f737472756374733a3a4d41585f444543494d414c5f504f494e5453605d2e34456d707479417267756d656e74000104c84c656e677468206f66206974656d732070726f70657274696573206d7573742062652067726561746572207468616e20302ea85265706172746974696f6e43616c6c65644f6e4e6f6e526566756e6769626c65436f6c6c656374696f6e000204dc5265706572746974696f6e206973206f6e6c7920737570706f7274656420627920726566756e6769626c6520636f6c6c656374696f6e2e04a84572726f727320666f722074686520636f6d6d6f6e20556e69717565207472616e73616374696f6e732e45090c5070616c6c65745f636f6e66696775726174696f6e1870616c6c6574144572726f7204045400010464496e636f6e73697374656e74436f6e66696775726174696f6e000000048054686520604572726f726020656e756d206f6620746869732070616c6c65742e4909083c75705f646174615f7374727563747328436f6c6c656374696f6e04244163636f756e7449640100002401146f776e65720001244163636f756e7449640001106d6f646545040138436f6c6c656374696f6e4d6f64650001106e616d6535040138436f6c6c656374696f6e4e616d6500012c6465736372697074696f6e3d040154436f6c6c656374696f6e4465736372697074696f6e000130746f6b656e5f70726566697841040154436f6c6c656374696f6e546f6b656e50726566697800012c73706f6e736f72736869704d09016c53706f6e736f727368697053746174653c4163636f756e7449643e0001186c696d6974735d040140436f6c6c656374696f6e4c696d69747300012c7065726d697373696f6e7371040154436f6c6c656374696f6e5065726d697373696f6e73000114666c6167736902013c436f6c6c656374696f6e466c61677300004d09083c75705f646174615f737472756374734053706f6e736f7273686970537461746504244163636f756e7449640100010c2044697361626c65640000002c556e636f6e6669726d656404000001244163636f756e74496400010024436f6e6669726d656404000001244163636f756e744964000200005109083c75705f646174615f737472756374732850726f7065727469657300000c010c6d61705509017050726f706572746965734d61703c50726f706572747956616c75653e000138636f6e73756d65645f737061636510010c7533320001245f726573657276656410010c75333200005509083c75705f646174615f737472756374733450726f706572746965734d6170041456616c756501b1040004005909011901426f756e64656442547265654d61703c50726f70657274794b65792c2056616c75652c20436f6e73745533323c4d41585f50524f504552544945535f5045525f4954454d3e3e000059090c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f6d61703c426f756e64656442547265654d61700c044b019d04045601b1040453000004005d09013842547265654d61703c4b2c20563e00005d09042042547265654d617008044b019d04045601b104000400610900000061090000026509006509000004089d04b104006909083c75705f646174615f737472756374733450726f706572746965734d6170041456616c756501a1040004006d09011901426f756e64656442547265654d61703c50726f70657274794b65792c2056616c75652c20436f6e73745533323c4d41585f50524f504552544945535f5045525f4954454d3e3e00006d090c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f6d61703c426f756e64656442547265654d61700c044b019d04045601a1040453000004007109013842547265654d61703c4b2c20563e00007109042042547265654d617008044b019d04045601a104000400750900000075090000027909007909000004089d04a104007d090000040889044d040081090000041485098904d90489098d09008509083c75705f646174615f737472756374733c436f6c6c656374696f6e537461747300000c011c6372656174656410010c75333200012464657374726f79656410010c753332000114616c69766510010c75333200008909083c75705f646174615f7374727563747328546f6b656e4368696c640000080114746f6b656ed904011c546f6b656e4964000128636f6c6c656374696f6e89040130436f6c6c656374696f6e496400008d09082c5068616e746f6d547970653c75705f646174615f73747275637473040454019109000400c10900000091090000040c95099909a109009509083c75705f646174615f7374727563747324546f6b656e44617461043843726f73734163636f756e744964014d04000c012870726f70657274696573b50401345665633c50726f70657274793e0001146f776e6572bd0401584f7074696f6e3c43726f73734163636f756e7449643e0001187069656365731801107531323800009909083c75705f646174615f7374727563747334527063436f6c6c656374696f6e04244163636f756e7449640100003001146f776e65720001244163636f756e7449640001106d6f646545040138436f6c6c656374696f6e4d6f64650001106e616d65390401205665633c7531363e00012c6465736372697074696f6e390401205665633c7531363e000130746f6b656e5f70726566697834011c5665633c75383e00012c73706f6e736f72736869704d09016c53706f6e736f727368697053746174653c4163636f756e7449643e0001186c696d6974735d040140436f6c6c656374696f6e4c696d69747300012c7065726d697373696f6e7371040154436f6c6c656374696f6e5065726d697373696f6e73000168746f6b656e5f70726f70657274795f7065726d697373696f6e73a50401685665633c50726f70657274794b65795065726d697373696f6e3e00012870726f70657274696573b50401345665633c50726f70657274793e000124726561645f6f6e6c7935010110626f6f6c000114666c6167739d090148527063436f6c6c656374696f6e466c61677300009d09083c75705f646174615f7374727563747348527063436f6c6c656374696f6e466c616773000008011c666f726569676e35010110626f6f6c0001386572633732316d6574616461746135010110626f6f6c0000a109084c75705f706f765f657374696d6174655f7270631c506f76496e666f000014012870726f6f665f73697a652c010c753634000148636f6d706163745f70726f6f665f73697a652c010c753634000154636f6d707265737365645f70726f6f665f73697a652c010c75363400011c726573756c7473a50901645665633c4170706c7945787472696e736963526573756c743e0001286b65795f76616c756573b90901445665633c547269654b657956616c75653e0000a509000002a90900a9090418526573756c7408045401a8044501ad090108084f6b0400a8000000000c4572720400ad090000010000ad090c2873705f72756e74696d65507472616e73616374696f6e5f76616c6964697479605472616e73616374696f6e56616c69646974794572726f720001081c496e76616c69640400b1090148496e76616c69645472616e73616374696f6e0000001c556e6b6e6f776e0400b5090148556e6b6e6f776e5472616e73616374696f6e00010000b1090c2873705f72756e74696d65507472616e73616374696f6e5f76616c696469747948496e76616c69645472616e73616374696f6e00012c1043616c6c0000001c5061796d656e7400010018467574757265000200145374616c650003002042616450726f6f6600040044416e6369656e744269727468426c6f636b0005004445786861757374735265736f757263657300060018437573746f6d04000801087538000700304261644d616e6461746f72790008004c4d616e6461746f727956616c69646174696f6e000900244261645369676e6572000a0000b5090c2873705f72756e74696d65507472616e73616374696f6e5f76616c696469747948556e6b6e6f776e5472616e73616374696f6e00010c3043616e6e6f744c6f6f6b75700000004c4e6f556e7369676e656456616c696461746f7200010018437573746f6d0400080108753800020000b909000002bd0900bd09084c75705f706f765f657374696d6174655f72706330547269654b657956616c7565000008010c6b657934011c5665633c75383e00011476616c756534011c5665633c75383e0000c10900000300000000910900c5090c3470616c6c65745f636f6d6d6f6e1870616c6c6574144572726f7204045400019848436f6c6c656374696f6e4e6f74466f756e640000047c5468697320636f6c6c656374696f6e20646f6573206e6f742065786973742e404d7573744265546f6b656e4f776e6572000104b853656e64657220706172616d6574657220616e64206974656d206f776e6572206d75737420626520657175616c2e304e6f5065726d697373696f6e0002047c4e6f207065726d697373696f6e20746f20706572666f726d20616374696f6e7443616e7444657374726f794e6f74456d707479436f6c6c656374696f6e000304b044657374726f79696e67206f6e6c7920656d70747920636f6c6c656374696f6e7320697320616c6c6f7765645c5075626c69634d696e74696e674e6f74416c6c6f7765640004047c436f6c6c656374696f6e206973206e6f7420696e206d696e74206d6f64652e54416464726573734e6f74496e416c6c6f776c6973740005047441646472657373206973206e6f7420696e20616c6c6f77206c6973742e6c436f6c6c656374696f6e4e616d654c696d69744578636565646564000604bc436f6c6c656374696f6e206e616d652063616e206e6f74206265206c6f6e676572207468616e20363320636861722e88436f6c6c656374696f6e4465736372697074696f6e4c696d69744578636565646564000704dc436f6c6c656374696f6e206465736372697074696f6e2063616e206e6f74206265206c6f6e676572207468616e2032353520636861722e88436f6c6c656374696f6e546f6b656e5072656669784c696d69744578636565646564000804b0546f6b656e207072656669782063616e206e6f74206265206c6f6e676572207468616e20313520636861722e74546f74616c436f6c6c656374696f6e734c696d6974457863656564656400090484546f74616c20636f6c6c656374696f6e7320626f756e642065786365656465642e70436f6c6c656374696f6e41646d696e436f756e744578636565646564000a04604578636565646564206d61782061646d696e20636f756e7474436f6c6c656374696f6e4c696d6974426f756e64734578636565646564000b04bc436f6c6c656374696f6e206c696d697420626f756e64732070657220636f6c6c656374696f6e206578636565646564784f776e65725065726d697373696f6e7343616e7442655265766572746564000c040d01547269656420746f20656e61626c65207065726d697373696f6e7320776869636820617265206f6e6c79207065726d697474656420746f2062652064697361626c6564485472616e736665724e6f74416c6c6f776564000d04cc436f6c6c656374696f6e2073657474696e6773206e6f7420616c6c6f77696e67206974656d73207472616e7366657272696e67644163636f756e74546f6b656e4c696d69744578636565646564000e04ac4163636f756e7420746f6b656e206c696d69742065786365656465642070657220636f6c6c656374696f6e70436f6c6c656374696f6e546f6b656e4c696d69744578636565646564000f047c436f6c6c656374696f6e20746f6b656e206c696d6974206578636565646564484d65746164617461466c616746726f7a656e001004504d6574616461746120666c61672066726f7a656e34546f6b656e4e6f74466f756e640011044c4974656d20646f6573206e6f7420657869737440546f6b656e56616c7565546f6f4c6f77001204684974656d2069732062616c616e6365206e6f7420656e6f7567684c417070726f76656456616c7565546f6f4c6f77001304a45265717565737465642076616c7565206973206d6f7265207468616e2074686520617070726f7665646043616e74417070726f76654d6f72655468616e4f776e656400140480547269656420746f20617070726f7665206d6f7265207468616e206f776e6564544164647265737349734e6f744574684d6972726f72001504bc4f6e6c79207370656e64696e672066726f6d20657468206d6972726f7220636f756c6420626520617070726f766564344164647265737349735a65726f001604b843616e2774207472616e7366657220746f6b656e7320746f20657468657265756d207a65726f206164647265737350556e737570706f727465644f7065726174696f6e00170478546865206f7065726174696f6e206973206e6f7420737570706f727465644c4e6f7453756666696369656e74466f756e64730018049c496e73756666696369656e742066756e647320746f20706572666f726d20616e20616374696f6e585573657249734e6f74416c6c6f776564546f4e657374001904985573657220646f6573206e6f74207361746973667920746865206e657374696e672072756c6588536f75726365436f6c6c656374696f6e49734e6f74416c6c6f776564546f4e657374001a0411014f6e6c7920746f6b656e732066726f6d20737065636966696320636f6c6c656374696f6e73206d6179206e65737420746f6b656e7320756e6465722074686973206f6e656c436f6c6c656374696f6e4669656c6453697a654578636565646564001b04e4547269656420746f2073746f7265206d6f72652064617461207468616e20616c6c6f77656420696e20636f6c6c656374696f6e206669656c64484e6f5370616365466f7250726f7065727479001c04b8547269656420746f2073746f7265206d6f72652070726f70657274792064617461207468616e20616c6c6f7765645050726f70657274794c696d697452656163686564001d04b8547269656420746f2073746f7265206d6f72652070726f7065727479206b657973207468616e20616c6c6f7765645050726f70657274794b65794973546f6f4c6f6e67001e046050726f7065727479206b657920697320746f6f206c6f6e6774496e76616c6964436861726163746572496e50726f70657274794b6579001f0415014f6e6c79204153434949206c6574746572732c206469676974732c20616e642073796d626f6c7320605f602c20602d602c20616e6420602e602061726520616c6c6f77656440456d70747950726f70657274794b657900200484456d7074792070726f7065727479206b6579732061726520666f7262696464656e50436f6c6c656374696f6e497345787465726e616c002104ec547269656420746f2061636365737320616e2065787465726e616c20636f6c6c656374696f6e207769746820616e20696e7465726e616c2041504950436f6c6c656374696f6e4973496e7465726e616c002204ec547269656420746f2061636365737320616e20696e7465726e616c20636f6c6c656374696f6e207769746820616e2065787465726e616c2041504958436f6e6669726d53706f6e736f72736869704661696c0023040d01546869732061646472657373206973206e6f74207365742061732073706f6e736f722c2075736520736574436f6c6c656374696f6e53706f6e736f722066697273742e605573657249734e6f74436f6c6c656374696f6e41646d696e002404845468652075736572206973206e6f7420616e2061646d696e6973747261746f722e5446756e6769626c654974656d73486176654e6f4964002504710146756e6769626c6520746f6b656e7320686f6c64206e6f2049442c20616e64207468652064656661756c742076616c7565206f6620546f6b656e496420666f7220612066756e6769626c6520636f6c6c656374696f6e20697320302e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ec9090000040c89044d044d0400cd090c3c70616c6c65745f66756e6769626c651870616c6c6574144572726f72040454000118c04e6f7446756e6769626c654461746155736564546f4d696e7446756e6769626c65436f6c6c656374696f6e546f6b656e000004ec4e6f742046756e6769626c65206974656d2064617461207573656420746f206d696e7420696e2046756e6769626c6520636f6c6c656374696f6e2e6446756e6769626c654974656d73446f6e74486176654461746100010490547269656420746f20736574206461746120666f722066756e6769626c65206974656d2e6046756e6769626c65446973616c6c6f77734e657374696e67000204a046756e6769626c6520746f6b656e20646f6573206e6f7420737570706f7274206e657374696e672e6c53657474696e6750726f706572746965734e6f74416c6c6f7765640003049c53657474696e67206974656d2070726f70657274696573206973206e6f7420616c6c6f7765642e8053657474696e67416c6c6f77616e6365466f72416c6c4e6f74416c6c6f776564000404a453657474696e6720616c6c6f77616e636520666f7220616c6c206973206e6f7420616c6c6f7765642e7046756e6769626c65546f6b656e73417265416c7761797356616c696400050445014f6e6c7920612066756e6769626c6520636f6c6c656374696f6e20636f756c6420626520706f737369626c792062726f6b656e3b20616e792066756e6769626c6520746f6b656e2069732076616c69642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ed109083c75705f646174615f737472756374732850726f7065727469657300000c010c6d61705509017050726f706572746965734d61703c50726f706572747956616c75653e000138636f6e73756d65645f737061636510010c7533320001245f726573657276656410010c7533320000d5090000040c89044d04d90400d9090000040c8904d9044d0400dd09000004108904d9044d044d0400e1090c4470616c6c65745f726566756e6769626c651870616c6c6574144572726f72040454000114c84e6f74526566756e6769626c654461746155736564546f4d696e7446756e6769626c65436f6c6c656374696f6e546f6b656e000004fc4e6f7420526566756e6769626c65206974656d2064617461207573656420746f206d696e7420696e20526566756e6769626c6520636f6c6c656374696f6e2e5457726f6e67526566756e6769626c655069656365730001047c4d6178696d756d20726566756e676962696c6974792065786365656465642e885265706172746974696f6e5768696c654e6f744f776e696e67416c6c5069656365730002042901526566756e6769626c6520746f6b656e2063616e2774206265207265706172746974696f6e656420627920757365722077686f2069736e2774206f776e7320616c6c207069656365732e68526566756e6769626c65446973616c6c6f77734e657374696e67000304a4526566756e6769626c6520746f6b656e2063616e2774206e657374206f7468657220746f6b656e732e6c53657474696e6750726f706572746965734e6f74416c6c6f7765640004049c53657474696e67206974656d2070726f70657274696573206973206e6f7420616c6c6f7765642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ee509084870616c6c65745f6e6f6e66756e6769626c65204974656d44617461043843726f73734163636f756e744964014d04000401146f776e65724d04013843726f73734163636f756e7449640000e909000004108904d904ed099d0400ed09083c75705f646174615f737472756374733450726f706572747953636f7065000108104e6f6e6500000010526d726b00010000f1090c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000f5090000040c8904d904390900f9090c4870616c6c65745f6e6f6e66756e6769626c651870616c6c6574144572726f7204045400010ccc4e6f744e6f6e66756e6769626c654461746155736564546f4d696e7446756e6769626c65436f6c6c656374696f6e546f6b656e00000405014e6f74204e6f6e66756e6769626c65206974656d2064617461207573656420746f206d696e7420696e204e6f6e66756e6769626c6520636f6c6c656374696f6e2e704e6f6e66756e6769626c654974656d73486176654e6f416d6f756e74000104605573656420616d6f756e74203e20312077697468204e46545c43616e744275726e4e6674576974684368696c6472656e00020480556e61626c6520746f206275726e204e46542077697468206368696c6472656e048054686520604572726f726020656e756d206f6620746869732070616c6c65742efd090c4070616c6c65745f7374727563747572651870616c6c6574144572726f72040454000114444f75726f626f726f73446574656374656400000421015768696c65206e657374696e672c20656e636f756e746572656420616e20616c726561647920636865636b6564206163636f756e742c20646574656374696e672061206c6f6f702e2844657074684c696d697400010445015768696c65206e657374696e672c207265616368656420746865206465707468206c696d6974206f66206e657374696e672c20657863656564696e67207468652070726f7669646564206275646765742e30427265616474684c696d69740002044d015768696c65206e657374696e672c2072656163686564207468652062726561647468206c696d6974206f66206e657374696e672c20657863656564696e67207468652070726f7669646564206275646765742e34546f6b656e4e6f74466f756e64000304d4436f756c646e27742066696e642074686520746f6b656e206f776e6572207468617420697320697473656c66206120746f6b656e2e7043616e744e657374546f6b656e556e646572436f6c6c656374696f6e0004043d01547269656420746f206e65737420746f6b656e20756e64657220636f6c6c656374696f6e20636f6e747261637420616464726573732c20696e7374656164206f6620746f6b656e2061646472657373048054686520604572726f726020656e756d206f6620746869732070616c6c65742e010a00000408001000050a00000408181000090a0c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401a5070453000004000d0a01185665633c543e00000d0a000002a50700110a0c5070616c6c65745f6170705f70726f6d6f74696f6e1870616c6c6574144572726f7204045400011c2c41646d696e4e6f74536574000004b84572726f722064756520746f20616374696f6e20726571756972696e672061646d696e20746f206265207365742e304e6f5065726d697373696f6e0001048c4e6f207065726d697373696f6e20746f20706572666f726d20616e20616374696f6e2e484e6f7453756666696369656e7446756e6473000204a0496e73756666696369656e742066756e647320746f20706572666f726d20616e20616374696f6e2e5c50656e64696e67466f72426c6f636b4f766572666c6f7700030499014f6363757273207768656e20612070656e64696e6720756e7374616b652063616e6e6f7420626520616464656420696e207468697320626c6f636b2e2050454e44494e475f4c494d49545f5045525f424c4f434b60206c696d6974732065786365656465642e3453706f6e736f724e6f74536574000404cd01546865206572726f722069732064756520746f20746865206661637420746861742074686520636f6c6c656374696f6e2f636f6e7472616374206d75737420616c72656164792062652073706f6e736f72656420696e206f7264657220746f20706572666f726d2074686520616374696f6e2e64496e73756666696369656e745374616b656442616c616e6365000504b44572726f72732063617573656420627920696e73756666696369656e74207374616b65642062616c616e63652e48496e636f6e73697374656e6379537461746500060419014572726f72732063617573656420627920696e636f7272656374207374617465206f662061207374616b657220696e20636f6e74657874206f66207468652070616c6c65742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e150a0c5470616c6c65745f666f726569676e5f617373657473186d6f64756c65144572726f720404540001102c4261644c6f636174696f6e000008410154686520676976656e206c6f636174696f6e20636f756c64206e6f7420626520757365642028652e672e20626563617573652069742063616e6e6f742062652065787072657373656420696e2074686560646573697265642076657273696f6e206f662058434d292e504d756c74694c6f636174696f6e45786973746564000104544d756c74694c6f636174696f6e206578697374656440417373657449644e6f744578697374730002044841737365744964206e6f7420657869737473384173736574496445786973746564000304384173736574496420657869737473048054686520604572726f726020656e756d206f6620746869732070616c6c65742e190a082870616c6c65745f65766d30436f64654d65746164617461000008011073697a652c010c753634000110686173683001104832353600001d0a0000040865033000210a0c2870616c6c65745f65766d1870616c6c6574144572726f7204045400012c2842616c616e63654c6f77000004904e6f7420656e6f7567682062616c616e636520746f20706572666f726d20616374696f6e2c4665654f766572666c6f770001048043616c63756c6174696e6720746f74616c20666565206f766572666c6f7765643c5061796d656e744f766572666c6f770002049043616c63756c6174696e6720746f74616c207061796d656e74206f766572666c6f7765643857697468647261774661696c65640003044c576974686472617720666565206661696c6564384761735072696365546f6f4c6f770004045447617320707269636520697320746f6f206c6f772e30496e76616c69644e6f6e6365000504404e6f6e636520697320696e76616c6964384761734c696d6974546f6f4c6f7700060454476173206c696d697420697320746f6f206c6f772e3c4761734c696d6974546f6f4869676800070458476173206c696d697420697320746f6f20686967682e24556e646566696e656400080440556e646566696e6564206572726f722e285265656e7472616e63790009043845564d207265656e7472616e6379685472616e73616374696f6e4d757374436f6d6546726f6d454f41000a04244549502d333630372c048054686520604572726f726020656e756d206f6620746869732070616c6c65742e250a000002290a00290a0000040c65052d0a3d0a002d0a081866705f727063445472616e73616374696f6e53746174757300001c01407472616e73616374696f6e5f68617368300110483235360001447472616e73616374696f6e5f696e64657810010c75333200011066726f6d6503011048313630000108746f310a01304f7074696f6e3c483136303e000140636f6e74726163745f61646472657373310a01304f7074696f6e3c483136303e0001106c6f67739d0501205665633c4c6f673e0001286c6f67735f626c6f6f6d350a0114426c6f6f6d0000310a04184f7074696f6e0404540165030108104e6f6e6500000010536f6d65040065030000010000350a0820657468626c6f6f6d14426c6f6f6d00000400390a01405b75383b20424c4f4f4d5f53495a455d0000390a0000030001000008003d0a0c20657468657265756d1c726563656970742452656365697074563300010c184c65676163790400410a014445495036353852656365697074446174610000001c454950323933300400410a01484549503239333052656365697074446174610001001c454950313535390400410a014845495031353539526563656970744461746100020000410a0c20657468657265756d1c72656365697074444549503635385265636569707444617461000010012c7374617475735f636f64650801087538000120757365645f67617349050110553235360001286c6f67735f626c6f6f6d350a0114426c6f6f6d0001106c6f67739d0501205665633c4c6f673e0000450a0c20657468657265756d14626c6f636b14426c6f636b040454016505000c0118686561646572490a01184865616465720001307472616e73616374696f6e73510a01185665633c543e0001186f6d6d657273550a012c5665633c4865616465723e0000490a0c20657468657265756d186865616465721848656164657200003c012c706172656e745f686173683001104832353600012c6f6d6d6572735f686173683001104832353600012c62656e6566696369617279650301104831363000012873746174655f726f6f74300110483235360001447472616e73616374696f6e735f726f6f743001104832353600013472656365697074735f726f6f74300110483235360001286c6f67735f626c6f6f6d350a0114426c6f6f6d000128646966666963756c747949050110553235360001186e756d62657249050110553235360001246761735f6c696d697449050110553235360001206761735f75736564490501105532353600012474696d657374616d702c010c75363400012865787472615f6461746134011442797465730001206d69785f68617368300110483235360001146e6f6e63654d0a010c48363400004d0a0c38657468657265756d5f747970657310686173680c483634000004000101011c5b75383b20385d0000510a000002650500550a000002490a00590a0000023d0a005d0a0000022d0a00610a0c3c70616c6c65745f657468657265756d1870616c6c6574144572726f7204045400010840496e76616c69645369676e6174757265000004545369676e617475726520697320696e76616c69642e305072654c6f67457869737473000104d85072652d6c6f672069732070726573656e742c207468657265666f7265207472616e73616374206973206e6f7420616c6c6f7765642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e650a0c6870616c6c65745f65766d5f636f6465725f7375627374726174651870616c6c6574144572726f72040454000108204f75744f66476173000000244f75744f6646756e6400010014850144697370617463684572726f72206973206f70617175652c20627574207765206e65656420746f20736f6d65686f77206578747261637420636f7272656374206572726f7220696e2063617365206f66204f75744f66476173206661696c7572659101536f207765206861766520746869732070616c6c65742c20776869636820646566696e6573204f75744f66476173206572726f722c20616e64206b6e65777320697473206f776e20696420746f20636865636b2069662044697370617463684572726f725c6973207468726f776e2062656361757365206f662069740061015468657365206572726f72732073686f756c646e277420656e6420696e2065787472696e73696320726573756c74732c2061732074686579206f6e6c79207573656420696e2065766d20657865637574696f6e2070617468690a083c75705f646174615f737472756374734053706f6e736f7273686970537461746504244163636f756e744964014d04010c2044697361626c65640000002c556e636f6e6669726d656404004d0401244163636f756e74496400010024436f6e6669726d656404004d0401244163636f756e744964000200006d0a086c70616c6c65745f65766d5f636f6e74726163745f68656c706572733c53706f6e736f72696e674d6f64655400010c2044697361626c65640000002c416c6c6f776c69737465640001002047656e65726f757300020000710a0c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f6d61703c426f756e64656442547265654d61700c044b01100456014905045300000400750a013842547265654d61703c4b2c20563e0000750a042042547265654d617008044b01100456014905000400790a000000790a0000027d0a007d0a0000040810490500810a000004086503650300850a0c6c70616c6c65745f65766d5f636f6e74726163745f68656c706572731870616c6c6574144572726f7204045400010c304e6f5065726d697373696f6e000004c054686973206d6574686f64206973206f6e6c792065786563757461626c6520627920636f6e7472616374206f776e6572404e6f50656e64696e6753706f6e736f72000104804e6f2070656e64696e672073706f6e736f7220666f7220636f6e74726163742e80546f6f4d616e794d6574686f64734861766553706f6e736f7265644c696d697400020419014e756d626572206f66206d6574686f647320746861742073706f6e736f726564206c696d697420697320646566696e656420666f722065786365656473206d6178696d756d2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e890a0c5070616c6c65745f65766d5f6d6967726174696f6e1870616c6c6574144572726f7204045400010c3c4163636f756e744e6f74456d7074790000048843616e206f6e6c79206d69677261746520746f20656d70747920616464726573732e544163636f756e7449734e6f744d6967726174696e6700010409014d6967726174696f6e206f662074686973206163636f756e74206973206e6f742079657420737461727465642c206f7220616c72656164792066696e69736865642e204261644576656e74000204704661696c656420746f206465636f6465206576656e74206279746573048054686520604572726f726020656e756d206f6620746869732070616c6c65742e8d0a0c4870616c6c65745f6d61696e74656e616e63651870616c6c6574144572726f72040454000100048054686520604572726f726020656e756d206f6620746869732070616c6c65742e910a0c3870616c6c65745f7574696c6974791870616c6c6574144572726f7204045400010430546f6f4d616e7943616c6c730000045c546f6f206d616e792063616c6c7320626174636865642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e950a0c4470616c6c65745f746573745f7574696c731870616c6c6574144572726f72040454000108485465737450616c6c657444697361626c65640000003c54726967676572526f6c6c6261636b000100048054686520604572726f726020656e756d206f6620746869732070616c6c65742e990a0c4466705f73656c665f636f6e7461696e65644c756e636865636b65645f65787472696e73696348556e636865636b656445787472696e736963101c4164647265737301dd011043616c6c015d01245369676e6174757265019d0a14457874726101b50a000400e90a01250173705f72756e74696d653a3a67656e657269633a3a556e636865636b656445787472696e7369633c416464726573732c2043616c6c2c205369676e61747572652c2045787472610a3e00009d0a082873705f72756e74696d65384d756c74695369676e617475726500010c1c456432353531390400a10a0148656432353531393a3a5369676e61747572650000001c537232353531390400a90a0148737232353531393a3a5369676e61747572650001001445636473610400ad0a014065636473613a3a5369676e617475726500020000a10a0c1c73705f636f72651c65643235353139245369676e617475726500000400a50a01205b75383b2036345d0000a50a000003400000000800a90a0c1c73705f636f72651c73723235353139245369676e617475726500000400a50a01205b75383b2036345d0000ad0a0c1c73705f636f7265146563647361245369676e617475726500000400b10a01205b75383b2036355d0000b10a000003410000000800b50a00000428b90abd0ac10ac50acd0ad10ad50ad90add0ae50a00b90a10306672616d655f73797374656d28657874656e73696f6e7348636865636b5f737065635f76657273696f6e40436865636b5370656356657273696f6e04045400000000bd0a10306672616d655f73797374656d28657874656e73696f6e7340636865636b5f74785f76657273696f6e38436865636b547856657273696f6e04045400000000c10a10306672616d655f73797374656d28657874656e73696f6e7334636865636b5f67656e6573697330436865636b47656e6573697304045400000000c50a10306672616d655f73797374656d28657874656e73696f6e733c636865636b5f6d6f7274616c69747938436865636b4d6f7274616c69747904045400000400c90a010c4572610000c90a102873705f72756e74696d651c67656e657269630c6572610c4572610001010420496d6d6f7274616c0000001c4d6f7274616c31040008000001001c4d6f7274616c32040008000002001c4d6f7274616c33040008000003001c4d6f7274616c34040008000004001c4d6f7274616c35040008000005001c4d6f7274616c36040008000006001c4d6f7274616c37040008000007001c4d6f7274616c38040008000008001c4d6f7274616c3904000800000900204d6f7274616c313004000800000a00204d6f7274616c313104000800000b00204d6f7274616c313204000800000c00204d6f7274616c313304000800000d00204d6f7274616c313404000800000e00204d6f7274616c313504000800000f00204d6f7274616c313604000800001000204d6f7274616c313704000800001100204d6f7274616c313804000800001200204d6f7274616c313904000800001300204d6f7274616c323004000800001400204d6f7274616c323104000800001500204d6f7274616c323204000800001600204d6f7274616c323304000800001700204d6f7274616c323404000800001800204d6f7274616c323504000800001900204d6f7274616c323604000800001a00204d6f7274616c323704000800001b00204d6f7274616c323804000800001c00204d6f7274616c323904000800001d00204d6f7274616c333004000800001e00204d6f7274616c333104000800001f00204d6f7274616c333204000800002000204d6f7274616c333304000800002100204d6f7274616c333404000800002200204d6f7274616c333504000800002300204d6f7274616c333604000800002400204d6f7274616c333704000800002500204d6f7274616c333804000800002600204d6f7274616c333904000800002700204d6f7274616c343004000800002800204d6f7274616c343104000800002900204d6f7274616c343204000800002a00204d6f7274616c343304000800002b00204d6f7274616c343404000800002c00204d6f7274616c343504000800002d00204d6f7274616c343604000800002e00204d6f7274616c343704000800002f00204d6f7274616c343804000800003000204d6f7274616c343904000800003100204d6f7274616c353004000800003200204d6f7274616c353104000800003300204d6f7274616c353204000800003400204d6f7274616c353304000800003500204d6f7274616c353404000800003600204d6f7274616c353504000800003700204d6f7274616c353604000800003800204d6f7274616c353704000800003900204d6f7274616c353804000800003a00204d6f7274616c353904000800003b00204d6f7274616c363004000800003c00204d6f7274616c363104000800003d00204d6f7274616c363204000800003e00204d6f7274616c363304000800003f00204d6f7274616c363404000800004000204d6f7274616c363504000800004100204d6f7274616c363604000800004200204d6f7274616c363704000800004300204d6f7274616c363804000800004400204d6f7274616c363904000800004500204d6f7274616c373004000800004600204d6f7274616c373104000800004700204d6f7274616c373204000800004800204d6f7274616c373304000800004900204d6f7274616c373404000800004a00204d6f7274616c373504000800004b00204d6f7274616c373604000800004c00204d6f7274616c373704000800004d00204d6f7274616c373804000800004e00204d6f7274616c373904000800004f00204d6f7274616c383004000800005000204d6f7274616c383104000800005100204d6f7274616c383204000800005200204d6f7274616c383304000800005300204d6f7274616c383404000800005400204d6f7274616c383504000800005500204d6f7274616c383604000800005600204d6f7274616c383704000800005700204d6f7274616c383804000800005800204d6f7274616c383904000800005900204d6f7274616c393004000800005a00204d6f7274616c393104000800005b00204d6f7274616c393204000800005c00204d6f7274616c393304000800005d00204d6f7274616c393404000800005e00204d6f7274616c393504000800005f00204d6f7274616c393604000800006000204d6f7274616c393704000800006100204d6f7274616c393804000800006200204d6f7274616c393904000800006300244d6f7274616c31303004000800006400244d6f7274616c31303104000800006500244d6f7274616c31303204000800006600244d6f7274616c31303304000800006700244d6f7274616c31303404000800006800244d6f7274616c31303504000800006900244d6f7274616c31303604000800006a00244d6f7274616c31303704000800006b00244d6f7274616c31303804000800006c00244d6f7274616c31303904000800006d00244d6f7274616c31313004000800006e00244d6f7274616c31313104000800006f00244d6f7274616c31313204000800007000244d6f7274616c31313304000800007100244d6f7274616c31313404000800007200244d6f7274616c31313504000800007300244d6f7274616c31313604000800007400244d6f7274616c31313704000800007500244d6f7274616c31313804000800007600244d6f7274616c31313904000800007700244d6f7274616c31323004000800007800244d6f7274616c31323104000800007900244d6f7274616c31323204000800007a00244d6f7274616c31323304000800007b00244d6f7274616c31323404000800007c00244d6f7274616c31323504000800007d00244d6f7274616c31323604000800007e00244d6f7274616c31323704000800007f00244d6f7274616c31323804000800008000244d6f7274616c31323904000800008100244d6f7274616c31333004000800008200244d6f7274616c31333104000800008300244d6f7274616c31333204000800008400244d6f7274616c31333304000800008500244d6f7274616c31333404000800008600244d6f7274616c31333504000800008700244d6f7274616c31333604000800008800244d6f7274616c31333704000800008900244d6f7274616c31333804000800008a00244d6f7274616c31333904000800008b00244d6f7274616c31343004000800008c00244d6f7274616c31343104000800008d00244d6f7274616c31343204000800008e00244d6f7274616c31343304000800008f00244d6f7274616c31343404000800009000244d6f7274616c31343504000800009100244d6f7274616c31343604000800009200244d6f7274616c31343704000800009300244d6f7274616c31343804000800009400244d6f7274616c31343904000800009500244d6f7274616c31353004000800009600244d6f7274616c31353104000800009700244d6f7274616c31353204000800009800244d6f7274616c31353304000800009900244d6f7274616c31353404000800009a00244d6f7274616c31353504000800009b00244d6f7274616c31353604000800009c00244d6f7274616c31353704000800009d00244d6f7274616c31353804000800009e00244d6f7274616c31353904000800009f00244d6f7274616c3136300400080000a000244d6f7274616c3136310400080000a100244d6f7274616c3136320400080000a200244d6f7274616c3136330400080000a300244d6f7274616c3136340400080000a400244d6f7274616c3136350400080000a500244d6f7274616c3136360400080000a600244d6f7274616c3136370400080000a700244d6f7274616c3136380400080000a800244d6f7274616c3136390400080000a900244d6f7274616c3137300400080000aa00244d6f7274616c3137310400080000ab00244d6f7274616c3137320400080000ac00244d6f7274616c3137330400080000ad00244d6f7274616c3137340400080000ae00244d6f7274616c3137350400080000af00244d6f7274616c3137360400080000b000244d6f7274616c3137370400080000b100244d6f7274616c3137380400080000b200244d6f7274616c3137390400080000b300244d6f7274616c3138300400080000b400244d6f7274616c3138310400080000b500244d6f7274616c3138320400080000b600244d6f7274616c3138330400080000b700244d6f7274616c3138340400080000b800244d6f7274616c3138350400080000b900244d6f7274616c3138360400080000ba00244d6f7274616c3138370400080000bb00244d6f7274616c3138380400080000bc00244d6f7274616c3138390400080000bd00244d6f7274616c3139300400080000be00244d6f7274616c3139310400080000bf00244d6f7274616c3139320400080000c000244d6f7274616c3139330400080000c100244d6f7274616c3139340400080000c200244d6f7274616c3139350400080000c300244d6f7274616c3139360400080000c400244d6f7274616c3139370400080000c500244d6f7274616c3139380400080000c600244d6f7274616c3139390400080000c700244d6f7274616c3230300400080000c800244d6f7274616c3230310400080000c900244d6f7274616c3230320400080000ca00244d6f7274616c3230330400080000cb00244d6f7274616c3230340400080000cc00244d6f7274616c3230350400080000cd00244d6f7274616c3230360400080000ce00244d6f7274616c3230370400080000cf00244d6f7274616c3230380400080000d000244d6f7274616c3230390400080000d100244d6f7274616c3231300400080000d200244d6f7274616c3231310400080000d300244d6f7274616c3231320400080000d400244d6f7274616c3231330400080000d500244d6f7274616c3231340400080000d600244d6f7274616c3231350400080000d700244d6f7274616c3231360400080000d800244d6f7274616c3231370400080000d900244d6f7274616c3231380400080000da00244d6f7274616c3231390400080000db00244d6f7274616c3232300400080000dc00244d6f7274616c3232310400080000dd00244d6f7274616c3232320400080000de00244d6f7274616c3232330400080000df00244d6f7274616c3232340400080000e000244d6f7274616c3232350400080000e100244d6f7274616c3232360400080000e200244d6f7274616c3232370400080000e300244d6f7274616c3232380400080000e400244d6f7274616c3232390400080000e500244d6f7274616c3233300400080000e600244d6f7274616c3233310400080000e700244d6f7274616c3233320400080000e800244d6f7274616c3233330400080000e900244d6f7274616c3233340400080000ea00244d6f7274616c3233350400080000eb00244d6f7274616c3233360400080000ec00244d6f7274616c3233370400080000ed00244d6f7274616c3233380400080000ee00244d6f7274616c3233390400080000ef00244d6f7274616c3234300400080000f000244d6f7274616c3234310400080000f100244d6f7274616c3234320400080000f200244d6f7274616c3234330400080000f300244d6f7274616c3234340400080000f400244d6f7274616c3234350400080000f500244d6f7274616c3234360400080000f600244d6f7274616c3234370400080000f700244d6f7274616c3234380400080000f800244d6f7274616c3234390400080000f900244d6f7274616c3235300400080000fa00244d6f7274616c3235310400080000fb00244d6f7274616c3235320400080000fc00244d6f7274616c3235330400080000fd00244d6f7274616c3235340400080000fe00244d6f7274616c3235350400080000ff0000cd0a10306672616d655f73797374656d28657874656e73696f6e732c636865636b5f6e6f6e636528436865636b4e6f6e636504045400000400e00120543a3a4e6f6e63650000d10a10306672616d655f73797374656d28657874656e73696f6e7330636865636b5f7765696768742c436865636b57656967687404045400000000d50a10306f70616c5f72756e74696d653872756e74696d655f636f6d6d6f6e2c6d61696e74656e616e636540436865636b4d61696e74656e616e636500000000d90a10306f70616c5f72756e74696d653872756e74696d655f636f6d6d6f6e206964656e746974795044697361626c654964656e7469747943616c6c7300000000dd0a088c70616c6c65745f74656d706c6174655f7472616e73616374696f6e5f7061796d656e74604368617267655472616e73616374696f6e5061796d656e7404045401e10a000400bc013042616c616e63654f663c543e0000e10a08306f70616c5f72756e74696d651c52756e74696d6500000000e50a083c70616c6c65745f657468657265756d6046616b655472616e73616374696f6e46696e616c697a657204045401e10a000000e90a102873705f72756e74696d651c67656e657269634c756e636865636b65645f65787472696e73696348556e636865636b656445787472696e736963101c4164647265737301dd011043616c6c015d01245369676e6174757265019d0a14457874726101b50a00040034000000d41853797374656d011853797374656d401c4163636f756e7401010402000c4101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008004e8205468652066756c6c206163636f756e7420696e666f726d6174696f6e20666f72206120706172746963756c6172206163636f756e742049442e3845787472696e736963436f756e74000010040004b820546f74616c2065787472696e7369637320636f756e7420666f72207468652063757272656e7420626c6f636b2e2c426c6f636b576569676874010020180000000000000488205468652063757272656e742077656967687420666f722074686520626c6f636b2e40416c6c45787472696e736963734c656e000010040004410120546f74616c206c656e6774682028696e2062797465732920666f7220616c6c2065787472696e736963732070757420746f6765746865722c20666f72207468652063757272656e7420626c6f636b2e24426c6f636b486173680101040510308000000000000000000000000000000000000000000000000000000000000000000498204d6170206f6620626c6f636b206e756d6265727320746f20626c6f636b206861736865732e3445787472696e736963446174610101040510340400043d012045787472696e73696373206461746120666f72207468652063757272656e7420626c6f636b20286d61707320616e2065787472696e736963277320696e64657820746f206974732064617461292e184e756d6265720100101000000000040901205468652063757272656e7420626c6f636b206e756d626572206265696e672070726f6365737365642e205365742062792060657865637574655f626c6f636b602e28506172656e744861736801003080000000000000000000000000000000000000000000000000000000000000000004702048617368206f66207468652070726576696f757320626c6f636b2e18446967657374010038040004f020446967657374206f66207468652063757272656e7420626c6f636b2c20616c736f2070617274206f662074686520626c6f636b206865616465722e184576656e747301004804001ca0204576656e7473206465706f736974656420666f72207468652063757272656e7420626c6f636b2e001d01204e4f54453a20546865206974656d20697320756e626f756e6420616e642073686f756c64207468657265666f7265206e657665722062652072656164206f6e20636861696e2ed020497420636f756c64206f746865727769736520696e666c6174652074686520506f562073697a65206f66206120626c6f636b2e002d01204576656e747320686176652061206c6172676520696e2d6d656d6f72792073697a652e20426f7820746865206576656e747320746f206e6f7420676f206f75742d6f662d6d656d6f7279fc206a75737420696e206361736520736f6d656f6e65207374696c6c207265616473207468656d2066726f6d2077697468696e207468652072756e74696d652e284576656e74436f756e74010010100000000004b820546865206e756d626572206f66206576656e747320696e2074686520604576656e74733c543e60206c6973742e2c4576656e74546f70696373010104023025060400282501204d617070696e67206265747765656e206120746f7069632028726570726573656e74656420627920543a3a486173682920616e64206120766563746f72206f6620696e646578657394206f66206576656e747320696e2074686520603c4576656e74733c543e3e60206c6973742e00510120416c6c20746f70696320766563746f727320686176652064657465726d696e69737469632073746f72616765206c6f636174696f6e7320646570656e64696e67206f6e2074686520746f7069632e2054686973450120616c6c6f7773206c696768742d636c69656e747320746f206c6576657261676520746865206368616e67657320747269652073746f7261676520747261636b696e67206d656368616e69736d20616e64e420696e2063617365206f66206368616e67657320666574636820746865206c697374206f66206576656e7473206f6620696e7465726573742e005901205468652076616c756520686173207468652074797065206028426c6f636b4e756d626572466f723c543e2c204576656e74496e646578296020626563617573652069662077652075736564206f6e6c79206a7573744d012074686520604576656e74496e64657860207468656e20696e20636173652069662074686520746f70696320686173207468652073616d6520636f6e74656e7473206f6e20746865206e65787420626c6f636b0101206e6f206e6f74696669636174696f6e2077696c6c20626520747269676765726564207468757320746865206576656e74206d69676874206265206c6f73742e484c61737452756e74696d65557067726164650000290604000455012053746f726573207468652060737065635f76657273696f6e6020616e642060737065635f6e616d6560206f66207768656e20746865206c6173742072756e74696d6520757067726164652068617070656e65642e545570677261646564546f553332526566436f756e74010035010400044d012054727565206966207765206861766520757067726164656420736f207468617420607479706520526566436f756e74602069732060753332602e2046616c7365202864656661756c7429206966206e6f742e605570677261646564546f547269706c65526566436f756e74010035010400085d012054727565206966207765206861766520757067726164656420736f2074686174204163636f756e74496e666f20636f6e7461696e73207468726565207479706573206f662060526566436f756e74602e2046616c736548202864656661756c7429206966206e6f742e38457865637574696f6e506861736500002106040004882054686520657865637574696f6e207068617365206f662074686520626c6f636b2e01610101541830426c6f636b576569676874732d066d01025b1f5d00070088526a7402004001c2a0a91d000107d00918a44b0200d000010700e6bd4f570200f000010000c2a0a91d000107d0abacbe680200200101070088526a7402004001010700a2941a1d02005000c2a0a91d0000000004d020426c6f636b20262065787472696e7369637320776569676874733a20626173652076616c75657320616e64206c696d6974732e2c426c6f636b4c656e6774683d063000003c00000050000000500004a820546865206d6178696d756d206c656e677468206f66206120626c6f636b2028696e206279746573292e38426c6f636b48617368436f756e74101060090000045501204d6178696d756d206e756d626572206f6620626c6f636b206e756d62657220746f20626c6f636b2068617368206d617070696e677320746f206b65657020286f6c64657374207072756e6564206669727374292e20446257656967687445064040787d010000000000e1f505000000000409012054686520776569676874206f662072756e74696d65206461746162617365206f7065726174696f6e73207468652072756e74696d652063616e20696e766f6b652e1c56657273696f6e49064103106f70616c106f70616c01000000cfbd9800000000003c4fdc4d29738b36d503000000144f3c7616f5c8a102000000df6acb689907609b0400000037e397fc7c91f5e40200000040fe3ad401f8959a06000000d2bc9897eed08f1503000000f78b278be53f454c02000000582211f65bb14b8905000000ab3c0572291feb8b01000000dd718d5cc53262d401000000ea93e3f16f3d696202000000bc9d89904f5b923f0100000037c8bb1350a9a2a80400000032d67ae360cae94401000000e65b00e46cedd0aa0200000003000000010484204765742074686520636861696e27732063757272656e742076657273696f6e2e28535335385072656669784901082a0014a8205468652064657369676e61746564205353353820707265666978206f66207468697320636861696e2e0039012054686973207265706c6163657320746865202273733538466f726d6174222070726f7065727479206465636c6172656420696e2074686520636861696e20737065632e20526561736f6e20697331012074686174207468652072756e74696d652073686f756c64206b6e6f772061626f7574207468652070726566697820696e206f7264657220746f206d616b6520757365206f662069742061737020616e206964656e746966696572206f662074686520636861696e2e01590600485374617465547269654d6967726174696f6e01485374617465547269654d6967726174696f6e0c404d6967726174696f6e50726f6365737301007d013800000000000000000000000000001050204d6967726174696f6e2070726f67726573732e005d0120546869732073746f7265732074686520736e617073686f74206f6620746865206c617374206d69677261746564206b6579732e2049742063616e2062652073657420696e746f206d6f74696f6e20616e64206d6f7665d420666f727761726420627920616e79206f6620746865206d65616e732070726f766964656420627920746869732070616c6c65742e284175746f4c696d6974730100750104000cd420546865206c696d69747320746861742061726520696d706f736564206f6e206175746f6d61746963206d6967726174696f6e732e00d42049662073657420746f204e6f6e652c207468656e206e6f206175746f6d61746963206d6967726174696f6e2068617070656e732e605369676e65644d6967726174696f6e4d61784c696d6974730000790104000ce020546865206d6178696d756d206c696d697473207468617420746865207369676e6564206d6967726174696f6e20636f756c64207573652e00b4204966206e6f74207365742c206e6f207369676e6564207375626d697373696f6e20697320616c6c6f7765642e017101017804244d61784b65794c656e10100002000054b4204d6178696d616c206e756d626572206f6620627974657320746861742061206b65792063616e20686176652e00b0204652414d4520697473656c6620646f6573206e6f74206c696d697420746865206b6579206c656e6774682e01012054686520636f6e63726574652076616c7565206d757374207468657265666f726520646570656e64206f6e20796f75722073746f726167652075736167652e59012041205b606672616d655f737570706f72743a3a73746f726167653a3a53746f726167654e4d6170605d20666f72206578616d706c652063616e206861766520616e20617262697472617279206e756d626572206f664501206b65797320776869636820617265207468656e2068617368656420616e6420636f6e636174656e617465642c20726573756c74696e6720696e206172626974726172696c79206c6f6e67206b6579732e0041012055736520746865202a7374617465206d6967726174696f6e205250432a20746f20726574726965766520746865206c656e677468206f6620746865206c6f6e67657374206b657920696e20796f757201012073746f726167653a203c68747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f6973737565732f31313634323e00290120546865206d6967726174696f6e2077696c6c2068616c7420776974682061206048616c74656460206576656e7420696620746869732076616c756520697320746f6f20736d616c6c2e49012053696e6365207468657265206973206e6f207265616c2070656e616c74792066726f6d206f7665722d657374696d6174696e672c206974206973206164766973656420746f207573652061206c61726765802076616c75652e205468652064656661756c742069732035313220627974652e008020536f6d65206b6579206c656e6774687320666f72207265666572656e63653ad0202d205b606672616d655f737570706f72743a3a73746f726167653a3a53746f7261676556616c7565605d3a2033322062797465c8202d205b606672616d655f737570706f72743a3a73746f726167653a3a53746f726167654d6170605d3a2036342062797465e0202d205b606672616d655f737570706f72743a3a73746f726167653a3a53746f72616765446f75626c654d6170605d3a2039362062797465004820466f72206d6f726520696e666f207365653501203c68747470733a2f2f7777772e736861776e74616272697a692e636f6d2f7375627374726174652f7175657279696e672d7375627374726174652d73746f726167652d7669612d7270632f3e0180013c50617261636861696e53797374656d013c50617261636861696e53797374656d6044556e696e636c756465645365676d656e7401005d060400184901204c617465737420696e636c7564656420626c6f636b2064657363656e64616e7473207468652072756e74696d652061636365707465642e20496e206f7468657220776f7264732c20746865736520617265610120616e636573746f7273206f66207468652063757272656e746c7920657865637574696e6720626c6f636b2077686963682068617665206e6f74206265656e20696e636c7564656420696e20746865206f627365727665644c2072656c61792d636861696e2073746174652e00750120546865207365676d656e74206c656e677468206973206c696d69746564206279207468652063617061636974792072657475726e65642066726f6d20746865205b60436f6e73656e737573486f6f6b605d20636f6e666967757265643c20696e207468652070616c6c65742e6c41676772656761746564556e696e636c756465645365676d656e740000810604000c69012053746f72616765206669656c642074686174206b6565707320747261636b206f662062616e64776964746820757365642062792074686520756e696e636c75646564207365676d656e7420616c6f6e672077697468207468655901206c617465737420746865206c61746573742048524d502077617465726d61726b2e205573656420666f72206c696d6974696e672074686520616363657074616e6365206f66206e657720626c6f636b73207769746890207265737065637420746f2072656c617920636861696e20636f6e73747261696e74732e5450656e64696e6756616c69646174696f6e436f6465010034040018590120496e2063617365206f662061207363686564756c656420757067726164652c20746869732073746f72616765206669656c6420636f6e7461696e73207468652076616c69646174696f6e20636f646520746f20626524206170706c6965642e003d0120417320736f6f6e206173207468652072656c617920636861696e2067697665732075732074686520676f2d6168656164207369676e616c2c2077652077696c6c206f7665727772697465207468657101205b603a636f6465605d5b73705f636f72653a3a73746f726167653a3a77656c6c5f6b6e6f776e5f6b6579733a3a434f44455d2077686963682077696c6c20726573756c7420746865206e65787420626c6f636b2070726f636573730901207769746820746865206e65772076616c69646174696f6e20636f64652e205468697320636f6e636c756465732074686520757067726164652070726f636573732e444e657756616c69646174696f6e436f64650000340400145d012056616c69646174696f6e20636f6465207468617420697320736574206279207468652070617261636861696e20616e6420697320746f20626520636f6d6d756e69636174656420746f20636f6c6c61746f7220616e647820636f6e73657175656e746c79207468652072656c61792d636861696e2e00650120546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b206966206e6f206f746865722070616c6c657420616c7265616479207365742c207468652076616c75652e3856616c69646174696f6e446174610000910104000cd020546865205b6050657273697374656456616c69646174696f6e44617461605d2073657420666f72207468697320626c6f636b2e2d0120546869732076616c756520697320657870656374656420746f20626520736574206f6e6c79206f6e63652070657220626c6f636b20616e642069742773206e657665722073746f7265643420696e2074686520747269652e5044696453657456616c69646174696f6e436f646501003501040004e02057657265207468652076616c69646174696f6e20646174612073657420746f206e6f74696679207468652072656c617920636861696e3f644c61737452656c6179436861696e426c6f636b4e756d6265720100101000000000041d01205468652072656c617920636861696e20626c6f636b206e756d626572206173736f636961746564207769746820746865206c6173742070617261636861696e20626c6f636b2e60557067726164655265737472696374696f6e5369676e616c0100850604001c750120416e206f7074696f6e20776869636820696e64696361746573206966207468652072656c61792d636861696e20726573747269637473207369676e616c6c696e6720612076616c69646174696f6e20636f646520757067726164652e610120496e206f7468657220776f7264732c20696620746869732069732060536f6d656020616e64205b604e657756616c69646174696f6e436f6465605d2069732060536f6d6560207468656e207468652070726f64756365646c2063616e6469646174652077696c6c20626520696e76616c69642e00710120546869732073746f72616765206974656d2069732061206d6972726f72206f662074686520636f72726573706f6e64696e672076616c756520666f72207468652063757272656e742070617261636861696e2066726f6d207468656d012072656c61792d636861696e2e20546869732076616c756520697320657068656d6572616c207768696368206d65616e7320697420646f65736e277420686974207468652073746f726167652e20546869732076616c756520697360207365742061667465722074686520696e686572656e742e3855706772616465476f416865616401007906040014dc204f7074696f6e616c207570677261646520676f2d6168656164207369676e616c2066726f6d207468652072656c61792d636861696e2e00710120546869732073746f72616765206974656d2069732061206d6972726f72206f662074686520636f72726573706f6e64696e672076616c756520666f72207468652063757272656e742070617261636861696e2066726f6d207468656d012072656c61792d636861696e2e20546869732076616c756520697320657068656d6572616c207768696368206d65616e7320697420646f65736e277420686974207468652073746f726167652e20546869732076616c756520697360207365742061667465722074686520696e686572656e742e3c52656c6179537461746550726f6f6600009901040018c4205468652073746174652070726f6f6620666f7220746865206c6173742072656c617920706172656e7420626c6f636b2e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e5852656c6576616e744d6573736167696e67537461746500008d0604001c65012054686520736e617073686f74206f6620736f6d652073746174652072656c6174656420746f206d6573736167696e672072656c6576616e7420746f207468652063757272656e742070617261636861696e2061732070657248207468652072656c617920706172656e742e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e44486f7374436f6e66696775726174696f6e0000a1060400182901205468652070617261636861696e20686f737420636f6e66696775726174696f6e207468617420776173206f627461696e65642066726f6d207468652072656c617920706172656e742e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e384c617374446d714d7163486561640100a90680000000000000000000000000000000000000000000000000000000000000000010f420546865206c61737420646f776e77617264206d65737361676520717565756520636861696e20686561642077652068617665206f627365727665642e00650120546869732076616c7565206973206c6f61646564206265666f726520616e642073617665642061667465722070726f63657373696e6720696e626f756e6420646f776e77617264206d65737361676573206361727269656460206279207468652073797374656d20696e686572656e742e404c61737448726d704d716348656164730100ad06040010490120546865206d65737361676520717565756520636861696e2068656164732077652068617665206f62736572766564207065722065616368206368616e6e656c20696e636f6d696e67206368616e6e656c2e00650120546869732076616c7565206973206c6f61646564206265666f726520616e642073617665642061667465722070726f63657373696e6720696e626f756e6420646f776e77617264206d65737361676573206361727269656460206279207468652073797374656d20696e686572656e742e6450726f636573736564446f776e776172644d6573736167657301001010000000000cc8204e756d626572206f6620646f776e77617264206d657373616765732070726f63657373656420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e3448726d7057617465726d61726b01001010000000000ca02048524d502077617465726d61726b2074686174207761732073657420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e5048726d704f7574626f756e644d657373616765730100b90604000ca42048524d50206d65737361676573207468617420776572652073656e7420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e385570776172644d6573736167657301006d0104000cac20557077617264206d65737361676573207468617420776572652073656e7420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e5450656e64696e675570776172644d6573736167657301006d01040004310120557077617264206d65737361676573207468617420617265207374696c6c2070656e64696e6720616e64206e6f74207965742073656e6420746f207468652072656c617920636861696e2e84416e6e6f756e63656448726d704d6573736167657350657243616e646964617465010010100000000008650120546865206e756d626572206f662048524d50206d65737361676573207765206f6273657276656420696e20606f6e5f696e697469616c697a656020616e64207468757320757365642074686174206e756d62657220666f72f020616e6e6f756e63696e672074686520776569676874206f6620606f6e5f696e697469616c697a656020616e6420606f6e5f66696e616c697a65602e68526573657276656458636d705765696768744f766572726964650000240400085d01205468652077656967687420776520726573657276652061742074686520626567696e6e696e67206f662074686520626c6f636b20666f722070726f63657373696e672058434d50206d657373616765732e2054686973b8206f76657272696465732074686520616d6f756e742073657420696e2074686520436f6e6669672074726169742e645265736572766564446d705765696768744f766572726964650000240400085901205468652077656967687420776520726573657276652061742074686520626567696e6e696e67206f662074686520626c6f636b20666f722070726f63657373696e6720444d50206d657373616765732e2054686973b8206f76657272696465732074686520616d6f756e742073657420696e2074686520436f6e6669672074726169742e44417574686f72697a6564557067726164650000c106040004b820546865206e65787420617574686f72697a656420757067726164652c206966207468657265206973206f6e652e60437573746f6d56616c69646174696f6e486561644461746100003404000c2901204120637573746f6d2068656164206461746120746861742073686f756c642062652072657475726e656420617320726573756c74206f66206076616c69646174655f626c6f636b602e00110120536565206050616c6c65743a3a7365745f637573746f6d5f76616c69646174696f6e5f686561645f646174616020666f72206d6f726520696e666f726d6174696f6e2e01890101840001c506143450617261636861696e496e666f013450617261636861696e496e666f042c50617261636861696e49640100ad0110640000000001c1010000001528417574686f72736869700128417574686f72736869700418417574686f720000000400046420417574686f72206f662063757272656e7420626c6f636b2e000000001644436f6c6c61746f7253656c656374696f6e0144436f6c6c61746f7253656c656374696f6e1034496e76756c6e657261626c65730100c9060400048c2054686520696e76756c6e657261626c652c20666978656420636f6c6c61746f72732e404c6963656e73654465706f7369744f66010104020018400000000000000000000000000000000004ac205468652028636f6d6d756e6974792920636f6c6c6174696f6e206c6963656e736520686f6c646572732e2843616e646964617465730100c906040004bc205468652028636f6d6d756e6974792c206c696d697465642920636f6c6c6174696f6e2063616e646964617465732e444c617374417574686f726564426c6f636b01010405001010000000000484204c61737420626c6f636b20617574686f72656420627920636f6c6c61746f722e01c501018c0001cd06171c53657373696f6e011c53657373696f6e1c2856616c696461746f72730100e5010400047c205468652063757272656e7420736574206f662076616c696461746f72732e3043757272656e74496e646578010010100000000004782043757272656e7420696e646578206f66207468652073657373696f6e2e345175657565644368616e67656401003501040008390120547275652069662074686520756e6465726c79696e672065636f6e6f6d6963206964656e746974696573206f7220776569676874696e6720626568696e64207468652076616c696461746f7273a420686173206368616e67656420696e20746865207175657565642076616c696461746f72207365742e285175657565644b6579730100d1060400083d012054686520717565756564206b65797320666f7220746865206e6578742073657373696f6e2e205768656e20746865206e6578742073657373696f6e20626567696e732c207468657365206b657973e02077696c6c206265207573656420746f2064657465726d696e65207468652076616c696461746f7227732073657373696f6e206b6579732e4844697361626c656456616c696461746f7273010031050400148020496e6469636573206f662064697361626c65642076616c696461746f72732e003d01205468652076656320697320616c77617973206b65707420736f7274656420736f20746861742077652063616e2066696e642077686574686572206120676976656e2076616c696461746f722069733d012064697361626c6564207573696e672062696e617279207365617263682e204974206765747320636c6561726564207768656e20606f6e5f73657373696f6e5f656e64696e67602072657475726e73642061206e657720736574206f66206964656e7469746965732e204e6578744b6579730001040500cd010400049c20546865206e6578742073657373696f6e206b65797320666f7220612076616c696461746f722e204b65794f776e657200010405d90600040004090120546865206f776e6572206f662061206b65792e20546865206b65792069732074686520604b657954797065496460202b2074686520656e636f646564206b65792e01c90101900001e106181041757261011041757261082c417574686f7269746965730100e5060400046c205468652063757272656e7420617574686f72697479207365742e2c43757272656e74536c6f740100ed062000000000000000000c80205468652063757272656e7420736c6f74206f66207468697320626c6f636b2e009420546869732077696c6c2062652073657420696e20606f6e5f696e697469616c697a65602e00000000191c41757261457874011c41757261457874082c417574686f7269746965730100e506040014942053657276657320617320636163686520666f722074686520617574686f7269746965732e0071012054686520617574686f72697469657320696e204175526120617265206f7665727772697474656e20696e20606f6e5f696e697469616c697a6560207768656e2077652073776974636820746f2061206e65772073657373696f6e2c5d0120627574207765207265717569726520746865206f6c6420617574686f72697469657320746f2076657269667920746865207365616c207768656e2076616c69646174696e67206120506f562e20546869732077696c6c0d0120616c77617973206265207570646174656420746f20746865206c6174657374204175526120617574686f72697469657320696e20606f6e5f66696e616c697a65602e20536c6f74496e666f0000f10604000cd82043757272656e7420736c6f742070616972656420776974682061206e756d626572206f6620617574686f72656420626c6f636b732e00982055706461746564206f6e206561636820626c6f636b20696e697469616c697a6174696f6e2e000000001a2042616c616e636573012042616c616e6365731c34546f74616c49737375616e6365010018400000000000000000000000000000000004982054686520746f74616c20756e6974732069737375656420696e207468652073797374656d2e40496e61637469766549737375616e636501001840000000000000000000000000000000000409012054686520746f74616c20756e697473206f66206f75747374616e64696e672064656163746976617465642062616c616e636520696e207468652073797374656d2e1c4163636f756e74010104020014010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080600901205468652042616c616e6365732070616c6c6574206578616d706c65206f662073746f72696e67207468652062616c616e6365206f6620616e206163636f756e742e00282023204578616d706c650034206060606e6f636f6d70696c65b02020696d706c2070616c6c65745f62616c616e6365733a3a436f6e66696720666f722052756e74696d65207b19022020202074797065204163636f756e7453746f7265203d2053746f726167654d61705368696d3c53656c663a3a4163636f756e743c52756e74696d653e2c206672616d655f73797374656d3a3a50726f76696465723c52756e74696d653e2c204163636f756e7449642c2053656c663a3a4163636f756e74446174613c42616c616e63653e3e0c20207d102060606000150120596f752063616e20616c736f2073746f7265207468652062616c616e6365206f6620616e206163636f756e7420696e20746865206053797374656d602070616c6c65742e00282023204578616d706c650034206060606e6f636f6d70696c65b02020696d706c2070616c6c65745f62616c616e6365733a3a436f6e66696720666f722052756e74696d65207b7420202074797065204163636f756e7453746f7265203d2053797374656d0c20207d102060606000510120427574207468697320636f6d657320776974682074726164656f6666732c2073746f72696e67206163636f756e742062616c616e63657320696e207468652073797374656d2070616c6c65742073746f7265736d0120606672616d655f73797374656d60206461746120616c6f6e677369646520746865206163636f756e74206461746120636f6e747261727920746f2073746f72696e67206163636f756e742062616c616e63657320696e207468652901206042616c616e636573602070616c6c65742c20776869636820757365732061206053746f726167654d61706020746f2073746f72652062616c616e6365732064617461206f6e6c792e4101204e4f54453a2054686973206973206f6e6c79207573656420696e207468652063617365207468617420746869732070616c6c6574206973207573656420746f2073746f72652062616c616e6365732e144c6f636b730101040200f506040008b820416e79206c6971756964697479206c6f636b73206f6e20736f6d65206163636f756e742062616c616e6365732e2501204e4f54453a2053686f756c64206f6e6c79206265206163636573736564207768656e2073657474696e672c206368616e67696e6720616e642066726565696e672061206c6f636b2e20526573657276657301010402000507040004a4204e616d6564207265736572766573206f6e20736f6d65206163636f756e742062616c616e6365732e14486f6c6473010104020011070400046c20486f6c6473206f6e206163636f756e742062616c616e6365732e1c467265657a6573010104020025070400048820467265657a65206c6f636b73206f6e206163636f756e742062616c616e6365732e01d901019414484578697374656e7469616c4465706f73697418400000000000000000000000000000000020410120546865206d696e696d756d20616d6f756e7420726571756972656420746f206b65657020616e206163636f756e74206f70656e2e204d5553542042452047524541544552205448414e205a45524f2100590120496620796f75202a7265616c6c792a206e65656420697420746f206265207a65726f2c20796f752063616e20656e61626c652074686520666561747572652060696e7365637572655f7a65726f5f65646020666f72610120746869732070616c6c65742e20486f77657665722c20796f7520646f20736f20617420796f7572206f776e207269736b3a20746869732077696c6c206f70656e2075702061206d616a6f7220446f5320766563746f722e590120496e206361736520796f752068617665206d756c7469706c6520736f7572636573206f662070726f7669646572207265666572656e6365732c20796f75206d617920616c736f2067657420756e65787065637465648c206265686176696f757220696620796f7520736574207468697320746f207a65726f2e00f020426f74746f6d206c696e653a20446f20796f757273656c662061206661766f757220616e64206d616b65206974206174206c65617374206f6e6521204d61784c6f636b7310103200000008f420546865206d6178696d756d206e756d626572206f66206c6f636b7320746861742073686f756c64206578697374206f6e20616e206163636f756e742edc204e6f74207374726963746c7920656e666f726365642c20627574207573656420666f722077656967687420657374696d6174696f6e2e2c4d61785265736572766573101032000000040d0120546865206d6178696d756d206e756d626572206f66206e616d656420726573657276657320746861742063616e206578697374206f6e20616e206163636f756e742e204d6178486f6c647310100a00000004190120546865206d6178696d756d206e756d626572206f6620686f6c647320746861742063616e206578697374206f6e20616e206163636f756e7420617420616e792074696d652e284d6178467265657a657310100a00000004610120546865206d6178696d756d206e756d626572206f6620696e646976696475616c20667265657a65206c6f636b7320746861742063616e206578697374206f6e20616e206163636f756e7420617420616e792074696d652e0131071e2454696d657374616d70012454696d657374616d70080c4e6f7701002c20000000000000000004902043757272656e742074696d6520666f72207468652063757272656e7420626c6f636b2e2444696455706461746501003501040004b420446964207468652074696d657374616d7020676574207570646174656420696e207468697320626c6f636b3f01e9010004344d696e696d756d506572696f642c207017000000000000104d0120546865206d696e696d756d20706572696f64206265747765656e20626c6f636b732e204265776172652074686174207468697320697320646966666572656e7420746f20746865202a65787065637465642a5d0120706572696f6420746861742074686520626c6f636b2070726f64756374696f6e206170706172617475732070726f76696465732e20596f75722063686f73656e20636f6e73656e7375732073797374656d2077696c6c5d012067656e6572616c6c7920776f726b2077697468207468697320746f2064657465726d696e6520612073656e7369626c6520626c6f636b2074696d652e20652e672e20466f7220417572612c2069742077696c6c206265a020646f75626c65207468697320706572696f64206f6e2064656661756c742073657474696e67732e0020485472616e73616374696f6e5061796d656e7401485472616e73616374696f6e5061796d656e7408444e6578744665654d756c7469706c6965720100350740000064a7b3b6e00d0000000000000000003853746f7261676556657273696f6e0100390704000000019c04604f7065726174696f6e616c4665654d756c7469706c696572080405545901204120666565206d756c6974706c69657220666f7220604f7065726174696f6e616c602065787472696e7369637320746f20636f6d7075746520227669727475616c207469702220746f20626f6f73742074686569722c20607072696f7269747960004d0120546869732076616c7565206973206d756c7469706c656420627920746865206066696e616c5f6665656020746f206f627461696e206120227669727475616c20746970222074686174206973206c61746572f420616464656420746f20612074697020636f6d706f6e656e7420696e20726567756c617220607072696f72697479602063616c63756c6174696f6e732e4d01204974206d65616e732074686174206120604e6f726d616c60207472616e73616374696f6e2063616e2066726f6e742d72756e20612073696d696c61726c792d73697a656420604f7065726174696f6e616c6041012065787472696e736963202877697468206e6f20746970292c20627920696e636c7564696e672061207469702076616c75652067726561746572207468616e20746865207669727475616c207469702e003c20606060727573742c69676e6f726540202f2f20466f7220604e6f726d616c608c206c6574207072696f72697479203d207072696f726974795f63616c6328746970293b0054202f2f20466f7220604f7065726174696f6e616c601101206c6574207669727475616c5f746970203d2028696e636c7573696f6e5f666565202b2074697029202a204f7065726174696f6e616c4665654d756c7469706c6965723bc4206c6574207072696f72697479203d207072696f726974795f63616c6328746970202b207669727475616c5f746970293b1020606060005101204e6f746520746861742073696e636520776520757365206066696e616c5f6665656020746865206d756c7469706c696572206170706c69657320616c736f20746f2074686520726567756c61722060746970605d012073656e74207769746820746865207472616e73616374696f6e2e20536f2c206e6f74206f6e6c7920646f657320746865207472616e73616374696f6e206765742061207072696f726974792062756d702062617365646101206f6e207468652060696e636c7573696f6e5f666565602c2062757420776520616c736f20616d706c6966792074686520696d70616374206f662074697073206170706c69656420746f20604f7065726174696f6e616c6038207472616e73616374696f6e732e002120547265617375727901205472656173757279103450726f706f73616c436f756e74010010100000000004a4204e756d626572206f662070726f706f73616c7320746861742068617665206265656e206d6164652e2450726f706f73616c7300010405103d070400047c2050726f706f73616c7320746861742068617665206265656e206d6164652e2c4465616374697661746564010018400000000000000000000000000000000004f02054686520616d6f756e7420776869636820686173206265656e207265706f7274656420617320696e61637469766520746f2043757272656e63792e24417070726f76616c7301004107040004f82050726f706f73616c20696e646963657320746861742068617665206265656e20617070726f76656420627574206e6f742079657420617761726465642e01ed0101a01c3050726f706f73616c426f6e6445071050c30000085501204672616374696f6e206f6620612070726f706f73616c27732076616c756520746861742073686f756c6420626520626f6e64656420696e206f7264657220746f20706c616365207468652070726f706f73616c2e110120416e2061636365707465642070726f706f73616c2067657473207468657365206261636b2e20412072656a65637465642070726f706f73616c20646f6573206e6f742e4c50726f706f73616c426f6e644d696e696d756d1840000064a7b3b6e00d0000000000000000044901204d696e696d756d20616d6f756e74206f662066756e647320746861742073686f756c6420626520706c6163656420696e2061206465706f73697420666f72206d616b696e6720612070726f706f73616c2e4c50726f706f73616c426f6e644d6178696d756d250544010000a0dec5adc9353600000000000000044901204d6178696d756d20616d6f756e74206f662066756e647320746861742073686f756c6420626520706c6163656420696e2061206465706f73697420666f72206d616b696e6720612070726f706f73616c2e2c5370656e64506572696f64101019000000048820506572696f64206265747765656e2073756363657373697665207370656e64732e104275726e450710000000000411012050657263656e74616765206f662073706172652066756e64732028696620616e7929207468617420617265206275726e7420706572207370656e6420706572696f642e2050616c6c6574496449072070792f74727372790419012054686520747265617375727927732070616c6c65742069642c207573656420666f72206465726976696e672069747320736f7665726569676e206163636f756e742049442e304d6178417070726f76616c731010640000000c150120546865206d6178696d756d206e756d626572206f6620617070726f76616c7320746861742063616e207761697420696e20746865207370656e64696e672071756575652e004d01204e4f54453a205468697320706172616d6574657220697320616c736f20757365642077697468696e2074686520426f756e746965732050616c6c657420657874656e73696f6e20696620656e61626c65642e014d0722105375646f01105375646f040c4b6579000000040004842054686520604163636f756e74496460206f6620746865207375646f206b65792e01f10101a400015107231c56657374696e67011c56657374696e67044056657374696e675363686564756c65730101040200550704000c842056657374696e67207363686564756c6573206f6620616e206163636f756e742e00e02056657374696e675363686564756c65733a206d6170204163636f756e744964203d3e205665633c56657374696e675363686564756c653e01f50101b404444d696e5665737465645472616e7366657218400000e8890423c78a000000000000000004e820546865206d696e696d756d20616d6f756e74207472616e7366657272656420746f2063616c6c20607665737465645f7472616e73666572602e015907251c58546f6b656e730001fd0101c0083053656c664c6f636174696f6ed414010100bd2004542053656c6620636861696e206c6f636174696f6e2e344261736558636d57656967687424180284d717a10f104420426173652058434d207765696768742e00f8205468652061637475616c6c792077656967687420666f7220616e2058434d206d6573736167652069732060543a3a4261736558636d576569676874202b6c20543a3a576569676865723a3a77656967687428266d736729602e015d072618546f6b656e730118546f6b656e731034546f74616c49737375616e6365010104050d0118400000000000000000000000000000000004902054686520746f74616c2069737375616e6365206f66206120746f6b656e20747970652e144c6f636b73010108020561076507040008d820416e79206c6971756964697479206c6f636b73206f66206120746f6b656e207479706520756e64657220616e206163636f756e742e2501204e4f54453a2053686f756c64206f6e6c79206265206163636573736564207768656e2073657474696e672c206368616e67696e6720616e642066726565696e672061206c6f636b2e204163636f756e7473010108020561077107c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018b8205468652062616c616e6365206f66206120746f6b656e207479706520756e64657220616e206163636f756e742e00fc204e4f54453a2049662074686520746f74616c2069732065766572207a65726f2c206465637265617365206163636f756e7420726566206163636f756e742e001901204e4f54453a2054686973206973206f6e6c79207573656420696e20746865206361736520746861742074686973206d6f64756c65206973207573656420746f2073746f7265282062616c616e6365732e205265736572766573010108020561077507040004a4204e616d6564207265736572766573206f6e20736f6d65206163636f756e742062616c616e6365732e014d0201090108204d61784c6f636b73101032000000002c4d61785265736572766573101032000000040d0120546865206d6178696d756d206e756d626572206f66206e616d656420726573657276657320746861742063616e206578697374206f6e20616e206163636f756e742e01810727204964656e7469747901204964656e7469747910284964656e746974794f660001040500f90204000c210120496e666f726d6174696f6e20746861742069732070657274696e656e7420746f206964656e746966792074686520656e7469747920626568696e6420616e206163636f756e742e00c02054574f582d4e4f54453a204f4b20e2809520604163636f756e7449646020697320612073656375726520686173682e1c53757065724f660001040200e1020400086101205468652073757065722d6964656e74697479206f6620616e20616c7465726e6174697665202273756222206964656e7469747920746f676574686572207769746820697473206e616d652c2077697468696e2074686174510120636f6e746578742e20496620746865206163636f756e74206973206e6f7420736f6d65206f74686572206163636f756e742773207375622d6964656e746974792c207468656e206a75737420604e6f6e65602e18537562734f660101040500850744000000000000000000000000000000000014b820416c7465726e6174697665202273756222206964656e746974696573206f662074686973206163636f756e742e001d0120546865206669727374206974656d20697320746865206465706f7369742c20746865207365636f6e64206973206120766563746f72206f6620746865206163636f756e74732e00c02054574f582d4e4f54453a204f4b20e2809520604163636f756e7449646020697320612073656375726520686173682e285265676973747261727301008d070400104d012054686520736574206f6620726567697374726172732e204e6f7420657870656374656420746f206765742076657279206269672061732063616e206f6e6c79206265206164646564207468726f7567682061a8207370656369616c206f726967696e20286c696b656c79206120636f756e63696c206d6f74696f6e292e0029012054686520696e64657820696e746f20746869732063616e206265206361737420746f2060526567697374726172496e6465786020746f2067657420612076616c69642076616c75652e015102011501183042617369634465706f73697418400000e8890423c78a000000000000000004d42054686520616d6f756e742068656c64206f6e206465706f73697420666f7220612072656769737465726564206964656e74697479304669656c644465706f7369741840008062175ed158000000000000000000042d012054686520616d6f756e742068656c64206f6e206465706f73697420706572206164646974696f6e616c206669656c6420666f7220612072656769737465726564206964656e746974792e445375624163636f756e744465706f73697418400000c84e676dc11b00000000000000000c65012054686520616d6f756e742068656c64206f6e206465706f73697420666f7220612072656769737465726564207375626163636f756e742e20546869732073686f756c64206163636f756e7420666f7220746865206661637465012074686174206f6e652073746f72616765206974656d27732076616c75652077696c6c20696e637265617365206279207468652073697a65206f6620616e206163636f756e742049442c20616e642074686572652077696c6c350120626520616e6f746865722074726965206974656d2077686f73652076616c7565206973207468652073697a65206f6620616e206163636f756e7420494420706c75732033322062797465732e384d61785375624163636f756e7473101064000000040d0120546865206d6178696d756d206e756d626572206f66207375622d6163636f756e747320616c6c6f77656420706572206964656e746966696564206163636f756e742e4c4d61784164646974696f6e616c4669656c6473101064000000086501204d6178696d756d206e756d626572206f66206164646974696f6e616c206669656c64732074686174206d61792062652073746f72656420696e20616e2049442e204e656564656420746f20626f756e642074686520492f4fe020726571756972656420746f2061636365737320616e206964656e746974792c206275742063616e2062652070726574747920686967682e344d617852656769737472617273101014000000085101204d61786d696d756d206e756d626572206f66207265676973747261727320616c6c6f77656420696e207468652073797374656d2e204e656564656420746f20626f756e642074686520636f6d706c65786974797c206f662c20652e672e2c207570646174696e67206a756467656d656e74732e019d072820507265696d6167650120507265696d6167650824537461747573466f720001040630a1070400049020546865207265717565737420737461747573206f66206120676976656e20686173682e2c507265696d616765466f7200010406ad07b1070400000119030119010001b507292444656d6f6372616379012444656d6f6372616379303c5075626c696350726f70436f756e74010010100000000004f420546865206e756d626572206f6620287075626c6963292070726f706f73616c7320746861742068617665206265656e206d61646520736f206661722e2c5075626c696350726f70730100b907040004050120546865207075626c69632070726f706f73616c732e20556e736f727465642e20546865207365636f6e64206974656d206973207468652070726f706f73616c2e244465706f7369744f660001040510c50704000c842054686f73652077686f2068617665206c6f636b65642061206465706f7369742e00d82054574f582d4e4f54453a20536166652c20617320696e6372656173696e6720696e7465676572206b6579732061726520736166652e3c5265666572656e64756d436f756e74010010100000000004310120546865206e6578742066726565207265666572656e64756d20696e6465782c20616b6120746865206e756d626572206f66207265666572656e6461207374617274656420736f206661722e344c6f77657374556e62616b6564010010100000000008250120546865206c6f77657374207265666572656e64756d20696e64657820726570726573656e74696e6720616e20756e62616b6564207265666572656e64756d2e20457175616c20746fdc20605265666572656e64756d436f756e74602069662074686572652069736e2774206120756e62616b6564207265666572656e64756d2e405265666572656e64756d496e666f4f660001040510cd0704000cb420496e666f726d6174696f6e20636f6e6365726e696e6720616e7920676976656e207265666572656e64756d2e0009012054574f582d4e4f54453a205341464520617320696e646578657320617265206e6f7420756e64657220616e2061747461636b6572e280997320636f6e74726f6c2e20566f74696e674f660101040500d907d8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000105d0120416c6c20766f74657320666f72206120706172746963756c617220766f7465722e2057652073746f7265207468652062616c616e636520666f7220746865206e756d626572206f6620766f74657320746861742077655d012068617665207265636f726465642e20546865207365636f6e64206974656d2069732074686520746f74616c20616d6f756e74206f662064656c65676174696f6e732c20746861742077696c6c2062652061646465642e00e82054574f582d4e4f54453a205341464520617320604163636f756e7449646073206172652063727970746f2068617368657320616e797761792e544c6173745461626c656457617345787465726e616c010035010400085901205472756520696620746865206c617374207265666572656e64756d207461626c656420776173207375626d69747465642065787465726e616c6c792e2046616c7365206966206974207761732061207075626c6963282070726f706f73616c2e304e65787445787465726e616c0000f107040010590120546865207265666572656e64756d20746f206265207461626c6564207768656e6576657220697420776f756c642062652076616c696420746f207461626c6520616e2065787465726e616c2070726f706f73616c2e550120546869732068617070656e73207768656e2061207265666572656e64756d206e6565647320746f206265207461626c656420616e64206f6e65206f662074776f20636f6e646974696f6e7320617265206d65743aa4202d20604c6173745461626c656457617345787465726e616c60206973206066616c7365603b206f7268202d20605075626c696350726f70736020697320656d7074792e24426c61636b6c6973740001040630f50704000851012041207265636f7264206f662077686f207665746f656420776861742e204d6170732070726f706f73616c206861736820746f206120706f737369626c65206578697374656e7420626c6f636b206e756d626572e82028756e74696c207768656e206974206d6179206e6f742062652072657375626d69747465642920616e642077686f207665746f65642069742e3443616e63656c6c6174696f6e73010104063035010400042901205265636f7264206f6620616c6c2070726f706f73616c7320746861742068617665206265656e207375626a65637420746f20656d657267656e63792063616e63656c6c6174696f6e2e284d657461646174614f66000104022d0130040018ec2047656e6572616c20696e666f726d6174696f6e20636f6e6365726e696e6720616e792070726f706f73616c206f72207265666572656e64756d2e6901205468652060507265696d61676548617368602072656665727320746f2074686520707265696d616765206f66207468652060507265696d61676573602070726f76696465722077686963682063616e2062652061204a534f4e882064756d70206f7220495046532068617368206f662061204a534f4e2066696c652e00750120436f6e73696465722061206761726261676520636f6c6c656374696f6e20666f722061206d65746164617461206f662066696e6973686564207265666572656e64756d7320746f2060756e7265717565737460202872656d6f76652944206c6172676520707265696d616765732e011d03011d01303c456e6163746d656e74506572696f6410101900000014e82054686520706572696f64206265747765656e20612070726f706f73616c206265696e6720617070726f76656420616e6420656e61637465642e0031012049742073686f756c642067656e6572616c6c792062652061206c6974746c65206d6f7265207468616e2074686520756e7374616b6520706572696f6420746f20656e737572652074686174510120766f74696e67207374616b657273206861766520616e206f70706f7274756e69747920746f2072656d6f7665207468656d73656c7665732066726f6d207468652073797374656d20696e207468652063617365b4207768657265207468657920617265206f6e20746865206c6f73696e672073696465206f66206120766f74652e304c61756e6368506572696f6410102c01000004e420486f77206f6674656e2028696e20626c6f636b7329206e6577207075626c6963207265666572656e646120617265206c61756e636865642e30566f74696e67506572696f6410102c01000004b820486f77206f6674656e2028696e20626c6f636b732920746f20636865636b20666f72206e657720766f7465732e44566f74654c6f636b696e67506572696f64101019000000109020546865206d696e696d756d20706572696f64206f6620766f7465206c6f636b696e672e0065012049742073686f756c64206265206e6f2073686f72746572207468616e20656e6163746d656e7420706572696f6420746f20656e73757265207468617420696e207468652063617365206f6620616e20617070726f76616c2c49012074686f7365207375636365737366756c20766f7465727320617265206c6f636b656420696e746f2074686520636f6e73657175656e636573207468617420746865697220766f74657320656e7461696c2e384d696e696d756d4465706f73697418400000000000000000000000000000000004350120546865206d696e696d756d20616d6f756e7420746f20626520757365642061732061206465706f73697420666f722061207075626c6963207265666572656e64756d2070726f706f73616c2e38496e7374616e74416c6c6f776564350104000c550120496e64696361746f7220666f72207768657468657220616e20656d657267656e6379206f726967696e206973206576656e20616c6c6f77656420746f2068617070656e2e20536f6d6520636861696e73206d617961012077616e7420746f207365742074686973207065726d616e656e746c7920746f206066616c7365602c206f7468657273206d61792077616e7420746f20636f6e646974696f6e206974206f6e207468696e67732073756368a020617320616e207570677261646520686176696e672068617070656e656420726563656e746c792e5446617374547261636b566f74696e67506572696f6410103200000004ec204d696e696d756d20766f74696e6720706572696f6420616c6c6f77656420666f72206120666173742d747261636b207265666572656e64756d2e34436f6f6c6f6666506572696f6410102c01000004610120506572696f6420696e20626c6f636b7320776865726520616e2065787465726e616c2070726f706f73616c206d6179206e6f742062652072652d7375626d6974746564206166746572206265696e67207665746f65642e204d6178566f74657310106400000010b020546865206d6178696d756d206e756d626572206f6620766f74657320666f7220616e206163636f756e742e00d420416c736f207573656420746f20636f6d70757465207765696768742c20616e206f7665726c79206269672076616c75652063616e1501206c65616420746f2065787472696e7369632077697468207665727920626967207765696768743a20736565206064656c65676174656020666f7220696e7374616e63652e304d617850726f706f73616c73101064000000040d0120546865206d6178696d756d206e756d626572206f66207075626c69632070726f706f73616c7320746861742063616e20657869737420617420616e792074696d652e2c4d61784465706f73697473101064000000041d0120546865206d6178696d756d206e756d626572206f66206465706f736974732061207075626c69632070726f706f73616c206d6179206861766520617420616e792074696d652e384d6178426c61636b6c697374656410106400000004d820546865206d6178696d756d206e756d626572206f66206974656d732077686963682063616e20626520626c61636b6c69737465642e01f9072a1c436f756e63696c011c436f756e63696c182450726f706f73616c730100fd07040004902054686520686173686573206f6620746865206163746976652070726f706f73616c732e2850726f706f73616c4f6600010406305d01040004cc2041637475616c2070726f706f73616c20666f72206120676976656e20686173682c20696620697427732063757272656e742e18566f74696e6700010406300108040004b420566f746573206f6e206120676976656e2070726f706f73616c2c206966206974206973206f6e676f696e672e3450726f706f73616c436f756e74010010100000000004482050726f706f73616c7320736f206661722e1c4d656d626572730100e5010400043901205468652063757272656e74206d656d62657273206f662074686520636f6c6c6563746976652e20546869732069732073746f72656420736f7274656420286a7573742062792076616c7565292e145072696d65000000040004650120546865207072696d65206d656d62657220746861742068656c70732064657465726d696e65207468652064656661756c7420766f7465206265686176696f7220696e2063617365206f6620616273656e746174696f6e732e012d0301310104444d617850726f706f73616c57656967687424280700a0db215d0200000104250120546865206d6178696d756d20776569676874206f6620612064697370617463682063616c6c20746861742063616e2062652070726f706f73656420616e642065786563757465642e0105082b48546563686e6963616c436f6d6d69747465650148546563686e6963616c436f6d6d6974746565182450726f706f73616c7301000908040004902054686520686173686573206f6620746865206163746976652070726f706f73616c732e2850726f706f73616c4f6600010406305d01040004cc2041637475616c2070726f706f73616c20666f72206120676976656e20686173682c20696620697427732063757272656e742e18566f74696e6700010406300108040004b420566f746573206f6e206120676976656e2070726f706f73616c2c206966206974206973206f6e676f696e672e3450726f706f73616c436f756e74010010100000000004482050726f706f73616c7320736f206661722e1c4d656d626572730100e5010400043901205468652063757272656e74206d656d62657273206f662074686520636f6c6c6563746976652e20546869732069732073746f72656420736f7274656420286a7573742062792076616c7565292e145072696d65000000040004650120546865207072696d65206d656d62657220746861742068656c70732064657465726d696e65207468652064656661756c7420766f7465206265686176696f7220696e2063617365206f6620616273656e746174696f6e732e01310301390104444d617850726f706f73616c57656967687424280700a0db215d0200000104250120546865206d6178696d756d20776569676874206f6620612064697370617463682063616c6c20746861742063616e2062652070726f706f73656420616e642065786563757465642e010d082c44436f756e63696c4d656d626572736869700144436f756e63696c4d656d62657273686970081c4d656d6265727301001108040004c8205468652063757272656e74206d656d626572736869702c2073746f72656420617320616e206f726465726564205665632e145072696d65000000040004a4205468652063757272656e74207072696d65206d656d6265722c206966206f6e65206578697374732e013503013d01000115082d70546563686e6963616c436f6d6d69747465654d656d626572736869700170546563686e6963616c436f6d6d69747465654d656d62657273686970081c4d656d6265727301001908040004c8205468652063757272656e74206d656d626572736869702c2073746f72656420617320616e206f726465726564205665632e145072696d65000000040004a4205468652063757272656e74207072696d65206d656d6265722c206966206f6e65206578697374732e01390301410100011d082e5046656c6c6f7773686970436f6c6c656374697665015046656c6c6f7773686970436f6c6c656374697665182c4d656d626572436f756e7401010405490110100000000008690120546865206e756d626572206f66206d656d6265727320696e2074686520636f6c6c6563746976652077686f2068617665206174206c65617374207468652072616e6b206163636f7264696e6720746f2074686520696e64657830206f6620746865207665632e1c4d656d62657273000104050021080400049c205468652063757272656e74206d656d62657273206f662074686520636f6c6c6563746976652e244964546f496e646578000108050525081004000461012054686520696e646578206f6620656163682072616e6b732773206d656d62657220696e746f207468652067726f7570206f66206d656d626572732077686f2068617665206174206c6561737420746861742072616e6b2e24496e646578546f496400010805052908000400085d0120546865206d656d6265727320696e2074686520636f6c6c65637469766520627920696e6465782e20416c6c20696e646963657320696e207468652072616e67652060302e2e4d656d626572436f756e74602077696c6c65012072657475726e2060536f6d65602c20686f77657665722061206d656d626572277320696e646578206973206e6f742067756172616e7465656420746f2072656d61696e20756e6368616e676564206f7665722074696d652e18566f74696e6700010802052d084d01040004b420566f746573206f6e206120676976656e2070726f706f73616c2c206966206974206973206f6e676f696e672e34566f74696e67436c65616e757000010402103108040000013d03014501000135082f4c46656c6c6f77736869705265666572656e6461014c46656c6c6f77736869705265666572656e6461143c5265666572656e64756d436f756e74010010100000000004310120546865206e6578742066726565207265666572656e64756d20696e6465782c20616b6120746865206e756d626572206f66207265666572656e6461207374617274656420736f206661722e445265666572656e64756d496e666f466f7200010402103908040004b420496e666f726d6174696f6e20636f6e6365726e696e6720616e7920676976656e207265666572656e64756d2e28547261636b517565756501010405490159080400105d012054686520736f72746564206c697374206f66207265666572656e646120726561647920746f206265206465636964656420627574206e6f7420796574206265696e6720646563696465642c206f7264657265642062797c20636f6e76696374696f6e2d776569676874656420617070726f76616c732e00410120546869732073686f756c6420626520656d70747920696620604465636964696e67436f756e7460206973206c657373207468616e2060547261636b496e666f3a3a6d61785f6465636964696e67602e344465636964696e67436f756e7401010405490110100000000004c420546865206e756d626572206f66207265666572656e6461206265696e6720646563696465642063757272656e746c792e284d657461646174614f66000104021030040018050120546865206d6574616461746120697320612067656e6572616c20696e666f726d6174696f6e20636f6e6365726e696e6720746865207265666572656e64756d2e6901205468652060507265696d61676548617368602072656665727320746f2074686520707265696d616765206f66207468652060507265696d61676573602070726f76696465722077686963682063616e2062652061204a534f4e882064756d70206f7220495046532068617368206f662061204a534f4e2066696c652e00750120436f6e73696465722061206761726261676520636f6c6c656374696f6e20666f722061206d65746164617461206f662066696e6973686564207265666572656e64756d7320746f2060756e7265717565737460202872656d6f76652944206c6172676520707265696d616765732e01410301550114445375626d697373696f6e4465706f7369741840e803000000000000000000000000000004350120546865206d696e696d756d20616d6f756e7420746f20626520757365642061732061206465706f73697420666f722061207075626c6963207265666572656e64756d2070726f706f73616c2e244d617851756575656410106400000004e4204d6178696d756d2073697a65206f6620746865207265666572656e64756d20717565756520666f7220612073696e676c6520747261636b2e44556e6465636964696e6754696d656f757410102c01000008550120546865206e756d626572206f6620626c6f636b73206166746572207375626d697373696f6e20746861742061207265666572656e64756d206d75737420626567696e206265696e6720646563696465642062792ee4204f6e63652074686973207061737365732c207468656e20616e796f6e65206d61792063616e63656c20746865207265666572656e64756d2e34416c61726d496e74657276616c1010010000000c5d01205175616e74697a6174696f6e206c6576656c20666f7220746865207265666572656e64756d2077616b657570207363686564756c65722e204120686967686572206e756d6265722077696c6c20726573756c7420696e5d012066657765722073746f726167652072656164732f777269746573206e656564656420666f7220736d616c6c657220766f746572732c2062757420616c736f20726573756c7420696e2064656c61797320746f207468655501206175746f6d61746963207265666572656e64756d20737461747573206368616e6765732e204578706c6963697420736572766963696e6720696e737472756374696f6e732061726520756e61666665637465642e18547261636b735d085501040a004c64656d6f63726163795f70726f706f73616c730a0000000000e8890423c78a0000000000000000190000002c01000032000000050000000000ca9a3b0065cd1d00ca9a3b0000ca9a3b000000000065cd1d04e020496e666f726d6174696f6e20636f6e6365726e696e672074686520646966666572656e74207265666572656e64756d20747261636b732e01750830245363686564756c657201245363686564756c65720c3c496e636f6d706c65746553696e6365000010040000184167656e6461010104051079080400044d01204974656d7320746f2062652065786563757465642c20696e64657865642062792074686520626c6f636b206e756d626572207468617420746865792073686f756c64206265206578656375746564206f6e2e184c6f6f6b757000010405047903040010f8204c6f6f6b75702066726f6d2061206e616d6520746f2074686520626c6f636b206e756d62657220616e6420696e646578206f6620746865207461736b2e00590120466f72207633202d3e207634207468652070726576696f75736c7920756e626f756e646564206964656e7469746965732061726520426c616b65322d3235362068617368656420746f20666f726d2074686520763430206964656e7469746965732e01710301b90508344d6178696d756d57656967687424280700b864d9450200c00004290120546865206d6178696d756d207765696768742074686174206d6179206265207363686564756c65642070657220626c6f636b20666f7220616e7920646973706174636861626c65732e504d61785363686564756c6564506572426c6f636b101032000000141d0120546865206d6178696d756d206e756d626572206f66207363686564756c65642063616c6c7320696e2074686520717565756520666f7220612073696e676c6520626c6f636b2e0018204e4f54453a5101202b20446570656e64656e742070616c6c657473272062656e63686d61726b73206d696768742072657175697265206120686967686572206c696d697420666f72207468652073657474696e672e205365742061c420686967686572206c696d697420756e646572206072756e74696d652d62656e63686d61726b736020666561747572652e018908311c4f726967696e730000000000632458636d705175657565012458636d7051756575652844496e626f756e6458636d7053746174757301008d080400049420537461747573206f662074686520696e626f756e642058434d50206368616e6e656c732e4c496e626f756e6458636d704d657373616765730101080205a50834040004190120496e626f756e64206167677265676174652058434d50206d657373616765732e2049742063616e206f6e6c79206265206f6e6520706572205061726149642f626c6f636b2e484f7574626f756e6458636d705374617475730100a9080400185d0120546865206e6f6e2d656d7074792058434d50206368616e6e656c7320696e206f72646572206f66206265636f6d696e67206e6f6e2d656d7074792c20616e642074686520696e646578206f6620746865206669727374510120616e64206c617374206f7574626f756e64206d6573736167652e204966207468652074776f20696e64696365732061726520657175616c2c207468656e20697420696e6469636174657320616e20656d707479590120717565756520616e64207468657265206d7573742062652061206e6f6e2d604f6b6020604f7574626f756e64537461747573602e20576520617373756d65207175657565732067726f77206e6f20677265617465725901207468616e203635353335206974656d732e20517565756520696e646963657320666f72206e6f726d616c206d6573736167657320626567696e206174206f6e653b207a65726f20697320726573657276656420696e11012063617365206f6620746865206e65656420746f2073656e64206120686967682d7072696f72697479207369676e616c206d657373616765207468697320626c6f636b2e09012054686520626f6f6c20697320747275652069662074686572652069732061207369676e616c206d6573736167652077616974696e6720746f2062652073656e742e504f7574626f756e6458636d704d657373616765730101080205b50834040004bc20546865206d65737361676573206f7574626f756e6420696e206120676976656e2058434d50206368616e6e656c2e385369676e616c4d6573736167657301010402ad0134040004a020416e79207369676e616c206d657373616765732077616974696e6720746f2062652073656e742e2c5175657565436f6e6669670100b90874020000000500000001000000821a06000008000700c817a804020004000415012054686520636f6e66696775726174696f6e20776869636820636f6e74726f6c73207468652064796e616d696373206f6620746865206f7574626f756e642071756575652e284f766572776569676874000104052cbd08040010050120546865206d657373616765732074686174206578636565646564206d617820696e646976696475616c206d65737361676520776569676874206275646765742e003901205468657365206d657373616765207374617920696e20746869732073746f72616765206d617020756e74696c207468657920617265206d616e75616c6c79206469737061746368656420766961582060736572766963655f6f766572776569676874602e50436f756e746572466f724f766572776569676874010010100000000004ac436f756e74657220666f72207468652072656c6174656420636f756e7465642073746f72616765206d61703c4f766572776569676874436f756e7401002c20000000000000000008690120546865206e756d626572206f66206f766572776569676874206d657373616765732065766572207265636f7264656420696e20604f766572776569676874602e20416c736f20646f75626c657320617320746865206e6578748420617661696c61626c652066726565206f76657277656967687420696e6465782e38517565756553757370656e6465640100350104000441012057686574686572206f72206e6f74207468652058434d502071756575652069732073757370656e6465642066726f6d20657865637574696e6720696e636f6d696e672058434d73206f72206e6f742e017d0301bd050001c108322c506f6c6b61646f7458636d012c506f6c6b61646f7458636d30305175657279436f756e74657201002c200000000000000000048820546865206c617465737420617661696c61626c6520717565727920696e6465782e1c51756572696573000104022cc5080400045420546865206f6e676f696e6720717565726965732e28417373657454726170730101040630101000000000106820546865206578697374696e672061737365742074726170732e007501204b65792069732074686520626c616b6532203235362068617368206f6620286f726967696e2c2076657273696f6e656420604d756c7469417373657473602920706169722e2056616c756520697320746865206e756d626572206f661d012074696d65732074686973207061697220686173206265656e20747261707065642028757375616c6c79206a75737420312069662069742065786973747320617420616c6c292e385361666558636d56657273696f6e00001004000861012044656661756c742076657273696f6e20746f20656e636f64652058434d207768656e206c61746573742076657273696f6e206f662064657374696e6174696f6e20697320756e6b6e6f776e2e20496620604e6f6e65602c3d01207468656e207468652064657374696e6174696f6e732077686f73652058434d2076657273696f6e20697320756e6b6e6f776e2061726520636f6e7369646572656420756e726561636861626c652e40537570706f7274656456657273696f6e0001080502d90810040004f020546865204c61746573742076657273696f6e732074686174207765206b6e6f7720766172696f7573206c6f636174696f6e7320737570706f72742e4056657273696f6e4e6f746966696572730001080502d9082c040004050120416c6c206c6f636174696f6e7320746861742077652068617665207265717565737465642076657273696f6e206e6f74696669636174696f6e732066726f6d2e5056657273696f6e4e6f74696679546172676574730001080502d908dd0804000871012054686520746172676574206c6f636174696f6e73207468617420617265207375627363726962656420746f206f75722076657273696f6e206368616e6765732c2061732077656c6c20617320746865206d6f737420726563656e7494206f66206f75722076657273696f6e7320776520696e666f726d6564207468656d206f662e5456657273696f6e446973636f7665727951756575650100e10804000c65012044657374696e6174696f6e732077686f7365206c61746573742058434d2076657273696f6e20776520776f756c64206c696b6520746f206b6e6f772e204475706c696361746573206e6f7420616c6c6f7765642c20616e6471012074686520607533326020636f756e74657220697320746865206e756d626572206f662074696d6573207468617420612073656e6420746f207468652064657374696e6174696f6e20686173206265656e20617474656d707465642c8c20776869636820697320757365642061732061207072696f726974697a6174696f6e2e4043757272656e744d6967726174696f6e0000ed080400049c205468652063757272656e74206d6967726174696f6e27732073746167652c20696620616e792e5452656d6f74654c6f636b656446756e6769626c657300010c050202f508fd08040004f02046756e6769626c6520617373657473207768696368207765206b6e6f7720617265206c6f636b6564206f6e20612072656d6f746520636861696e2e3c4c6f636b656446756e6769626c657300010402000d09040004e02046756e6769626c6520617373657473207768696368207765206b6e6f7720617265206c6f636b6564206f6e207468697320636861696e2e5458636d457865637574696f6e53757370656e64656401003501040004b420476c6f62616c2073757370656e73696f6e207374617465206f66207468652058434d206578656375746f722e01810301c10500011909332843756d756c757358636d0001250401c90500011d093420446d7051756575650120446d7051756575651434436f6e66696775726174696f6e01002109280700e40b540202000400044c2054686520636f6e66696775726174696f6e2e2450616765496e646578010025094000000000000000000000000000000000044020546865207061676520696e6465782e1450616765730101040210290904000444205468652071756575652070616765732e284f766572776569676874000104022c2d090400046420546865206f766572776569676874206d657373616765732e50436f756e746572466f724f766572776569676874010010100000000004ac436f756e74657220666f72207468652072656c6174656420636f756e7465642073746f72616765206d617001290401cd05000131093524496e666c6174696f6e0124496e666c6174696f6e14645374617274696e6759656172546f74616c49737375616e636501001840000000000000000000000000000000000474207374617274696e67207965617220746f74616c2069737375616e636538426c6f636b496e666c6174696f6e01001840000000000000000000000000000000000401012043757272656e7420696e666c6174696f6e20666f722060496e666c6174696f6e426c6f636b496e74657276616c60206e756d626572206f6620626c6f636b73484e657874496e666c6174696f6e426c6f636b010010100000000004e4204e65787420746172676574202872656c61792920626c6f636b207768656e20696e666c6174696f6e2077696c6c206265206170706c696564584e657874526563616c63756c6174696f6e426c6f636b010010100000000004e4204e65787420746172676574202872656c61792920626c6f636b207768656e20696e666c6174696f6e20697320726563616c63756c61746564285374617274426c6f636b0100101000000000049c2052656c617920626c6f636b207768656e20696e666c6174696f6e206861732073746172746564012d04000458496e666c6174696f6e426c6f636b496e74657276616c101064000000043501204e756d626572206f6620626c6f636b7320746861742070617373206265747765656e2074726561737572792062616c616e636520757064617465732064756520746f20696e666c6174696f6e003c18556e697175650118556e697175652430436861696e56657273696f6e01002c2000000000000000000450205573656420666f72206d6967726174696f6e73404372656174654974656d4261736b657400010402350910040008cc2028436f6c6c656374696f6e2069642028636f6e74726f6c6c65643f32292c2077686f206372656174656420287265616c2929410120544f444f3a204f666620636861696e20776f726b65722073686f756c642072656d6f76652066726f6d2074686973206d6170207768656e20636f6c6c656374696f6e20676574732072656d6f766564444e66745472616e736665724261736b65740001080202390910040004d820436f6c6c656374696f6e2069642028636f6e74726f6c6c65643f32292c20746f6b656e2069642028636f6e74726f6c6c65643f32295846756e6769626c655472616e736665724261736b65740001080205350910040004c420436f6c6c656374696f6e2069642028636f6e74726f6c6c65643f32292c206f776e696e67207573657220287265616c2960526546756e6769626c655472616e736665724261736b657400010c0202053d0910040004d820436f6c6c656374696f6e2069642028636f6e74726f6c6c65643f32292c20746f6b656e2069642028636f6e74726f6c6c65643f32294c546f6b656e50726f70657274794261736b657400010802023909100400045901204c6173742073706f6e736f72696e67206f6620746f6b656e2070726f70657274792073657474696e67202f2f20746f646f3a646f63207265706872617365207468697320616e642074686520666f6c6c6f77696e67404e6674417070726f76654261736b65740001080202390910040004c0204c6173742073706f6e736f72696e67206f66204e465420617070726f76616c20696e206120636f6c6c656374696f6e5446756e6769626c65417070726f76654261736b65740001080205350910040004f0204c6173742073706f6e736f72696e67206f662066756e6769626c6520746f6b656e7320617070726f76616c20696e206120636f6c6c656374696f6e5c526566756e6769626c65417070726f76654261736b657400010c0202053d0910040004c0204c6173742073706f6e736f72696e67206f662052465420617070726f76616c20696e206120636f6c6c656374696f6e0131040034386e657374696e675f62756467657410100500000004fc2041206d6178696d756d206e756d626572206f66206c6576656c73206f6620646570746820696e2074686520746f6b656e206e657374696e6720747265652e686d61785f636f6c6c656374696f6e5f6e616d655f6c656e6774681010400000000494204d6178696d616c206c656e677468206f66206120636f6c6c656374696f6e206e616d652e846d61785f636f6c6c656374696f6e5f6465736372697074696f6e5f6c656e67746810100001000004b0204d6178696d616c206c656e677468206f66206120636f6c6c656374696f6e206465736372697074696f6e2e5c6d61785f746f6b656e5f7072656669785f6c656e6774681010100000000488204d6178696d616c206c656e677468206f66206120746f6b656e207072656669782e5c636f6c6c656374696f6e5f61646d696e735f6c696d6974101005000000047c204d6178696d756d2061646d696e732070657220636f6c6c656374696f6e2e5c6d61785f70726f70657274795f6b65795f6c656e6774681010000100000488204d6178696d616c206c656e677468206f6620612070726f7065727479206b65792e646d61785f70726f70657274795f76616c75655f6c656e6774681010008000000490204d6178696d616c206c656e677468206f6620612070726f70657274792076616c75652e5c6d61785f70726f706572746965735f7065725f6974656d10104000000004982041206d6178696d756d206e756d626572206f6620746f6b656e2070726f706572746965732e786d61785f636f6c6c656374696f6e5f70726f706572746965735f73697a65101000a0000004b0204d6178696d756d2073697a6520666f7220616c6c20636f6c6c656374696f6e2070726f706572746965732e646d61785f746f6b656e5f70726f706572746965735f73697a651010008000000498204d6178696d756d2073697a65206f6620616c6c20746f6b656e2070726f706572746965732e746e66745f64656661756c745f636f6c6c656374696f6e5f6c696d6974735d04840140420f000100080000010001ffffffff0105000000010500000001000101010104782044656661756c74204e465420636f6c6c656374696f6e206c696d69742e747266745f64656661756c745f636f6c6c656374696f6e5f6c696d6974735d04840140420f000100080000010001ffffffff0105000000010500000001000101010104782044656661756c742052465420636f6c6c656374696f6e206c696d69742e7066745f64656661756c745f636f6c6c656374696f6e5f6c696d6974735d04840140420f000100080000010001ffffffff0105000000010500000001000101010104742044656661756c7420465420636f6c6c656374696f6e206c696d69742e0141093d34436f6e66696775726174696f6e0134436f6e66696775726174696f6e1878576569676874546f466565436f656666696369656e744f7665727269646501002c20449004f701540801004c4d696e47617350726963654f7665727269646501002c20788dfa33b4010000008c41707050726f6d6f6d6f74696f6e436f6e66696775726174696f6e4f7665727269646501001505100000000000a4436f6c6c61746f7253656c656374696f6e44657369726564436f6c6c61746f72734f76657272696465010010100a0000000090436f6c6c61746f7253656c656374696f6e4c6963656e7365426f6e644f766572726964650100184000000040eaed7446d09c2c9f0c0000000098436f6c6c61746f7253656c656374696f6e4b69636b5468726573686f6c644f76657272696465010010102c01000000010d0501d105207444656661756c74576569676874546f466565436f656666696369656e742c20449004f701540801004844656661756c744d696e47617350726963652c20788dfa33b401000000584d617858636d416c6c6f7765644c6f636174696f6e73101010000000005441707050726f6d6f74696f6e4461696c79526174651d051020a10700003844617952656c6179426c6f636b73101040380000009044656661756c74436f6c6c61746f7253656c656374696f6e4d6178436f6c6c61746f727310100a000000008c44656661756c74436f6c6c61746f7253656c656374696f6e4c6963656e7365426f6e64184000000040eaed7446d09c2c9f0c000000009444656661756c74436f6c6c61746f7253656c656374696f6e4b69636b5468726573686f6c6410102c010000000145093f204368617267696e6700000000004018436f6d6d6f6e0118436f6d6d6f6e245843726561746564436f6c6c656374696f6e436f756e740100890410000000000469012053746f72616765206f662074686520636f756e74206f66206372656174656420636f6c6c656374696f6e732e20457373656e7469616c6c7920636f6e7461696e7320746865206c61737420636f6c6c656374696f6e2049442e6044657374726f796564436f6c6c656374696f6e436f756e7401008904100000000004b42053746f72616765206f662074686520636f756e74206f662064656c6574656420636f6c6c656374696f6e732e38436f6c6c656374696f6e427949640001040289044909040004702053746f72616765206f6620636f6c6c656374696f6e20696e666f2e50436f6c6c656374696f6e50726f7065727469657301010402890451092400000000000000000004882053746f72616765206f6620636f6c6c656374696f6e2070726f706572746965732e74436f6c6c656374696f6e50726f70657274795065726d697373696f6e730101040289046909040004dc2053746f72616765206f6620746f6b656e2070726f7065727479207065726d697373696f6e73206f66206120636f6c6c656374696f6e2e2c41646d696e416d6f756e7401010402890410100000000004b02053746f72616765206f662074686520616d6f756e74206f6620636f6c6c656374696f6e2061646d696e732e1c497341646d696e01010802027d0935010400046c204c697374206f6620636f6c6c656374696f6e2061646d696e732e24416c6c6f776c69737401010802027d0935010400047820416c6c6f776c697374656420636f6c6c656374696f6e2075736572732e4444756d6d7953746f7261676556616c7565000081090400040501204e6f74207573656420627920636f64652c20657869737473206f6e6c7920746f2070726f7669646520736f6d6520747970657320746f206d657461646174612e0001d5050c5c436f6c6c656374696f6e4372656174696f6e507269636518400000c84e676dc11b000000000000000004882053657420707269636520746f20637265617465206120636f6c6c656374696f6e2e3c436f6e7472616374416464726573736503506c4e9fe1ae37a41e93cee429e8e1881abdcbb54f041901204164647265737320756e6465722077686963682074686520436f6c6c656374696f6e48656c70657220636f6e747261637420776f756c6420626520617661696c61626c652e5c636f6c6c656374696f6e5f61646d696e735f6c696d6974101005000000047c204d6178696d756d2061646d696e732070657220636f6c6c656374696f6e2e01c509422046756e6769626c65012046756e6769626c650c2c546f74616c537570706c7901010405890418400000000000000000000000000000000004d420546f74616c20616d6f756e74206f662066756e6769626c6520746f6b656e7320696e73696465206120636f6c6c656374696f6e2e1c42616c616e636501010805027d0918400000000000000000000000000000000004e820416d6f756e74206f6620746f6b656e73206f776e656420627920616e206163636f756e7420696e73696465206120636f6c6c656374696f6e2e24416c6c6f77616e636501010c050002c9091840000000000000000000000000000000000405012053746f7261676520666f72206173736574732064656c65676174656420746f2061206c696d6974656420657874656e7420746f206f746865722075736572732e00000001cd094328526566756e6769626c650128526566756e6769626c652430546f6b656e734d696e74656401010405890410100000000004bc20546f74616c20616d6f756e74206f66206d696e74656420746f6b656e7320696e206120636f6c6c656374696f6e2e2c546f6b656e734275726e7401010405890410100000000004a020416d6f756e74206f6620746f6b656e73206275726e7420696e206120636f6c6c656374696f6e2e3c546f6b656e50726f7065727469657300010805053909d109040004cc20416d6f756e74206f6620706965636573206120726566756e6769626c6520746f6b656e2069732073706c697420696e746f2e2c546f74616c537570706c7901010805053909184000000000000000000000000000000000048420546f74616c20616d6f756e74206f662070696563657320666f7220746f6b656e144f776e656401010c050205d5093501040004ac205573656420746f20656e756d657261746520746f6b656e73206f776e6564206279206163636f756e742e384163636f756e7442616c616e636501010805027d0910100000000004450120416d6f756e74206f6620746f6b656e7320286e6f742070696563657329207061727469616c6c79206f776e656420627920616e206163636f756e742077697468696e206120636f6c6c656374696f6e2e1c42616c616e636501010c050502d90918400000000000000000000000000000000004a420416d6f756e74206f6620746f6b656e20706965636573206f776e6564206279206163636f756e742e24416c6c6f77616e636501011005050002dd0918400000000000000000000000000000000004e50120416c6c6f77616e636520736574206279206120746f6b656e206f776e657220666f7220616e6f74686572207573657220746f20706572666f726d206f6e65206f66206365727461696e207472616e73616374696f6e73206f6e2061206e756d626572206f6620706965636573206f66206120746f6b656e2e4c436f6c6c656374696f6e416c6c6f77616e636501010c050202c90935010400048d01205370656e6465722073657420627920612077616c6c6574206f776e6572207468617420636f756c6420706572666f726d206365727461696e207472616e73616374696f6e73206f6e20616c6c20746f6b656e7320696e207468652077616c6c65742e00000001e109442c4e6f6e66756e6769626c65012c4e6f6e66756e6769626c652830546f6b656e734d696e74656401010405890410100000000004bc20546f74616c20616d6f756e74206f66206d696e74656420746f6b656e7320696e206120636f6c6c656374696f6e2e2c546f6b656e734275726e7401010405890410100000000004a020416d6f756e74206f66206275726e7420746f6b656e7320696e206120636f6c6c656374696f6e2e24546f6b656e4461746100010805053909e509040004c020546f6b656e20646174612c207573656420746f207061727469616c6c79206465736372696265206120746f6b656e2e3c546f6b656e50726f7065727469657300010805053909d109040004f0204d6170206f66206b65792d76616c75652070616972732c2064657363726962696e6720746865206d65746164617461206f66206120746f6b656e2e48546f6b656e41757850726f7065727469657300011005050505e909f109040024d020437573746f6d2064617461206f66206120746f6b656e20746861742069732073657269616c697a656420746f2062797465732cb0207072696d6172696c7920726573657276656420666f72206f6e2d636861696e206f7065726174696f6e732cac206e6f726d616c6c79206f627363757265642066726f6d207468652065787465726e616c2075736572732e00c420417578696c696172792070726f706572746965732061726520736c696768746c7920646966666572656e742066726f6dd420757375616c205b60546f6b656e50726f70657274696573605d2064756520746f20616e20756e6c696d69746564206e756d626572d820616e642073657061726174656c792073746f72656420616e64207772697474656e2d746f206b65792d76616c75652070616972732e00482043757272656e746c7920756e757365642e144f776e656401010c050205d5093501040004ac205573656420746f20656e756d657261746520746f6b656e73206f776e6564206279206163636f756e742e34546f6b656e4368696c6472656e01010c050505f509350104000490205573656420746f20656e756d657261746520746f6b656e2773206368696c6472656e2e384163636f756e7442616c616e636501010805027d0910100000000004d820416d6f756e74206f6620746f6b656e73206f776e656420627920616e206163636f756e7420696e206120636f6c6c656374696f6e2e24416c6c6f77616e6365000108050539094d040400048d0120416c6c6f77616e636520736574206279206120746f6b656e206f776e657220666f7220616e6f74686572207573657220746f20706572666f726d206f6e65206f66206365727461696e207472616e73616374696f6e73206f6e206120746f6b656e2e4c436f6c6c656374696f6e416c6c6f77616e636501010c050202c90935010400049101204f70657261746f722073657420627920612077616c6c6574206f776e6572207468617420636f756c6420706572666f726d206365727461696e207472616e73616374696f6e73206f6e20616c6c20746f6b656e7320696e207468652077616c6c65742e00000001f90945245374727563747572650001290501d9050001fd09463041707050726f6d6f74696f6e013041707050726f6d6f74696f6e182c546f74616c5374616b6564010018400000000000000000000000000000000004802053746f7265732074686520746f74616c207374616b656420616d6f756e742e1441646d696e00000004000485012053746f72657320746865206061646d696e60206163636f756e742e20536f6d652065787472696e736963732063616e206f6e6c7920626520657865637574656420696620746865792077657265207369676e6564206279206061646d696e602e185374616b65640101080205010a050a5000000000000000000000000000000000000000001809012053746f7265732074686520616d6f756e74206f6620746f6b656e73207374616b6564206279206163636f756e7420696e2074686520626c6f636b6e756d6265722e0074202a202a2a4b6579312a2a202d205374616b6572206163636f756e742ee4202a202a2a4b6579322a2a202d2052656c617920626c6f636b206e756d626572207768656e20746865207374616b6520776173206d6164652ed4202a202a2a2842616c616e63652c20426c6f636b4e756d626572292a2a202d2042616c616e6365206f6620746865207374616b652e490120546865206e756d626572206f66207468652072656c617920626c6f636b20696e207768696368207765206d75737420706572666f726d2074686520696e74657265737420726563616c63756c6174696f6e405374616b65735065724163636f756e74010104020008040010c42053746f726573206e756d626572206f66207374616b65207265636f72647320666f7220616e20604163636f756e74602e0070202a202a2a4b65792a2a202d205374616b6572206163636f756e742e80202a202a2a56616c75652a2a202d20416d6f756e74206f66207374616b65732e3850656e64696e67556e7374616b650101040510090a040010a82050656e64696e6720756e7374616b65207265636f72647320666f7220616e20604163636f756e74602e0070202a202a2a4b65792a2a202d205374616b6572206163636f756e742e80202a202a2a56616c75652a2a202d20416d6f756e74206f66207374616b65732e6050726576696f757343616c63756c617465645265636f72640000010a0400082d012053746f7265732061206b657920666f72207265636f726420666f722077686963682074686520726576656e756520726563616c63756c6174696f6e2077617320706572666f726d65642eb90120496620604e6f6e65602c207468656e20726563616c63756c6174696f6e20686173206e6f7420796574206265656e20706572666f726d6564206f722063616c63756c6174696f6e732068617665206265656e20636f6d706c6574656420666f7220616c6c207374616b6572732e012d0501dd05182050616c6c657449644907206170707374616b65041901205468652061707027732070616c6c65742069642c207573656420666f72206465726976696e672069747320736f7665726569676e206163636f756e7420616464726573732e40467265657a654964656e7469666965720501406170707374616b656170707374616b65049420467265657a65206964656e7469666965722075736564206279207468652070616c6c657454526563616c63756c6174696f6e496e74657276616c101040380000044420496e2072656c617920626c6f636b732e3c50656e64696e67496e74657276616c1010e0c40000045420496e2070617261636861696e20626c6f636b732e38496e74657276616c496e636f6d651d051020a107000429012052617465206f662072657475726e20666f7220696e74657276616c20696e20626c6f636b7320646566696e656420696e2060526563616c63756c6174696f6e496e74657276616c602e1c4e6f6d696e616c1840000064a7b3b6e00d0000000000000000047420446563696d616c7320666f7220746865206043757272656e6379602e01110a4934466f726569676e4173736574730134466f726569676e41737365747314484e657874466f726569676e4173736574496401001010000000000c8c204e65787420617661696c61626c6520466f726569676e20417373657449642049442e008c204e657874466f726569676e417373657449643a20466f726569676e4173736574496454466f726569676e41737365744c6f636174696f6e730001040510d404000c84205468652073746f726167657320666f72204d756c74694c6f636174696f6e732e000d0120466f726569676e41737365744c6f636174696f6e733a206d617020466f726569676e41737365744964203d3e204f7074696f6e3c4d756c74694c6f636174696f6e3e544c6f636174696f6e546f43757272656e637949647300010405d41004000c78205468652073746f726167657320666f722043757272656e63794964732e000d01204c6f636174696f6e546f43757272656e63794964733a206d6170204d756c74694c6f636174696f6e203d3e204f7074696f6e3c466f726569676e417373657449643e3841737365744d6574616461746173000104050d01390504000c84205468652073746f726167657320666f722041737365744d65746164617461732e00d82041737365744d65746164617461733a206d6170204173736574496473203d3e204f7074696f6e3c41737365744d657461646174613e30417373657442696e64696e6700010405108904040008dc205468652073746f726167657320666f722061737365747320746f2066756e6769626c6520636f6c6c656374696f6e2062696e64696e670001350501e1050001150a500c45564d010c45564d10304163636f756e74436f64657301010402650334040000504163636f756e74436f6465734d65746164617461000104026503190a0400003c4163636f756e7453746f726167657301010802021d0a30800000000000000000000000000000000000000000000000000000000000000000002c43757272656e744c6f677301009d05040008a0205772697474656e206f6e206c6f672c207265736574206166746572207472616e73616374696f6e942053686f756c6420626520656d707479206265747765656e207472616e73616374696f6e7301450501e5050001210a6420457468657265756d0120457468657265756d181c50656e64696e670100250a040004d02043757272656e74206275696c64696e6720626c6f636b2773207472616e73616374696f6e7320616e642072656365697074732e3043757272656e74426c6f636b0000450a04000470205468652063757272656e7420457468657265756d20626c6f636b2e3c43757272656e7452656365697074730000590a0400047c205468652063757272656e7420457468657265756d2072656365697074732e6843757272656e745472616e73616374696f6e537461747573657300005d0a04000488205468652063757272656e74207472616e73616374696f6e2073746174757365732e24426c6f636b48617368010104054905308000000000000000000000000000000000000000000000000000000000000000000034496e6a65637465644e6f6e63650100490580000000000000000000000000000000000000000000000000000000000000000004190120496e6a6563746564207472616e73616374696f6e732073686f756c64206861766520756e69717565206e6f6e63652c20686572652077652073746f72652063757272656e7401610501e9050001610a654445766d436f6465725375627374726174650000000001650a964845766d436f6e747261637448656c70657273014845766d436f6e747261637448656c7065727324144f776e6572010104036503650350000000000000000000000000000000000000000010682053746f7265206f776e657220666f7220636f6e74726163742e0078202a202a2a4b65792a2a202d20636f6e747261637420616464726573732e88202a202a2a56616c75652a2a202d206f776e657220666f7220636f6e74726163742e3853656c6653706f6e736f72696e6701010403650335010400049c20446570726563617465643a20746869732073746f7261676520697320646570726563617465642853706f6e736f72696e67010104056503690a040010982053746f726520666f7220636f6e74726163742073706f6e736f72736869702073746174652e0078202a202a2a4b65792a2a202d20636f6e747261637420616464726573732e84202a202a2a56616c75652a2a202d2073706f6e736f72736869702073746174652e3853706f6e736f72696e674d6f64650001040365036d0a04001c6c2053746f726520666f722073706f6e736f72696e67206d6f64652e00282023232320557361676591012050726566657220746f2064656c65746520636f6c6c656374696f6e2066726f6d2073746f72616765206966206d6f64652063686167656420746f205b6044697361626c6564605d2853706f6e736f72696e674d6f6465543a3a44697361626c6564292e0078202a202a2a4b65792a2a202d20636f6e747261637420616464726573732ed0202a202a2a56616c75652a2a202d205b6073706f6e736f72696e67206d6f6465605d2853706f6e736f72696e674d6f646554292e4c53706f6e736f72696e67526174654c696d69740101040365031010201c000010b42053746f7261676520666f722073706f6e736f72696e672072617465206c696d697420696e20626c6f636b732e0078202a202a2a4b65792a2a202d20636f6e747261637420616464726573732ea8202a202a2a56616c75652a2a202d20616d6f756e74206f662073706f6e736f72656420626c6f636b732e4853706f6e736f72696e674665654c696d6974010104036503710a040014882053746f7261676520666f72206c6173742073706f6e736f72656420626c6f636b2e007c202a202a2a4b6579312a2a202d20636f6e747261637420616464726573732e94202a202a2a4b6579322a2a202d2073706f6e736f726564207573657220616464726573732eac202a202a2a56616c75652a2a202d206c6173742073706f6e736f72656420626c6f636b206e756d6265722e3453706f6e736f724261736b65740001080303810a1004000040416c6c6f776c697374456e61626c6564010104036503350104001c69012053746f7265676520666f7220636f6e7472616374732077697468205b60416c6c6f776c6973746564605d2853706f6e736f72696e674d6f6465543a3a416c6c6f776c6973746564292073706f6e736f72696e67206d6f64652e002820232323205573616765bd012050726566657220746f2064656c65746520636f6c6c656374696f6e2066726f6d2073746f72616765206966206d6f64652063686167656420746f206e6f6e2060416c6c6f776c6973746564602c207468616e20736574202a2a56616c75652a2a20746f202a2a66616c73652a2a2e0078202a202a2a4b65792a2a202d20636f6e747261637420616464726573732e4501202a202a2a56616c75652a2a202d20697320636f6e747261637420696e205b60416c6c6f776c6973746564605d2853706f6e736f72696e674d6f6465543a3a416c6c6f776c697374656429206d6f64652e24416c6c6f776c6973740101080303810a3501040020c02053746f7261676520666f72207573657273207468617420616c6c6f77656420666f722073706f6e736f72736869702e00282023232320557361676539012050726566657220746f2064656c657465207265636f72642066726f6d2073746f726167652069662075736572206e6f206d6f726520616c6c6f77656420666f722073706f6e736f72736869702e007c202a202a2a4b6579312a2a202d20636f6e747261637420616464726573732ec0202a202a2a4b6579322a2a202d2075736572207468617420616c6c6f77656420666f722073706f6e736f72736869702ea4202a202a2a56616c75652a2a202d20616c6c6f77616e636520666f722073706f6e736f72736869702e018905010d06043c436f6e747261637441646472657373650350842899ecf380553e8a4de75bf534cdf6fbf6404904d820416464726573732c20756e646572207768696368206d6167696320636f6e74726163742077696c6c20626520617661696c61626c6501850a975445766d5472616e73616374696f6e5061796d656e740000000000983045766d4d6967726174696f6e013045766d4d6967726174696f6e04404d6967726174696f6e50656e64696e6701010405650335010400000191050111060001890a992c4d61696e74656e616e6365012c4d61696e74656e616e6365041c456e61626c65640100350104000001a50501150600018d0a9a3c42616c616e6365734164617074657200000000009b1c5574696c6974790001a905011906044c626174636865645f63616c6c735f6c696d69741010aa2a000004a820546865206c696d6974206f6e20746865206e756d626572206f6620626174636865642063616c6c732e01910a9c24546573745574696c730124546573745574696c73081c456e61626c656401003501040000245465737456616c756501001010000000000001b105011d060001950aff990a042840436865636b5370656356657273696f6eb90a1038436865636b547856657273696f6ebd0a1030436865636b47656e65736973c10a3038436865636b4d6f7274616c697479c50a3028436865636b4e6f6e6365cd0aac2c436865636b576569676874d10aac40436865636b4d61696e74656e616e6365d50aac5044697361626c654964656e7469747943616c6c73d90aac604368617267655472616e73616374696f6e5061796d656e74dd0aac6046616b655472616e73616374696f6e46696e616c697a6572e50aace10a","id":"1"} \ No newline at end of file --- a/js-packages/scripts/src/proposefasttrack.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {ApiPromise, WsProvider} from '@polkadot/api'; -import {blake2AsHex} from '@polkadot/util-crypto'; - -async function main() { - const networkUrl = process.argv[2]; - - const wsProvider = new WsProvider(networkUrl); - const api = await ApiPromise.create({provider: wsProvider}); - - const externalDemocracyProposal = (await api.query.democracy.nextExternal() as any).unwrap()[0]; - - let proposalHash; - if(externalDemocracyProposal.isInline) { - proposalHash = blake2AsHex(externalDemocracyProposal.asInline, 256); - } else if(externalDemocracyProposal.isLegacy) { - proposalHash = externalDemocracyProposal.asLegacy.toJSON().hash; - } else { - proposalHash = externalDemocracyProposal.asLookup.toJSON().hash; - } - - const voringPeriod = 7200; - const delay = 10; - - const democracyFastTrack = api.tx.democracy.fastTrack(proposalHash, voringPeriod, delay); - - const techCommThreshold = ((await api.query.technicalCommittee.members()).toJSON() as any[]).length; - const techCommProposal = api.tx.technicalCommittee.propose( - techCommThreshold, - democracyFastTrack.method.toHex(), - democracyFastTrack.encodedLength, - ); - - const encodedCall = techCommProposal.method.toHex(); - - console.log('-----------------'); - console.log('Fast Track Proposal: ', `https://polkadot.js.org/apps/?rpc=${networkUrl}#/extrinsics/decode/${encodedCall}`); - console.log('-----------------'); - - await api.disconnect(); -} - -await main(); --- a/js-packages/scripts/src/proposeupgrade.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {ApiPromise, WsProvider} from '@polkadot/api'; -import {blake2AsHex} from '@polkadot/util-crypto'; -import {readFileSync} from 'fs'; - -async function main() { - const networkUrl = process.argv[2]; - const wasmFile = process.argv[3]; - - const wsProvider = new WsProvider(networkUrl); - const api = await ApiPromise.create({provider: wsProvider}); - - const wasmFileBytes = readFileSync(wasmFile); - const wasmFileHash = blake2AsHex(wasmFileBytes, 256); - - const authorizeUpgrade = api.tx.parachainSystem.authorizeUpgrade(wasmFileHash, true); - const enableMaintenance = api.tx.maintenance.enable(); - - const councilMembers = (await api.query.council.members()).toJSON() as any[]; - const councilProposalThreshold = Math.floor(councilMembers.length / 2) + 1; - - const democracyProposalContent = api.tx.utility.batchAll([ - authorizeUpgrade.method.toHex(), - enableMaintenance.method.toHex(), - ]).method.toHex(); - - const democracyProposalHash = blake2AsHex(democracyProposalContent, 256); - const democracyProposalPreimage = api.tx.preimage.notePreimage(democracyProposalContent).method.toHex(); - - const democracyProposal = api.tx.democracy.externalProposeDefault({ - Legacy: democracyProposalHash, - }); - - const councilProposal = api.tx.council.propose( - councilProposalThreshold, - democracyProposal, - democracyProposal.method.encodedLength, - ).method.toHex(); - - const proposeUpgradeBatch = api.tx.utility.batchAll([ - democracyProposalPreimage, - councilProposal, - ]); - - const encodedCall = proposeUpgradeBatch.method.toHex(); - - console.log('-----------------'); - console.log('Upgrade Proposal: ', `https://polkadot.js.org/apps/?rpc=${networkUrl}#/extrinsics/decode/${encodedCall}`); - console.log('-----------------'); - - await api.disconnect(); -} - -await main(); --- a/js-packages/scripts/src/transfer.nload.ts +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -/* eslint-disable @typescript-eslint/no-floating-promises */ -import os from 'os'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds} from '@unique/tests/src/util/index.js'; -import {UniqueHelper} from '@unique/playgrounds/src/unique.js'; -import * as notReallyCluster from 'cluster'; // https://github.com/nodejs/node/issues/42271#issuecomment-1063415346 -const cluster = notReallyCluster as unknown as notReallyCluster.Cluster; - -async function findUnusedAddress(helper: UniqueHelper, privateKey: (account: string) => Promise, seedAddition = ''): Promise { - let bal = 0n; - let unused; - do { - const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition; - unused = await privateKey(`//${randomSeed}`); - bal = await helper.balance.getSubstrate(unused.address); - } while(bal !== 0n); - return unused; -} - -function findUnusedAddresses(helper: UniqueHelper, privateKey: (account: string) => Promise, amount: number): Promise { - return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(helper, privateKey, '_' + Date.now()))); -} - -// Innacurate transfer fee -const FEE = 10n ** 8n; - -let counters: { [key: string]: number } = {}; -function increaseCounter(name: string, amount: number) { - if(!counters[name]) { - counters[name] = 0; - } - counters[name] += amount; -} -function flushCounterToMaster() { - if(Object.keys(counters).length === 0) { - return; - } - process.send!(counters); - counters = {}; -} - -async function distributeBalance(source: IKeyringPair, helper: UniqueHelper, privateKey: (account: string) => Promise, totalAmount: bigint, stages: number) { - const accounts = [source]; - // we don't need source in output array - const failedAccounts = [0]; - - const finalUserAmount = 2 ** stages - 1; - accounts.push(...await findUnusedAddresses(helper, privateKey, finalUserAmount)); - // findUnusedAddresses produces at least 1 request per user - increaseCounter('requests', finalUserAmount); - - for(let stage = 0; stage < stages; stage++) { - const usersWithBalance = 2 ** stage; - const amount = totalAmount / (2n ** BigInt(stage)) - FEE * BigInt(stage); - // console.log(`Stage ${stage}/${stages}, ${usersWithBalance} => ${usersWithBalance * 2} = ${amount}`); - const txs: Promise[] = []; - for(let i = 0; i < usersWithBalance; i++) { - const newUser = accounts[i + usersWithBalance]; - // console.log(`${accounts[i].address} => ${newUser.address} = ${amountToSplit}`); - const tx = helper.balance.transferToSubstrate(accounts[i], newUser.address, amount); - txs.push(tx.catch(() => { - failedAccounts.push(i + usersWithBalance); - increaseCounter('txFailed', 1); - })); - increaseCounter('tx', 1); - } - await Promise.all(txs); - } - - for(const account of failedAccounts.reverse()) { - accounts.splice(account, 1); - } - return accounts; -} - -if(cluster.isMaster) { - let testDone = false; - usingPlaygrounds(async (helper) => { - const prevCounters: { [key: string]: number } = {}; - while(!testDone) { - for(const name in counters) { - if(!(name in prevCounters)) { - prevCounters[name] = 0; - } - if(counters[name] === prevCounters[name]) { - continue; - } - console.log(`${name.padEnd(15)} = ${counters[name] - prevCounters[name]}`); - prevCounters[name] = counters[name]; - } - await helper.wait.newBlocks(1); - } - }); - const waiting: Promise[] = []; - console.log(`Starting ${os.cpus().length} workers`); - usingPlaygrounds(async (helper, privateKey) => { - const alice = await privateKey('//Alice'); - for(const id in os.cpus()) { - const WORKER_NAME = `//LoadWorker${id}_${Date.now()}`; - const workerAccount = await privateKey(WORKER_NAME); - await helper.balance.transferToSubstrate(alice, workerAccount.address, 400n * 10n ** 23n); - - const worker = cluster.fork({ - WORKER_NAME, - STAGES: id + 2, - }); - worker.on('message', msg => { - for(const key in msg) { - increaseCounter(key, msg[key]); - } - }); - waiting.push(new Promise(res => worker.on('exit', res))); - } - await Promise.all(waiting); - testDone = true; - }); -} else { - increaseCounter('startedWorkers', 1); - usingPlaygrounds(async (helper, privateKey) => { - await distributeBalance(await privateKey(process.env.WORKER_NAME as string), helper, privateKey, 400n * 10n ** 22n, 10); - }); - const interval = setInterval(() => { - flushCounterToMaster(); - }, 100); - interval.unref(); -} --- /dev/null +++ b/js-packages/scripts/transfer.nload.ts @@ -0,0 +1,142 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +/* eslint-disable @typescript-eslint/no-floating-promises */ +import os from 'os'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds} from '@unique/tests/util/index.js'; +import {UniqueHelper} from '@unique/playgrounds/unique.js'; +import * as notReallyCluster from 'cluster'; // https://github.com/nodejs/node/issues/42271#issuecomment-1063415346 +const cluster = notReallyCluster as unknown as notReallyCluster.Cluster; + +async function findUnusedAddress(helper: UniqueHelper, privateKey: (account: string) => Promise, seedAddition = ''): Promise { + let bal = 0n; + let unused; + do { + const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition; + unused = await privateKey(`//${randomSeed}`); + bal = await helper.balance.getSubstrate(unused.address); + } while(bal !== 0n); + return unused; +} + +function findUnusedAddresses(helper: UniqueHelper, privateKey: (account: string) => Promise, amount: number): Promise { + return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(helper, privateKey, '_' + Date.now()))); +} + +// Innacurate transfer fee +const FEE = 10n ** 8n; + +let counters: { [key: string]: number } = {}; +function increaseCounter(name: string, amount: number) { + if(!counters[name]) { + counters[name] = 0; + } + counters[name] += amount; +} +function flushCounterToMaster() { + if(Object.keys(counters).length === 0) { + return; + } + process.send!(counters); + counters = {}; +} + +async function distributeBalance(source: IKeyringPair, helper: UniqueHelper, privateKey: (account: string) => Promise, totalAmount: bigint, stages: number) { + const accounts = [source]; + // we don't need source in output array + const failedAccounts = [0]; + + const finalUserAmount = 2 ** stages - 1; + accounts.push(...await findUnusedAddresses(helper, privateKey, finalUserAmount)); + // findUnusedAddresses produces at least 1 request per user + increaseCounter('requests', finalUserAmount); + + for(let stage = 0; stage < stages; stage++) { + const usersWithBalance = 2 ** stage; + const amount = totalAmount / (2n ** BigInt(stage)) - FEE * BigInt(stage); + // console.log(`Stage ${stage}/${stages}, ${usersWithBalance} => ${usersWithBalance * 2} = ${amount}`); + const txs: Promise[] = []; + for(let i = 0; i < usersWithBalance; i++) { + const newUser = accounts[i + usersWithBalance]; + // console.log(`${accounts[i].address} => ${newUser.address} = ${amountToSplit}`); + const tx = helper.balance.transferToSubstrate(accounts[i], newUser.address, amount); + txs.push(tx.catch(() => { + failedAccounts.push(i + usersWithBalance); + increaseCounter('txFailed', 1); + })); + increaseCounter('tx', 1); + } + await Promise.all(txs); + } + + for(const account of failedAccounts.reverse()) { + accounts.splice(account, 1); + } + return accounts; +} + +if(cluster.isMaster) { + let testDone = false; + usingPlaygrounds(async (helper) => { + const prevCounters: { [key: string]: number } = {}; + while(!testDone) { + for(const name in counters) { + if(!(name in prevCounters)) { + prevCounters[name] = 0; + } + if(counters[name] === prevCounters[name]) { + continue; + } + console.log(`${name.padEnd(15)} = ${counters[name] - prevCounters[name]}`); + prevCounters[name] = counters[name]; + } + await helper.wait.newBlocks(1); + } + }); + const waiting: Promise[] = []; + console.log(`Starting ${os.cpus().length} workers`); + usingPlaygrounds(async (helper, privateKey) => { + const alice = await privateKey('//Alice'); + for(const id in os.cpus()) { + const WORKER_NAME = `//LoadWorker${id}_${Date.now()}`; + const workerAccount = await privateKey(WORKER_NAME); + await helper.balance.transferToSubstrate(alice, workerAccount.address, 400n * 10n ** 23n); + + const worker = cluster.fork({ + WORKER_NAME, + STAGES: id + 2, + }); + worker.on('message', msg => { + for(const key in msg) { + increaseCounter(key, msg[key]); + } + }); + waiting.push(new Promise(res => worker.on('exit', res))); + } + await Promise.all(waiting); + testDone = true; + }); +} else { + increaseCounter('startedWorkers', 1); + usingPlaygrounds(async (helper, privateKey) => { + await distributeBalance(await privateKey(process.env.WORKER_NAME as string), helper, privateKey, 400n * 10n ** 22n, 10); + }); + const interval = setInterval(() => { + flushCounterToMaster(); + }, 100); + interval.unref(); +} --- a/js-packages/scripts/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig.packages.json", - "compilerOptions": { - "rootDir": "./src", - "outDir": "dist", - "resolveJsonModule": true - }, - "references": [ - { "path": "../types/tsconfig.json" }, - { "path": "../playgrounds/tsconfig.json" }, - { "path": "../tests/tsconfig.json" } - ] -} \ No newline at end of file --- /dev/null +++ b/js-packages/tests/addCollectionAdmin.test.ts @@ -0,0 +1,120 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from './util/index.js'; +import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js'; + +describe('Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => { + let donor: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itSub('Add collection admin.', async ({helper}) => { + const [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + + const collection = await helper.collection.getData(collectionId); + expect(collection!.normalizedOwner!).to.be.equal(helper.address.normalizeSubstrate(alice.address)); + + await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address}); + + const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); + }); +}); + +describe('Negative Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => { + let donor: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itSub("Not owner can't add collection admin.", async ({helper}) => { + const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + + const collection = await helper.collection.getData(collectionId); + expect(collection?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address)); + + await expect(helper.collection.addAdmin(bob, collectionId, {Substrate: charlie.address})).to.be.rejectedWith(/common\.NoPermission/); + await expect(helper.collection.addAdmin(bob, collectionId, {Substrate: bob.address})).to.be.rejectedWith(/common\.NoPermission/); + + const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId); + expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: charlie.address}); + expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: bob.address}); + }); + + itSub("Admin can't add collection admin.", async ({helper}) => { + const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + + await collection.addAdmin(alice, {Substrate: bob.address}); + + const adminListAfterAddAdmin = await collection.getAdmins(); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); + + await expect(collection.addAdmin(bob, {Substrate: charlie.address})).to.be.rejectedWith(/common\.NoPermission/); + + const adminListAfterAddNewAdmin = await collection.getAdmins(); + expect(adminListAfterAddNewAdmin).to.be.deep.contains({Substrate: bob.address}); + expect(adminListAfterAddNewAdmin).to.be.not.deep.contains({Substrate: charlie.address}); + }); + + itSub("Can't add collection admin of not existing collection.", async ({helper}) => { + const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + const collectionId = NON_EXISTENT_COLLECTION_ID; + + await expect(helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address})).to.be.rejectedWith(/common\.CollectionNotFound/); + + // Verifying that nothing bad happened (network is live, new collections can be created, etc.) + await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + }); + + itSub("Can't add an admin to a destroyed collection.", async ({helper}) => { + const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + + await collection.burn(alice); + await expect(collection.addAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.CollectionNotFound/); + + // Verifying that nothing bad happened (network is live, new collections can be created, etc.) + await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + }); + + itSub('Add an admin to a collection that has reached the maximum number of admins limit', async ({helper}) => { + const [alice, ...accounts] = await helper.arrange.createAccounts([10n, 0n, 0n, 0n, 0n, 0n, 0n, 0n], donor); + const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + + const chainAdminLimit = (helper.getApi().consts.common.collectionAdminsLimit as any).toNumber(); + expect(chainAdminLimit).to.be.equal(5); + + for(let i = 0; i < chainAdminLimit; i++) { + await collection.addAdmin(alice, {Substrate: accounts[i].address}); + const adminListAfterAddAdmin = await collection.getAdmins(); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: accounts[i].address}); + } + + await expect(collection.addAdmin(alice, {Substrate: accounts[chainAdminLimit].address})).to.be.rejectedWith(/common\.CollectionAdminCountExceeded/); + }); +}); --- /dev/null +++ b/js-packages/tests/adminTransferAndBurn.test.ts @@ -0,0 +1,61 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, itSub} from './util/index.js'; + +describe('Integration Test: ownerCanTransfer allows admins to use only transferFrom/burnFrom:', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + }); + }); + + itSub('admin transfers other user\'s token', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}); + await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); + const limits = await helper.collection.getEffectiveLimits(collectionId); + expect(limits.ownerCanTransfer).to.be.true; + + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); + await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})).to.be.rejected; + + await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address}); + const newTokenOwner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(newTokenOwner).to.be.deep.equal({Substrate: charlie.address}); + }); + + itSub('admin burns other user\'s token', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}); + + await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); + const limits = await helper.collection.getEffectiveLimits(collectionId); + expect(limits.ownerCanTransfer).to.be.true; + + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); + + await expect(helper.nft.burnToken(alice, collectionId, tokenId)).to.be.rejected; + + await helper.nft.burnToken(bob, collectionId, tokenId); + const token = await helper.nft.getToken(collectionId, tokenId); + expect(token).to.be.null; + }); +}); --- /dev/null +++ b/js-packages/tests/allowLists.test.ts @@ -0,0 +1,361 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, itSub} from './util/index.js'; +import type {ICollectionPermissions} from '@unique/playgrounds/types.js'; +import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js'; + +describe('Integration Test ext. Allow list tests', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor); + }); + }); + + describe('Positive', () => { + itSub('Owner can add address to allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + // allow list does not need to be enabled to add someone in advance + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + const allowList = await helper.nft.getAllowList(collectionId); + expect(allowList).to.deep.contain({Substrate: bob.address}); + }); + + itSub('Admin can add address to allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address}); + + // allow list does not need to be enabled to add someone in advance + await helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address}); + const allowList = await helper.nft.getAllowList(collectionId); + expect(allowList).to.deep.contain({Substrate: charlie.address}); + }); + + itSub('If address is already added to allow list, nothing happens', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + const allowList = await helper.nft.getAllowList(collectionId); + expect(allowList).to.deep.contain({Substrate: bob.address}); + }); + }); + + describe('Negative', () => { + itSub('Nobody can add address to allow list of non-existing collection', async ({helper}) => { + const collectionId = NON_EXISTENT_COLLECTION_ID; + await expect(helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('Nobody can add address to allow list of destroyed collection', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.collection.burn(alice, collectionId); + await expect(helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('Non-privileged user cannot add address to allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await expect(helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.NoPermission/); + }); + }); +}); + +describe('Integration Test ext. Remove from Allow List', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor); + }); + }); + + describe('Positive', () => { + itSub('Owner can remove address from allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + + await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); + + const allowList = await helper.nft.getAllowList(collectionId); + + expect(allowList).to.not.deep.contain({Substrate: bob.address}); + }); + + itSub('Admin can remove address from allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addAdmin(alice, collectionId, {Substrate: charlie.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + await helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: bob.address}); + + const allowList = await helper.nft.getAllowList(collectionId); + expect(allowList).to.not.deep.contain({Substrate: bob.address}); + }); + + itSub('If address is already removed from allow list, nothing happens', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); + const allowListBefore = await helper.nft.getAllowList(collectionId); + expect(allowListBefore).to.not.deep.contain({Substrate: bob.address}); + + await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); + + const allowListAfter = await helper.nft.getAllowList(collectionId); + expect(allowListAfter).to.not.deep.contain({Substrate: bob.address}); + }); + }); + + describe('Negative', () => { + itSub('Non-privileged user cannot remove address from allow list', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + await expect(helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.NoPermission/); + + const allowList = await helper.nft.getAllowList(collectionId); + expect(allowList).to.deep.contain({Substrate: charlie.address}); + }); + + itSub('Nobody can remove address from allow list of non-existing collection', async ({helper}) => { + const collectionId = NON_EXISTENT_COLLECTION_ID; + await expect(helper.collection.removeFromAllowList(bob, collectionId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('Nobody can remove address from allow list of deleted collection', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); + await helper.collection.burn(alice, collectionId); + + await expect(helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + }); +}); + +describe('Integration Test ext. Transfer if included in Allow List', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor); + }); + }); + + describe('Positive', () => { + itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transfer.', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner).to.be.deep.equal({Substrate: charlie.address}); + }); + + itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transferFrom.', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + + await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner).to.be.deep.equal({Substrate: charlie.address}); + }); + + itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + + await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner).to.be.deep.equal({Substrate: charlie.address}); + }); + + itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transferFrom', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + + await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner).to.be.deep.equal({Substrate: charlie.address}); + }); + }); + + describe('Negative', () => { + itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred from a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + + await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); + }); + + itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred from a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address}); + + await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); + }); + + itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred to a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + + await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); + }); + + itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred to a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); + await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); + + await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address}); + + await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); + }); + + itSub('If Public Access mode is set to AllowList, tokens can\'t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await expect(helper.nft.burnToken(bob, collectionId, tokenId)) + .to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('If Public Access mode is set to AllowList, token transfers can\'t be Approved by a non-allowlisted address (see Approve method)', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); + await expect(helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); + }); + }); +}); + +describe('Integration Test ext. Mint if included in Allow List', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + const permissionSet: ICollectionPermissions[] = [ + {access: 'Normal', mintMode: false}, + {access: 'Normal', mintMode: true}, + {access: 'AllowList', mintMode: false}, + {access: 'AllowList', mintMode: true}, + ]; + + const testPermissionSuite = (permissions: ICollectionPermissions) => { + const allowlistedMintingShouldFail = !permissions.mintMode!; + + const appropriateRejectionMessage = permissions.mintMode! ? /common\.AddressNotInAllowlist/ : /common\.PublicMintingNotAllowed/; + + const allowlistedMintingTest = () => itSub( + `With the condtions above, tokens can${allowlistedMintingShouldFail ? '\'t' : ''} be created by allow-listed addresses`, + async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setPermissions(alice, permissions); + await collection.addToAllowList(alice, {Substrate: bob.address}); + + if(allowlistedMintingShouldFail) + await expect(collection.mintToken(bob, {Substrate: bob.address})).to.be.rejectedWith(appropriateRejectionMessage); + else + await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected; + }, + ); + + + describe(`Public Access Mode = ${permissions.access}, Mint Mode = ${permissions.mintMode}`, () => { + describe('Positive', () => { + itSub('With the condtions above, tokens can be created by owner', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setPermissions(alice, permissions); + await expect(collection.mintToken(alice, {Substrate: alice.address})).to.not.be.rejected; + }); + + itSub('With the condtions above, tokens can be created by admin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setPermissions(alice, permissions); + await collection.addAdmin(alice, {Substrate: bob.address}); + await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected; + }); + + if(!allowlistedMintingShouldFail) allowlistedMintingTest(); + }); + + describe('Negative', () => { + itSub('With the condtions above, tokens can\'t be created by non-priviliged non-allow-listed address', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.setPermissions(alice, permissions); + await expect(collection.mintToken(bob, {Substrate: bob.address})) + .to.be.rejectedWith(appropriateRejectionMessage); + }); + + if(allowlistedMintingShouldFail) allowlistedMintingTest(); + }); + }); + }; + + for(const permissions of permissionSet) { + testPermissionSuite(permissions); + } +}); --- /dev/null +++ b/js-packages/tests/apiConsts.test.ts @@ -0,0 +1,116 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {ApiPromise} from '@polkadot/api'; +import {usingPlaygrounds, itSub, expect, COLLECTION_HELPER, CONTRACT_HELPER} from './util/index.js'; + + +const MAX_COLLECTION_DESCRIPTION_LENGTH = 256n; +const MAX_COLLECTION_NAME_LENGTH = 64n; +const COLLECTION_ADMINS_LIMIT = 5n; +const MAX_COLLECTION_PROPERTIES_SIZE = 40960n; +const MAX_TOKEN_PREFIX_LENGTH = 16n; +const MAX_PROPERTY_KEY_LENGTH = 256n; +const MAX_PROPERTY_VALUE_LENGTH = 32768n; +const MAX_PROPERTIES_PER_ITEM = 64n; +const MAX_TOKEN_PROPERTIES_SIZE = 32768n; +const NESTING_BUDGET = 5n; + +const DEFAULT_COLLETCTION_LIMIT = { + accountTokenOwnershipLimit: '1,000,000', + sponsoredDataSize: '2,048', + sponsoredDataRateLimit: 'SponsoringDisabled', + tokenLimit: '4,294,967,295', + sponsorTransferTimeout: '5', + sponsorApproveTimeout: '5', + ownerCanTransfer: false, + ownerCanDestroy: true, + transfersEnabled: true, +}; + +describe('integration test: API UNIQUE consts', () => { + let api: ApiPromise; + + before(async () => { + await usingPlaygrounds(async (helper) => { + api = await helper.getApi(); + }); + }); + + itSub('DEFAULT_NFT_COLLECTION_LIMITS', () => { + expect(api.consts.unique.nftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT); + }); + + itSub('DEFAULT_RFT_COLLECTION_LIMITS', () => { + expect(api.consts.unique.rftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT); + }); + + itSub('DEFAULT_FT_COLLECTION_LIMITS', () => { + expect(api.consts.unique.ftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT); + }); + + itSub('MAX_COLLECTION_NAME_LENGTH', () => { + checkConst(api.consts.unique.maxCollectionNameLength, MAX_COLLECTION_NAME_LENGTH); + }); + + itSub('MAX_COLLECTION_DESCRIPTION_LENGTH', () => { + checkConst(api.consts.unique.maxCollectionDescriptionLength, MAX_COLLECTION_DESCRIPTION_LENGTH); + }); + + itSub('MAX_COLLECTION_PROPERTIES_SIZE', () => { + checkConst(api.consts.unique.maxCollectionPropertiesSize, MAX_COLLECTION_PROPERTIES_SIZE); + }); + + itSub('MAX_TOKEN_PREFIX_LENGTH', () => { + checkConst(api.consts.unique.maxTokenPrefixLength, MAX_TOKEN_PREFIX_LENGTH); + }); + + itSub('MAX_PROPERTY_KEY_LENGTH', () => { + checkConst(api.consts.unique.maxPropertyKeyLength, MAX_PROPERTY_KEY_LENGTH); + }); + + itSub('MAX_PROPERTY_VALUE_LENGTH', () => { + checkConst(api.consts.unique.maxPropertyValueLength, MAX_PROPERTY_VALUE_LENGTH); + }); + + itSub('MAX_PROPERTIES_PER_ITEM', () => { + checkConst(api.consts.unique.maxPropertiesPerItem, MAX_PROPERTIES_PER_ITEM); + }); + + itSub('NESTING_BUDGET', () => { + checkConst(api.consts.unique.nestingBudget, NESTING_BUDGET); + }); + + itSub('MAX_TOKEN_PROPERTIES_SIZE', () => { + checkConst(api.consts.unique.maxTokenPropertiesSize, MAX_TOKEN_PROPERTIES_SIZE); + }); + + itSub('COLLECTION_ADMINS_LIMIT', () => { + checkConst(api.consts.unique.collectionAdminsLimit, COLLECTION_ADMINS_LIMIT); + }); + + itSub('HELPERS_CONTRACT_ADDRESS', () => { + expect(api.consts.evmContractHelpers.contractAddress.toString().toLowerCase()).to.be.equal(CONTRACT_HELPER.toLowerCase()); + }); + + itSub('EVM_COLLECTION_HELPERS_ADDRESS', () => { + expect(api.consts.common.contractAddress.toString().toLowerCase()).to.be.equal(COLLECTION_HELPER.toLowerCase()); + }); +}); + +function checkConst(constValue: any, expectedValue: T) { + expect(constValue.toBigInt()).equal(expectedValue); +} \ No newline at end of file --- /dev/null +++ b/js-packages/tests/approve.test.ts @@ -0,0 +1,658 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, Pallets, usingPlaygrounds} from './util/index.js'; +import {CrossAccountId} from '@unique/playgrounds/unique.js'; + + + +[ + {method: 'approveToken', account: (account: IKeyringPair) => CrossAccountId.fromKeyring(account).toICrossAccountId()}, + {method: 'approveTokenFromEth', account: (account: IKeyringPair) => CrossAccountId.fromKeyring(account).toEthereum().toICrossAccountId()}, +].map(testCase => { + describe(`Integration Test ${testCase.method}(spender, collection_id, item_id, amount):`, () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('[nft] Execute the extrinsic and check approvedList', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)}); + await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; + }); + + itSub('[fungible] Execute the extrinsic and check approvedList', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice)); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); + const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); + expect(amount).to.be.equal(1n); + }); + + itSub.ifWithPallets('[refungible] Execute the extrinsic and check approvedList', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n}); + await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); + const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); + expect(amount).to.be.equal(1n); + }); + + itSub('[nft] Remove approval by using 0 amount', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const collectionId = collection.collectionId; + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)}); + await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; + await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false; + }); + + itSub('[fungible] Remove approval by using 0 amount', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice)); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); + const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); + expect(amountBefore).to.be.equal(1n); + + await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); + const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); + expect(amountAfter).to.be.equal(0n); + }); + + itSub.ifWithPallets('[refungible] Remove approval by using 0 amount', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n}); + await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); + const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); + expect(amountBefore).to.be.equal(1n); + + await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); + const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); + expect(amountAfter).to.be.equal(0n); + }); + + itSub('can`t be called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); + const result = (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: charlie.address}); + await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned'); + }); + }); + + describe(`[${testCase.method}] Normal user can approve other users to transfer:`, () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('NFT', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); + await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.true; + }); + + itSub('Fungible up to an approved amount', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob)); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); + const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, testCase.account(bob)); + expect(amount).to.be.equal(1n); + }); + + itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n}); + await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 100n); + const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, testCase.account(bob)); + expect(amount).to.be.equal(100n); + }); + }); + + describe(`[${testCase.method}] Approved users can transferFrom up to approved amount:`, () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('NFT', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); + await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); + await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner).to.be.deep.equal({Substrate: alice.address}); + }); + + itSub('Fungible up to an approved amount', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob)); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); + const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n); + const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + expect(after - before).to.be.equal(1n); + }); + + itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n}); + await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); + const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n); + const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + expect(after - before).to.be.equal(1n); + }); + }); + + describe(`[${testCase.method}] Approved users cannot use transferFrom to repeat transfers if approved amount was already transferred:`, () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('NFT', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); + await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); + await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner).to.be.deep.equal({Substrate: alice.address}); + const transferTokenFromTx = () => helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}); + await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow'); + }); + + itSub('Fungible up to an approved amount', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob)); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); + const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n); + const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + expect(after - before).to.be.equal(1n); + + const transferTokenFromTx = () => helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n); + await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow'); + }); + + itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n}); + await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 100n); + const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 100n); + const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + expect(after - before).to.be.equal(100n); + const transferTokenFromTx = () => helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 100n); + await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow'); + }); + }); + + describe(`[${testCase.method}] Approved amount decreases by the transferred amount:`, () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + let dave: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor); + }); + }); + + itSub('If a user B is approved to transfer 10 Fungible tokens from user A, they can transfer 2 tokens to user C, which will result in decreasing approval from 10 to 8. Then user B can transfer 8 tokens to user D.', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice)); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 10n); + + const charlieBefore = await helper.ft.getBalance(collectionId, {Substrate: charlie.address}); + await helper.ft.transferTokenFrom(bob, collectionId, tokenId, testCase.account(alice), {Substrate: charlie.address}, 2n); + const charlieAfter = await helper.ft.getBalance(collectionId, {Substrate: charlie.address}); + expect(charlieAfter - charlieBefore).to.be.equal(2n); + + const daveBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); + await helper.ft.transferTokenFrom(bob, collectionId, tokenId, testCase.account(alice), {Substrate: dave.address}, 8n); + const daveAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); + expect(daveAfter - daveBefore).to.be.equal(8n); + }); + }); + + describe(`[${testCase.method}] User may clear the approvals to approving for 0 amount:`, () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('NFT', async ({helper}) => { + const owner = testCase.account(alice); + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: owner}); + + await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; + + await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false; + + const transferTokenFromTx = helper.nft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: bob.address}); + await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow'); + }); + + itSub('Fungible', async ({helper}) => { + const owner = testCase.account(alice); + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, owner); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); + const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner); + expect(amountBefore).to.be.equal(1n); + + await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); + const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner); + expect(amountAfter).to.be.equal(0n); + + const transferTokenFromTx = helper.ft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: charlie.address}, 1n); + await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow'); + }); + + itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => { + const owner = testCase.account(alice); + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner, pieces: 100n}); + await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); + const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner); + expect(amountBefore).to.be.equal(1n); + + await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); + const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner); + expect(amountAfter).to.be.equal(0n); + + const transferTokenFromTx = helper.rft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: charlie.address}, 1n); + await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow'); + }); + }); + + describe(`[${testCase.method}] User cannot approve for the amount greater than they own:`, () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('1 for NFT', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); + const result = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 2n); + await expect(result).to.be.rejectedWith('nonfungible.NonfungibleItemsHaveNoAmount'); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.false; + }); + + itSub('Fungible', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice)); + const tokenId = await helper.ft.getLastTokenId(collectionId); + const result = (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 11n); + await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned'); + }); + + itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n}); + const result = (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 101n); + await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned'); + }); + }); + + describe(`[${testCase.method}] Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:`, () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('cannot be called by collection admin on non-owned item', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)}); + await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); + const approveTx = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); + await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); + }); + }); + + describe(`[${testCase.method}] Negative Integration Test approve(spender, collection_id, item_id, amount):`, () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + const NONEXISTENT_COLLECTION = (2 ** 32) - 1; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('[nft] Approve for a collection that does not exist', async ({helper}) => { + const approveTx = (helper.nft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address}); + await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); + }); + + itSub('[fungible] Approve for a collection that does not exist', async ({helper}) => { + const approveTx = (helper.ft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address}); + await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); + }); + + itSub.ifWithPallets('[refungible] Approve for a collection that does not exist', [Pallets.ReFungible], async ({helper}) => { + const approveTx = (helper.rft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address}); + await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); + }); + + itSub('[nft] Approve for a collection that was destroyed', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.burn(alice, collectionId); + const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address}); + await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); + }); + + itSub('[fungible] Approve for a collection that was destroyed', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.ft.burn(alice, collectionId); + const approveTx = (helper.ft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address}); + await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); + }); + + itSub.ifWithPallets('[refungible] Approve for a collection that was destroyed', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.rft.burn(alice, collectionId); + const approveTx = (helper.rft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address}); + await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); + }); + + itSub('[nft] Approve transfer of a token that does not exist', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, 2, {Substrate: bob.address}); + await expect(approveTx).to.be.rejectedWith('common.TokenNotFound'); + }); + + itSub.ifWithPallets('[refungible] Approve transfer of a token that does not exist', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const approveTx = (helper.rft as any)[testCase.method](alice, collectionId, 2, {Substrate: bob.address}); + await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); // TODO: why the error is not common.TokenNotFound + }); + + itSub('[nft] Approve using the address that does not own the approved token', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)}); + const approveTx = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}); + await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); + }); + + itSub('[fungible] Approve using the address that does not own the approved token', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice)); + const tokenId = await helper.ft.getLastTokenId(collectionId); + const approveTx = (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}); + await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); + }); + + itSub.ifWithPallets('[refungible] Approve using the address that does not own the approved token', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n}); + const approveTx = (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}); + await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); + }); + + itSub.ifWithPallets('should fail if approved more ReFungibles than owned', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); + await helper.rft.transferToken(alice, collectionId, tokenId, testCase.account(bob), 100n); + await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 100n); + + const approveTx = (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 101n); + await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); + }); + + itSub('should fail if approved more Fungibles than owned', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.ft.mintTokens(alice, collectionId, 10n, alice.address); + const tokenId = await helper.ft.getLastTokenId(collectionId); + + await helper.ft.transferToken(alice, collectionId, tokenId, testCase.account(bob), 10n); + await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 10n); + const approveTx = (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 11n); + await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); + }); + + itSub('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); + await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: false}); + + const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: charlie.address}); + await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); + }); + }); +}); + +describe('Normal user can approve other users to be wallet operator:', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('[nft] Enable and disable approval', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + + const checkBeforeApproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); + expect(checkBeforeApproval).to.be.false; + + await helper.nft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, true); + const checkAfterApproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); + expect(checkAfterApproval).to.be.true; + + await helper.nft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, false); + const checkAfterDisapproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); + expect(checkAfterDisapproval).to.be.false; + }); + + itSub.ifWithPallets('[rft] Enable and disable approval', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + + const checkBeforeApproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); + expect(checkBeforeApproval).to.be.false; + + await helper.rft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, true); + const checkAfterApproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); + expect(checkAfterApproval).to.be.true; + + await helper.rft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, false); + const checkAfterDisapproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); + expect(checkAfterDisapproval).to.be.false; + }); +}); + +describe('Administrator and collection owner do not need approval in order to execute TransferFrom (with owner_can_transfer_flag = true):', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + let dave: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor); + }); + }); + + itSub('NFT', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: charlie.address}); + + await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}); + const owner1 = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner1).to.be.deep.equal({Substrate: dave.address}); + + await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); + await helper.nft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}); + const owner2 = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner2).to.be.deep.equal({Substrate: alice.address}); + }); + + itSub('Fungible up to an approved amount', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); + await helper.ft.mintTokens(alice, collectionId, 10n, charlie.address); + const tokenId = await helper.ft.getLastTokenId(collectionId); + + const daveBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); + await helper.ft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n); + const daveBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); + expect(daveBalanceAfter - daveBalanceBefore).to.be.equal(1n); + + await helper.collection.addAdmin(alice ,collectionId, {Substrate: bob.address}); + + const aliceBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + await helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n); + const aliceBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + expect(aliceBalanceAfter - aliceBalanceBefore).to.be.equal(1n); + }); + + itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: charlie.address, pieces: 100n}); + + const daveBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address}); + await helper.rft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n); + const daveAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address}); + expect(daveAfter - daveBefore).to.be.equal(1n); + + await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); + + const aliceBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + await helper.rft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n); + const aliceAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + expect(aliceAfter - aliceBefore).to.be.equal(1n); + }); +}); + +describe('Repeated approvals add up', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + let dave: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor); + }); + }); + + itSub.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. Fungible', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}); + await collection.mint(alice, 10n); + await collection.approveTokens(alice, {Substrate: bob.address}, 1n); + await collection.approveTokens(alice, {Substrate: charlie.address}, 1n); + // const allowances1 = await getAllowance(collectionId, 0, Alice.address, Bob.address); + // const allowances2 = await getAllowance(collectionId, 0, Alice.address, Charlie.address); + // expect(allowances1 + allowances2).to.be.eq(2n); + }); + + itSub.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. ReFungible', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + const token = await collection.mintToken(alice, 10n); + await token.approve(alice, {Substrate: bob.address}, 1n); + await token.approve(alice, {Substrate: charlie.address}, 1n); + // const allowances1 = await getAllowance(collectionId, itemId, Alice.address, Bob.address); + // const allowances2 = await getAllowance(collectionId, itemId, Alice.address, Charlie.address); + // expect(allowances1 + allowances2).to.be.eq(2n); + }); + + // Canceled by changing approve logic + itSub.skip('Cannot approve for more than total user\'s amount (owned: 10, approval 1: 5 - should succeed, approval 2: 6 - should fail). Fungible', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}); + await collection.mint(alice, 10n, {Substrate: dave.address}); + await collection.approveTokens(dave, {Substrate: bob.address}, 5n); + await expect(collection.approveTokens(dave, {Substrate: charlie.address}, 6n)) + .to.be.rejectedWith('this test would fail (since it is skipped), replace this expecting message with what would have been received'); + }); + + // Canceled by changing approve logic + itSub.skip('Cannot approve for more than total user\'s amount (owned: 100, approval 1: 50 - should succeed, approval 2: 51 - should fail). ReFungible', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + const token = await collection.mintToken(alice, 100n, {Substrate: dave.address}); + await token.approve(dave, {Substrate: bob.address}, 50n); + await expect(token.approve(dave, {Substrate: charlie.address}, 51n)) + .to.be.rejectedWith('this test would fail (since it is skipped), replace this expecting message with what would have been received'); + }); +}); --- /dev/null +++ b/js-packages/tests/burnItem.test.ts @@ -0,0 +1,165 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, usingPlaygrounds} from './util/index.js'; + + +describe('integration test: ext. burnItem():', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([100n], donor); + }); + }); + + itSub('Burn item in NFT collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice); + const token = await collection.mintToken(alice); + + await token.burn(alice); + expect(await token.doesExist()).to.be.false; + }); + + itSub('Burn item in Fungible collection', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}, 10); + await collection.mint(alice, 10n); + + await collection.burnTokens(alice, 1n); + expect(await collection.getBalance({Substrate: alice.address})).to.eq(9n); + }); +}); + +describe('integration test: ext. burnItem() with admin permissions:', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('Burn item in NFT collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice); + await collection.setLimits(alice, {ownerCanTransfer: true}); + await collection.addAdmin(alice, {Substrate: bob.address}); + const token = await collection.mintToken(alice); + + await token.burnFrom(bob, {Substrate: alice.address}); + expect(await token.doesExist()).to.be.false; + }); + + itSub('Burn item in Fungible collection', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}, 0); + await collection.setLimits(alice, {ownerCanTransfer: true}); + await collection.addAdmin(alice, {Substrate: bob.address}); + await collection.mint(alice, 10n); + + await collection.burnTokensFrom(bob, {Substrate: alice.address}, 1n); + expect(await collection.getBalance({Substrate: alice.address})).to.eq(9n); + }); +}); + +describe('Negative integration test: ext. burnItem():', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('Burn a token that was never created', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice); + await expect(collection.burnToken(alice, 10)).to.be.rejectedWith('common.TokenNotFound'); + }); + + itSub('Burn a token using the address that does not own it', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice); + const token = await collection.mintToken(alice); + + await expect(token.burn(bob)).to.be.rejectedWith('common.NoPermission'); + }); + + itSub('Transfer a burned token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice); + const token = await collection.mintToken(alice); + await token.burn(alice); + + await expect(token.transfer(alice, {Substrate: bob.address})).to.be.rejectedWith('common.TokenNotFound'); + }); + + itSub('Burn more than owned in Fungible collection', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}, 0); + await collection.mint(alice, 10n); + + await expect(collection.burnTokens(alice, 11n)).to.be.rejectedWith('common.TokenValueTooLow'); + expect(await collection.getBalance({Substrate: alice.address})).to.eq(10n); + }); + + itSub('Zero burn NFT', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Coll', description: 'Desc', tokenPrefix: 'T'}); + const tokenAlice = await collection.mintToken(alice, {Substrate: alice.address}); + const tokenBob = await collection.mintToken(alice, {Substrate: bob.address}); + + // 1. Zero burn of own tokens allowed: + await helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, tokenAlice.tokenId, 0]); + // 2. Zero burn of non-owned tokens not allowed: + await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, tokenBob.tokenId, 0])).to.be.rejectedWith('common.NoPermission'); + // 3. Zero burn of non-existing tokens not allowed: + await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, 9999, 0])).to.be.rejectedWith('common.TokenNotFound'); + expect(await tokenAlice.doesExist()).to.be.true; + expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: alice.address}); + expect(await tokenBob.getOwner()).to.deep.eq({Substrate: bob.address}); + // 4. Storage is not corrupted: + await tokenAlice.transfer(alice, {Substrate: bob.address}); + await tokenBob.transfer(bob, {Substrate: alice.address}); + expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: bob.address}); + expect(await tokenBob.getOwner()).to.deep.eq({Substrate: alice.address}); + }); + + itSub('zero burnFrom NFT', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Zero', description: 'Zero transfer', tokenPrefix: 'TF'}); + const notApprovedNft = await collection.mintToken(alice, {Substrate: bob.address}); + const approvedNft = await collection.mintToken(alice, {Substrate: bob.address}); + await approvedNft.approve(bob, {Substrate: alice.address}); + + // 1. Zero burnFrom of non-existing tokens not allowed: + await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, 9999, 0])).to.be.rejectedWith('common.ApprovedValueTooLow'); + // 2. Zero burnFrom of not approved tokens not allowed: + await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, notApprovedNft.tokenId, 0])).to.be.rejectedWith('common.ApprovedValueTooLow'); + // 3. Zero burnFrom of approved tokens allowed: + await helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, approvedNft.tokenId, 0]); + + // 4.1 approvedNft still approved: + expect(await approvedNft.isApproved({Substrate: alice.address})).to.be.true; + // 4.2 bob is still the owner: + expect(await approvedNft.getOwner()).to.deep.eq({Substrate: bob.address}); + expect(await notApprovedNft.getOwner()).to.deep.eq({Substrate: bob.address}); + // 4.3 Alice can burn approved nft: + await approvedNft.burnFrom(alice, {Substrate: bob.address}); + expect(await approvedNft.doesExist()).to.be.false; + }); +}); --- /dev/null +++ b/js-packages/tests/change-collection-owner.test.ts @@ -0,0 +1,170 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, itSub} from './util/index.js'; +import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js'; + +describe('Integration Test changeCollectionOwner(collection_id, new_owner):', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('Changing owner changes owner address', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const beforeChanging = await helper.collection.getData(collection.collectionId); + expect(beforeChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address)); + + await collection.changeOwner(alice, bob.address); + const afterChanging = await helper.collection.getData(collection.collectionId); + expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address)); + }); +}); + +describe('Integration Test changeCollectionOwner(collection_id, new_owner) special checks for exOwner:', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('Changing the owner of the collection is not allowed for the former owner', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + + await collection.changeOwner(alice, bob.address); + + const changeOwnerTx = () => collection.changeOwner(alice, alice.address); + await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/); + + const afterChanging = await helper.collection.getData(collection.collectionId); + expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address)); + }); + + itSub('New collectionOwner has access to sponsorship management operations in the collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.changeOwner(alice, bob.address); + + const afterChanging = await helper.collection.getData(collection.collectionId); + expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address)); + + await collection.setSponsor(bob, charlie.address); + await collection.confirmSponsorship(charlie); + await collection.removeSponsor(bob); + const limits = { + accountTokenOwnershipLimit: 1, + tokenLimit: 1, + sponsorTransferTimeout: 1, + ownerCanDestroy: true, + ownerCanTransfer: true, + }; + + await collection.setLimits(bob, limits); + const gotLimits = await collection.getEffectiveLimits(); + expect(gotLimits).to.be.deep.contains(limits); + + await collection.setPermissions(bob, {access: 'AllowList', mintMode: true}); + + await collection.burn(bob); + const collectionData = await helper.collection.getData(collection.collectionId); + expect(collectionData).to.be.null; + }); + + itSub('New collectionOwner has access to changeCollectionOwner', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.changeOwner(alice, bob.address); + await collection.changeOwner(bob, charlie.address); + const collectionData = await collection.getData(); + expect(collectionData?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(charlie.address)); + }); +}); + +describe('Negative Integration Test changeCollectionOwner(collection_id, new_owner):', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('Not owner can\'t change owner.', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const changeOwnerTx = () => collection.changeOwner(bob, bob.address); + await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Collection admin can\'t change owner.', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + const changeOwnerTx = () => collection.changeOwner(bob, bob.address); + await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Can\'t change owner of a non-existing collection.', async ({helper}) => { + const collectionId = NON_EXISTENT_COLLECTION_ID; + const changeOwnerTx = () => helper.collection.changeOwner(bob, collectionId, bob.address); + await expect(changeOwnerTx()).to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('Former collectionOwner not allowed to sponsorship management operations in the collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.changeOwner(alice, bob.address); + + const changeOwnerTx = () => collection.changeOwner(alice, alice.address); + await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/); + + const afterChanging = await helper.collection.getData(collection.collectionId); + expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address)); + + const setSponsorTx = () => collection.setSponsor(alice, charlie.address); + const confirmSponsorshipTx = () => collection.confirmSponsorship(alice); + const removeSponsorTx = () => collection.removeSponsor(alice); + await expect(setSponsorTx()).to.be.rejectedWith(/common\.NoPermission/); + await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); + await expect(removeSponsorTx()).to.be.rejectedWith(/common\.NoPermission/); + + const limits = { + accountTokenOwnershipLimit: 1, + tokenLimit: 1, + sponsorTransferTimeout: 1, + ownerCanDestroy: true, + ownerCanTransfer: true, + }; + + const setLimitsTx = () => collection.setLimits(alice, limits); + await expect(setLimitsTx()).to.be.rejectedWith(/common\.NoPermission/); + + const setPermissionTx = () => collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await expect(setPermissionTx()).to.be.rejectedWith(/common\.NoPermission/); + + const burnTx = () => collection.burn(alice); + await expect(burnTx()).to.be.rejectedWith(/common\.NoPermission/); + }); +}); --- /dev/null +++ b/js-packages/tests/check-event/burnItemEvent.test.ts @@ -0,0 +1,44 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, itSub} from '../util/index.js'; +import type {IEvent} from '@unique/playgrounds/types.js'; + + +describe('Burn Item event ', () => { + let alice: IKeyringPair; + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + itSub('Check event from burnItem(): ', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, {Substrate: alice.address}); + await token.burn(alice); + await helper.wait.newBlocks(1); + + const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; + const eventStrings = event.map(e => `${e.section}.${e.method}`); + + expect(eventStrings).to.contains('common.ItemDestroyed'); + expect(eventStrings).to.contains('treasury.Deposit'); + expect(eventStrings).to.contains('system.ExtrinsicSuccess'); + }); +}); --- /dev/null +++ b/js-packages/tests/check-event/createCollectionEvent.test.ts @@ -0,0 +1,40 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, itSub, expect} from '../util/index.js'; +import type {IEvent} from '@unique/playgrounds/types.js'; + +describe('Create collection event ', () => { + let alice: IKeyringPair; + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + itSub('Check event from createCollection(): ', async ({helper}) => { + await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + await helper.wait.newBlocks(1); + const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; + const eventStrings = event.map(e => `${e.section}.${e.method}`); + + expect(eventStrings).to.contains('common.CollectionCreated'); + expect(eventStrings).to.contains('treasury.Deposit'); + expect(eventStrings).to.contains('system.ExtrinsicSuccess'); + }); +}); --- /dev/null +++ b/js-packages/tests/check-event/createItemEvent.test.ts @@ -0,0 +1,41 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from '../util/index.js'; +import type {IEvent} from '@unique/playgrounds/types.js'; + +describe('Create Item event ', () => { + let alice: IKeyringPair; + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + itSub('Check event from createItem(): ', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + await collection.mintToken(alice, {Substrate: alice.address}); + await helper.wait.newBlocks(1); + const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; + const eventStrings = event.map(e => `${e.section}.${e.method}`); + + expect(eventStrings).to.contains('common.ItemCreated'); + expect(eventStrings).to.contains('treasury.Deposit'); + expect(eventStrings).to.contains('system.ExtrinsicSuccess'); + }); +}); --- /dev/null +++ b/js-packages/tests/check-event/createMultipleItemsEvent.test.ts @@ -0,0 +1,46 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, itSub, expect} from '../util/index.js'; +import type {IEvent} from '@unique/playgrounds/types.js'; + +describe('Create Multiple Items Event event ', () => { + let alice: IKeyringPair; + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + itSub('Check event from createMultipleItems(): ', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + + await collection.mintMultipleTokens(alice, [ + {owner: {Substrate: alice.address}}, + {owner: {Substrate: alice.address}}, + ]); + + await helper.wait.newBlocks(1); + const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; + const eventStrings = event.map(e => `${e.section}.${e.method}`); + + expect(eventStrings).to.contains('common.ItemCreated'); + expect(eventStrings).to.contains('treasury.Deposit'); + expect(eventStrings).to.contains('system.ExtrinsicSuccess'); + }); +}); --- /dev/null +++ b/js-packages/tests/check-event/destroyCollectionEvent.test.ts @@ -0,0 +1,42 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from '../util/index.js'; +import type {IEvent} from '@unique/playgrounds/types.js'; + +describe('Destroy collection event ', () => { + let alice: IKeyringPair; + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + + itSub('Check event from destroyCollection(): ', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + await collection.burn(alice); + await helper.wait.newBlocks(1); + const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; + const eventStrings = event.map(e => `${e.section}.${e.method}`); + + expect(eventStrings).to.contains('common.CollectionDestroyed'); + expect(eventStrings).to.contains('treasury.Deposit'); + expect(eventStrings).to.contains('system.ExtrinsicSuccess'); + }); +}); --- /dev/null +++ b/js-packages/tests/check-event/transferEvent.test.ts @@ -0,0 +1,45 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, itSub} from '../util/index.js'; +import type {IEvent} from '@unique/playgrounds/types.js'; + +describe('Transfer event ', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); + }); + }); + + itSub('Check event from transfer(): ', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, {Substrate: alice.address}); + await token.transfer(alice, {Substrate: bob.address}); + await helper.wait.newBlocks(1); + const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; + const eventStrings = event.map(e => `${e.section}.${e.method}`); + + expect(eventStrings).to.contains('common.Transfer'); + expect(eventStrings).to.contains('treasury.Deposit'); + expect(eventStrings).to.contains('system.ExtrinsicSuccess'); + }); +}); --- /dev/null +++ b/js-packages/tests/check-event/transferFromEvent.test.ts @@ -0,0 +1,44 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, itSub} from '../util/index.js'; +import type {IEvent} from '@unique/playgrounds/types.js'; + +describe('Transfer event ', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); + }); + }); + + itSub('Check event from transfer(): ', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, {Substrate: alice.address}); + await token.transferFrom(alice, {Substrate: alice.address}, {Substrate: bob.address}); + await helper.wait.newBlocks(1); + const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; + const eventStrings = event.map(e => `${e.section}.${e.method}`); + + expect(eventStrings).to.contains('common.Transfer'); + expect(eventStrings).to.contains('treasury.Deposit'); + expect(eventStrings).to.contains('system.ExtrinsicSuccess'); + }); +}); --- /dev/null +++ b/js-packages/tests/collator-selection/collatorSelection.seqtest.ts @@ -0,0 +1,423 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util/index.js'; + +async function nodeAddress(name: string) { + // eslint-disable-next-line require-await + return await usingPlaygrounds(async (helper) => { + const envNodeStash = `RELAY_UNIQUE_NODE_${name.toUpperCase()}_STASH`; + + const nodeStash = process.env[envNodeStash]; + if(nodeStash) { + return helper.address.normalizeSubstrateToChainFormat(nodeStash); + } else { + throw Error(`"${envNodeStash}" env var is not set`); + } + }); +} + +async function getInitialInvulnerables() { + return await Promise.all([ + nodeAddress('alpha'), + nodeAddress('beta'), + nodeAddress('gamma'), + nodeAddress('delta'), + ]); +} + +async function resetInvulnerables() { + await usingPlaygrounds(async (helper, privateKey) => { + const superuser = await privateKey('//Alice'); + const initialInvulnerables = await getInitialInvulnerables(); + + const invulnerables = await helper.collatorSelection.getInvulnerables(); + + // Remove all invulnerables but the first one + const firstInvulnerable = invulnerables[0]; + + let nonce = await helper.chain.getNonce(superuser.address); + await Promise.all(invulnerables.slice(1).map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++}))); + + // Add the initial invulnerables + await Promise.all(initialInvulnerables.map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [invulnerable], true, {nonce: nonce++}))); + + // Remove the first invulnerable if it's not an initial one + if(!initialInvulnerables.includes(firstInvulnerable)) { + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [firstInvulnerable]); + } + }); +} + +// todo:collator Most preferable to launch this test in parallel somehow -- or change the session period (1 hr). +describe('Integration Test: Collator Selection', () => { + let superuser: IKeyringPair; + let previousLicenseBond = 0n; + let licenseBond = 0n; + + before(async function() { + if(!process.env.RUN_COLLATOR_TESTS) this.skip(); + + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.CollatorSelection]); + superuser = await privateKey('//Alice'); + + previousLicenseBond = await helper.collatorSelection.getLicenseBond(); + licenseBond = 10n * helper.balance.getOneTokenNominal(); + await helper.getSudo().collatorSelection.setLicenseBond(superuser, licenseBond); + }); + }); + + describe('Dynamic shuffling of collators', () => { + // These two are the default invulnerables, and should return to be invulnerables after this suite. + let alphaNode: string; + let betaNode: string; + + let gammaNode: string; + let deltaNode: string; + + before(async function() { + await usingPlaygrounds(async (helper) => { + // todo:collator see again if blocks start to be finalized in dev mode + // Skip the collator block production in dev mode, since the blocks are sealed automatically. + if(await helper.arrange.isDevNode()) this.skip(); + + [alphaNode, betaNode, gammaNode, deltaNode] = await getInitialInvulnerables(); + + const invulnerables = await helper.collatorSelection.getInvulnerables(); + expect(invulnerables.length, 'Invalid initial invulnerables number').to.be.equal(4); + expect(invulnerables, 'Invalid initial invulnerables').containSubset([alphaNode, betaNode, gammaNode, deltaNode]); + }); + }); + + itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => { + let nonce = await helper.chain.getNonce(superuser.address); + + nonce = await helper.chain.getNonce(superuser.address); + await expect(Promise.all([ + helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alphaNode], true, {nonce: nonce++}), + helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [betaNode], true, {nonce: nonce++}), + ])).to.be.fulfilled; + + const newInvulnerables = await helper.collatorSelection.getInvulnerables(); + expect(newInvulnerables).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2); + + await helper.wait.newSessions(2); + + const newValidators = await helper.callRpc('api.query.session.validators'); + expect(newValidators).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2); + + const lastBlockNumber = await helper.chain.getLatestBlockNumber(); + await helper.wait.newBlocks(1); + const lastGammaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [gammaNode])).toNumber(); + const lastDeltaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [deltaNode])).toNumber(); + expect(lastGammaBlock >= lastBlockNumber || lastDeltaBlock >= lastBlockNumber).to.be.true; + }); + + after(async () => { + await resetInvulnerables(); + }); + }); + + describe('Getting and releasing licenses to collate', () => { + let crowd: IKeyringPair[]; + + before(async function() { + await usingPlaygrounds(async (helper) => { + crowd = await helper.arrange.createCrowd(20, 100n, superuser); + + // set session keys for everyone + await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc))); + }); + }); + + describe('Positive', () => { + itSub('Can lease and release a license', async ({helper}) => { + const account = crowd.pop()!; + + // make sure it does not have any reserved funds + expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(0n); + + // getting a license reserves a license bond cost + await helper.collatorSelection.obtainLicense(account); + expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond); + expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(licenseBond); + + // releasing a license un-reserves the license bond cost + await helper.collatorSelection.releaseLicense(account); + expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n); + + const balance = await helper.balance.getSubstrateFull(account.address); + expect(balance.reserved).to.be.equal(0n); + expect(balance.free > 100n - licenseBond); + }); + + itSub('Can force revoke a license', async ({helper}) => { + const account = crowd.pop()!; + + // getting a license reserves a license bond cost + const previousBalance = await helper.balance.getSubstrateFull(account.address); + await helper.collatorSelection.obtainLicense(account); + expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond); + + // force-releasing a license un-reserves the license bond cost as well + await helper.getSudo().collatorSelection.forceReleaseLicense(superuser, account.address); + expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(previousBalance.reserved); + + const balance = await helper.balance.getSubstrateFull(account.address); + expect(balance.reserved).to.be.equal(previousBalance.reserved); + expect(balance.free > previousBalance.free - licenseBond); + }); + }); + + describe('Negative', () => { + itSub('Cannot get a license without session keys set', async ({helper}) => { + const [account] = await helper.arrange.createAccounts([100n], superuser); + await expect(helper.collatorSelection.obtainLicense(account)) + .to.be.rejectedWith(/collatorSelection.ValidatorNotRegistered/); + }); + + itSub('Cannot register a license twice', async ({helper}) => { + const account = crowd.pop()!; + await helper.collatorSelection.obtainLicense(account); + await expect(helper.collatorSelection.obtainLicense(account)) + .to.be.rejectedWith(/collatorSelection.AlreadyHoldingLicense/); + }); + + itSub('Cannot release a license twice', async ({helper}) => { + const account = crowd.pop()!; + await helper.collatorSelection.obtainLicense(account); + await helper.collatorSelection.releaseLicense(account); + await expect(helper.collatorSelection.releaseLicense(account)) + .to.be.rejectedWith(/collatorSelection.NoLicense/); + }); + + itSub('Cannot force revoke a license as non-sudo', async ({helper}) => { + const account = crowd.pop()!; + await helper.collatorSelection.obtainLicense(account); + await expect(helper.collatorSelection.forceReleaseLicense(superuser, account.address)) + .to.be.rejectedWith(/BadOrigin/); + }); + }); + }); + + describe('Onboarding, collating, and offboarding as collator candidates', () => { + let crowd: IKeyringPair[]; + + before(async function() { + await usingPlaygrounds(async (helper) => { + crowd = await helper.arrange.createCrowd(20, 100n, superuser); + + // set session keys for everyone + await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc))); + }); + }); + + describe('Positive', () => { + itSub('Can onboard and offboard repeatedly', async ({helper}) => { + const account = crowd.pop()!; + await helper.collatorSelection.obtainLicense(account); + await helper.collatorSelection.onboard(account); + expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]); + + await helper.collatorSelection.offboard(account); + expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]); + + await helper.collatorSelection.onboard(account); + expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]); + + await helper.collatorSelection.offboard(account); + expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]); + }); + + itSub('Penalizes and forfeits license from faulty collators', async ({helper}) => { + // This one shouldn't even be able to produce blocks. + const account = crowd.pop()!; + await helper.collatorSelection.obtainLicense(account); + await helper.collatorSelection.onboard(account); + expect(await helper.collatorSelection.getCandidates()).to.contain(account.address); + + // Wait for 3 new sessions before checking that the collator will be kicked: + // one to get collator onboarded, and another two for the collator to fail + await helper.wait.newSessions(3); + + expect(await helper.collatorSelection.getCandidates()).to.not.contain(account.address); + expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n); + + // The account's reserved funds get slashed as a penalty + const balance = await helper.balance.getSubstrateFull(account.address); + expect(balance.reserved).to.be.equal(0n); + expect(balance.free < 100n - licenseBond); + }); + }); + + describe('Negative', () => { + itSub('Cannot onboard without a license', async ({helper}) => { + const account = crowd.pop()!; + await expect(helper.collatorSelection.onboard(account)) + .to.be.rejectedWith(/collatorSelection.NoLicense/); + }); + + itSub('Cannot offboard without a license', async ({helper}) => { + const account = crowd.pop()!; + await expect(helper.collatorSelection.offboard(account)) + .to.be.rejectedWith(/collatorSelection.NotCandidate/); + }); + + itSub('Cannot offboard while not onboarded', async ({helper}) => { + const account = crowd.pop()!; + await helper.collatorSelection.obtainLicense(account); + await expect(helper.collatorSelection.offboard(account)) + .to.be.rejectedWith(/collatorSelection.NotCandidate/); + }); + + itSub('Cannot onboard while already onboarded', async ({helper}) => { + const account = crowd.pop()!; + await helper.collatorSelection.obtainLicense(account); + await helper.collatorSelection.onboard(account); + await expect(helper.collatorSelection.onboard(account)) + .to.be.rejectedWith(/collatorSelection.AlreadyCandidate/); + }); + }); + }); + + describe('Addition and removal of invulnerables', () => { + describe('Positive', () => { + itSub('Adds an invulnerable', async ({helper}) => { + const [account] = await helper.arrange.createAccounts([10n], superuser); + const invulnerables = await helper.collatorSelection.getInvulnerables(); + + await helper.session.setOwnKeysFromAddress(account); + await helper.getSudo().collatorSelection.addInvulnerable(superuser, account.address); + + const newInvulnerables = await helper.collatorSelection.getInvulnerables(); + expect(invulnerables.concat(account.address)).to.have.all.members(newInvulnerables); + }); + + itSub('Removes an invulnerable', async ({helper}) => { + const invulnerables = await helper.collatorSelection.getInvulnerables(); + const lastInvulnerable = invulnerables.pop()!; + + await helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable); + const newInvulnerables = await helper.collatorSelection.getInvulnerables(); + // invulnerables had its last element removed, so they should be equal + expect(newInvulnerables).to.have.all.members(invulnerables); + }); + }); + + describe('Negative', () => { + itSub('Does not duplicate an invulnerable', async ({helper}) => { + const invulnerables = await helper.collatorSelection.getInvulnerables(); + // adding an already invulnerable should not fail, but should not duplicate it either + await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, invulnerables[0])) + .to.be.fulfilled; + const newInvulnerables = await helper.collatorSelection.getInvulnerables(); + expect(newInvulnerables).to.have.all.members(invulnerables); + }); + + itSub('Cannot remove a non-existent invulnerable', async ({helper}) => { + const [account] = await helper.arrange.createAccounts([0n], superuser); + await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, account.address)) + .to.be.rejectedWith(/collatorSelection.NotInvulnerable/); + }); + + itSub('Cannot allow invulnerables to be empty', async ({helper}) => { + const invulnerables = await helper.collatorSelection.getInvulnerables(); + const lastInvulnerable = invulnerables.pop()!; + + let nonce = await helper.chain.getNonce(superuser.address); + await Promise.all(invulnerables.map((i: any) => + helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i], true, {nonce: nonce++}))); + + await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable)) + .to.be.rejectedWith(/collatorSelection.TooFewInvulnerables/); + + const newInvulnerables = await helper.collatorSelection.getInvulnerables(); + expect(newInvulnerables).to.be.deep.equal([lastInvulnerable]); + + // restore the invulnerables to the previous state + nonce = await helper.chain.getNonce(superuser.address); + await Promise.all(invulnerables.map((i: any) => + helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i], true, {nonce: nonce++}))); + }); + + itSub('Cannot have too many invulnerables', async ({helper}) => { + // todo:collator make sure that there is enough session time for a set of tests + // 28 non-functioning collators, teehee. + + const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length; + const invulnerablesUntilLimit = (await helper.collatorSelection.getDesiredCollators()) - invulnerablesLength; + const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser); + const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser); + + await Promise.all(newInvulnerables.map((i: IKeyringPair) => + helper.session.setOwnKeysFromAddress(i))); + await helper.session.setOwnKeysFromAddress(lastInvulnerable); + + let nonce = await helper.chain.getNonce(superuser.address); + await Promise.all(newInvulnerables.map((i: IKeyringPair) => + helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i.address], true, {nonce: nonce++}))); + + await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, lastInvulnerable.address)) + .to.be.rejectedWith(/collatorSelection.TooManyInvulnerables/); + + // restore the invulnerables to the previous state + nonce = await helper.chain.getNonce(superuser.address); + await Promise.all(newInvulnerables.map((i: IKeyringPair) => + helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i.address], true, {nonce: nonce++}))); + }); + + itSub('Forbids a non-sudo to add an invulnerable', async ({helper}) => { + const [account] = await helper.arrange.createAccounts([10n], superuser); + const invulnerables = await helper.collatorSelection.getInvulnerables(); + + await helper.session.setOwnKeysFromAddress(account); + await expect(helper.collatorSelection.addInvulnerable(superuser, account.address)) + .to.be.rejectedWith(/BadOrigin/); + + const newInvulnerables = await helper.collatorSelection.getInvulnerables(); + expect(newInvulnerables).to.be.members(invulnerables); + }); + + itSub('Forbids a non-sudo to remove an invulnerable', async ({helper}) => { + const invulnerables = await helper.collatorSelection.getInvulnerables(); + await expect(helper.collatorSelection.removeInvulnerable(superuser, invulnerables[0])) + .to.be.rejectedWith(/BadOrigin/); + expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables); + }); + + after(async function() { + await resetInvulnerables(); + }); + }); + }); + + after(async function() { + if(!process.env.RUN_COLLATOR_TESTS) return; + + await usingPlaygrounds(async (helper) => { + if(helper.fetchMissingPalletNames([Pallets.CollatorSelection]).length != 0) return; + + await helper.getSudo().collatorSelection.setLicenseBond(superuser, previousLicenseBond); + + const candidates = await helper.collatorSelection.getCandidates(); + let nonce = await helper.chain.getNonce(superuser.address); + await Promise.all(candidates.map(candidate => + helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.forceReleaseLicense', [candidate], true, {nonce: nonce++}))); + }); + }); +}); --- /dev/null +++ b/js-packages/tests/collator-selection/identity.seqtest.ts @@ -0,0 +1,284 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util/index.js'; +import {UniqueHelper} from '@unique/playgrounds/unique.js'; + +async function getIdentities(helper: UniqueHelper) { + const identities: [string, any][] = []; + for(const [key, value] of await helper.getApi().query.identity.identityOf.entries()) + identities.push([(key as any).toHuman(), (value as any).unwrap()]); + return identities; +} + +async function getIdentityAccounts(helper: UniqueHelper) { + return (await getIdentities(helper)).flatMap(([key, _value]) => key); +} + +async function getSubIdentityAccounts(helper: UniqueHelper, address: string) { + return ((await helper.getApi().query.identity.subsOf(address)).toHuman() as any)[1]; +} + +async function getSubIdentityName(helper: UniqueHelper, address: string) { + return ((await helper.getApi().query.identity.superOf(address)).toHuman() as any); +} + +describe('Integration Test: Identities Manipulation', () => { + let superuser: IKeyringPair; + + before(async function() { + if(!process.env.RUN_COLLATOR_TESTS) this.skip(); + + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.Identity]); + superuser = await privateKey('//Alice'); + }); + }); + + itSub('Normal calls do not work', async ({helper}) => { + // console.error = () => {}; + await expect(helper.executeExtrinsic(superuser, 'api.tx.identity.setIdentity', [{info: {display: {Raw: 'Meowser'}}}] as any)) + .to.be.rejectedWith(/Transaction call is not expected/); + }); + + describe('Identities', () => { + itSub('Sets identities', async ({helper}) => { + const oldIdentitiesCount = (await getIdentityAccounts(helper)).length; + + const crowdSize = 10; + const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser); + const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]); + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any); + + expect((await getIdentityAccounts(helper)).length).to.be.equal(oldIdentitiesCount + crowdSize); + }); + + itSub('Setting identities does not delete existing but does overwrite', async ({helper}) => { + const crowd = await helper.arrange.createCrowd(10, 0n, superuser); + const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]); + + // insert a single identity + let singleIdentity = identities.pop()!; + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [[singleIdentity]] as any); + + const oldIdentitiesCount = (await getIdentityAccounts(helper)).length; + + // change an identity and push it with a few new others + singleIdentity = [singleIdentity[0], {info: {display: {Raw: 'something special'}}}]; + identities.push(singleIdentity); + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any); + + // oldIdentitiesCount + 9 because one identity is overwritten, not inserted on top + expect((await getIdentityAccounts(helper)).length).to.be.equal(oldIdentitiesCount + 9); + expect((await helper.callRpc('api.query.identity.identityOf', [singleIdentity[0]])).toHuman().info.display) + .to.be.deep.equal({Raw: 'something special'}); + }); + + itSub('Removes identities', async ({helper}) => { + const crowd = await helper.arrange.createCrowd(10, 0n, superuser); + const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]); + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any); + const oldIdentities = await getIdentityAccounts(helper); + + // delete a couple, check that they are no longer there + const scapegoats = [crowd.pop()!.address, crowd.pop()!.address]; + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [scapegoats]); + const newIdentities = await getIdentityAccounts(helper); + expect(newIdentities.concat(scapegoats)).to.be.have.members(oldIdentities); + }); + }); + + describe('Sub-identities', () => { + itSub('Sets subs', async ({helper}) => { + const crowdSize = 18; + const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser); + const supers = [crowd.pop()!, crowd.pop()!, crowd.pop()!]; + + const subsPerSup = crowd.length / supers.length; + let subCount = 0; + const subs = [ + crowd.slice(subCount, subCount += subsPerSup + 1), + crowd.slice(subCount, subCount += subsPerSup), + crowd.slice(subCount, subCount += subsPerSup - 1), + ]; + + const subsInfo = supers.map((acc, i) => [ + acc.address, [ + 1000000n + BigInt(i + 1), + subs[i].map((sub, j) => [ + sub.address, {Raw: `accounter #${j}`}, + ]), + ], + ]); + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo] as any); + + for(let i = 0; i < supers.length; i++) { + // check deposit + expect(((await helper.getApi().query.identity.subsOf(supers[i].address)).toJSON() as any)[0]).to.be.equal(1000001 + i); + + const subsAccounts = await getSubIdentityAccounts(helper, supers[i].address); + // check sub-identities as account ids + expect(subsAccounts).to.include.members(subs[i].map(x => x.address)); + + for(let j = 0; j < subsAccounts.length; j++) { + // check sub-identities' names + expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `accounter #${j}`}); + } + } + }); + + itSub('Setting sub-identities does not delete other existing but does overwrite own', async ({helper}) => { + const crowdSize = 18; + const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser); + const supers = [crowd.pop()!, crowd.pop()!, crowd.pop()!]; + + const subsPerSup = crowd.length / supers.length; + let subCount = 0; + const subs = [ + crowd.slice(subCount, subCount += subsPerSup + 1), + crowd.slice(subCount, subCount += subsPerSup), + crowd.slice(subCount, subCount += subsPerSup - 1), + ]; + + const subsInfo1 = supers.map((acc, i) => [ + acc.address, [ + 1000000n + BigInt(i + 1), + subs[i].map((sub, j) => [ + sub.address, {Raw: `accounter #${j}`}, + ]), + ], + ]); + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo1] as any); + + // change some sub-identities... + subs[2].pop(); subs[2].pop(); subs[2].push(...await helper.arrange.createAccounts([0n], superuser)); + + // ...and set them + const subsInfo2 = [[ + supers[2].address, [ + 999999n, + subs[2].map((sub, j) => [ + sub.address, {Raw: `discounter #${j}`}, + ]), + ], + ]]; + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo2] as any); + + // make sure everything else is the same + for(let i = 0; i < supers.length - 1; i++) { + // check deposit + expect(((await helper.getApi().query.identity.subsOf(supers[i].address)).toJSON() as any)[0]).to.be.equal(1000001 + i); + + const subsAccounts = await getSubIdentityAccounts(helper, supers[i].address); + // check sub-identities as account ids + expect(subsAccounts).to.include.members(subs[i].map(x => x.address)); + + for(let j = 0; j < subsAccounts; j++) { + // check sub-identities' names + expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `accounter #${j}`}); + } + } + + // check deposit + expect(((await helper.getApi().query.identity.subsOf(supers[2].address)).toJSON() as any)[0]).to.be.equal(999999); + + const subsAccounts = await getSubIdentityAccounts(helper, supers[2].address); + // check sub-identities as account ids + expect(subsAccounts).to.include.members(subs[2].map(x => x.address)); + + for(let j = 0; j < subsAccounts.length; j++) { + // check sub-identities' names + expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `discounter #${j}`}); + } + }); + + itSub('Removes sub-identities', async ({helper}) => { + const crowdSize = 3; + const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser); + const sup = crowd.pop()!; + + const subsInfo1 = [[ + sup.address, [ + 1000000n, + crowd.map((sub, j) => [ + sub.address, {Raw: `accounter #${j}`}, + ]), + ], + ]]; + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo1] as any); + + // empty sub-identities should delete the records + const subsInfo2 = [[ + sup.address, [ + 1000000n, + [], + ], + ]]; + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo2] as any); + + // check deposit + expect((await helper.getApi().query.identity.subsOf(sup.address)).toHuman()).to.be.deep.equal(['0', []]); + + for(let j = 0; j < crowd.length; j++) { + // check sub-identities' names + expect((await getSubIdentityName(helper, crowd[j].address))).to.be.null; + } + }); + + itSub('Removing identities deletes associated sub-identities', async ({helper}) => { + const crowd = await helper.arrange.createCrowd(3, 0n, superuser); + const sup = crowd.pop()!; + + // insert identity + const identities = [[sup.address, {info: {display: {Raw: 'mental'}}}]]; + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any); + + // and its sub-identities + const subsInfo = [[ + sup.address, [ + 1000000n, + crowd.map((sub, j) => [ + sub.address, {Raw: `accounter #${j}`}, + ]), + ], + ]]; + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo] as any); + + // delete top identity + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [[sup.address]]); + + // check that sub-identities are deleted + expect((await helper.getApi().query.identity.subsOf(sup.address)).toHuman()).to.be.deep.equal(['0', []]); + + for(let j = 0; j < crowd.length; j++) { + // check sub-identities' names + expect((await getSubIdentityName(helper, crowd[j].address))).to.be.null; + } + }); + }); + + after(async function() { + if(!process.env.RUN_COLLATOR_TESTS) return; + + await usingPlaygrounds(async helper => { + if(helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) return; + + const identitiesToRemove: string[] = await getIdentityAccounts(helper); + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [identitiesToRemove]); + }); + }); +}); --- /dev/null +++ b/js-packages/tests/config.ts @@ -0,0 +1,34 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import process from 'process'; + +const config = { + relayUrl: process.env.RELAY_URL || 'ws://127.0.0.1:9844', + substrateUrl: process.env.RELAY_OPAL_URL || process.env.RELAY_QUARTZ_URL || process.env.RELAY_UNIQUE_URL || process.env.RELAY_SAPPHIRE_URL || 'ws://127.0.0.1:9944', + acalaUrl: process.env.RELAY_ACALA_URL || 'ws://127.0.0.1:9946', + karuraUrl: process.env.RELAY_KARURA_URL || 'ws://127.0.0.1:9946', + moonbeamUrl: process.env.RELAY_MOONBEAM_URL || 'ws://127.0.0.1:9947', + moonriverUrl: process.env.RELAY_MOONRIVER_URL || 'ws://127.0.0.1:9947', + astarUrl: process.env.RELAY_ASTAR_URL || 'ws://127.0.0.1:9949', + shidenUrl: process.env.RELAY_SHIDEN_URL || 'ws://127.0.0.1:9949', + westmintUrl: process.env.RELAY_WESTMINT_URL || 'ws://127.0.0.1:9948', + statemineUrl: process.env.RELAY_STATEMINE_URL || 'ws://127.0.0.1:9948', + statemintUrl: process.env.RELAY_STATEMINT_URL || 'ws://127.0.0.1:9948', + polkadexUrl: process.env.RELAY_POLKADEX_URL || 'ws://127.0.0.1:9950', +}; + +export default config; --- /dev/null +++ b/js-packages/tests/confirmSponsorship.test.ts @@ -0,0 +1,253 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js'; +import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js'; + +async function setSponsorHelper(collection: any, signer: IKeyringPair, sponsorAddress: string) { + await collection.setSponsor(signer, sponsorAddress); + const raw = (await collection.getData())?.raw; + expect(raw.sponsorship.Unconfirmed).to.be.equal(sponsorAddress); +} + +async function confirmSponsorHelper(collection: any, signer: IKeyringPair) { + await collection.confirmSponsorship(signer); + const raw = (await collection.getData())?.raw; + expect(raw.sponsorship.Confirmed).to.be.equal(signer.address); +} + +describe('integration test: ext. confirmSponsorship():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + let zeroBalance: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie, zeroBalance] = await helper.arrange.createAccounts([100n, 100n, 100n, 0n], donor); + }); + }); + + itSub('Confirm collection sponsorship', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await setSponsorHelper(collection, alice, bob.address); + await confirmSponsorHelper(collection, bob); + }); + + itSub('Add sponsor to a collection after the same sponsor was already added and confirmed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await setSponsorHelper(collection, alice, bob.address); + await confirmSponsorHelper(collection, bob); + await setSponsorHelper(collection, alice, bob.address); + }); + itSub('Add new sponsor to a collection after another sponsor was already added and confirmed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await setSponsorHelper(collection, alice, bob.address); + await confirmSponsorHelper(collection, bob); + await setSponsorHelper(collection, alice, charlie.address); + }); + + itSub('NFT: Transfer fees are paid by the sponsor after confirmation', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); + const token = await collection.mintToken(alice, {Substrate: zeroBalance.address}); + await token.transfer(zeroBalance, {Substrate: alice.address}); + const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); + expect(bobBalanceAfter < bobBalanceBefore).to.be.true; + }); + + itSub('Fungible: Transfer fees are paid by the sponsor after confirmation', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); + await collection.mint(alice, 100n, {Substrate: zeroBalance.address}); + await collection.transfer(zeroBalance, {Substrate: alice.address}, 1n); + const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); + expect(bobBalanceAfter < bobBalanceBefore).to.be.true; + }); + + itSub.ifWithPallets('ReFungible: Transfer fees are paid by the sponsor after confirmation', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); + const token = await collection.mintToken(alice, 100n, {Substrate: zeroBalance.address}); + await token.transfer(zeroBalance, {Substrate: alice.address}, 1n); + const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); + expect(bobBalanceAfter < bobBalanceBefore).to.be.true; + }); + + itSub('CreateItem fees are paid by the sponsor after confirmation', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await collection.addToAllowList(alice, {Substrate: zeroBalance.address}); + + const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); + await collection.mintToken(zeroBalance, {Substrate: zeroBalance.address}); + const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); + + expect(bobBalanceAfter < bobBalanceBefore).to.be.true; + }); + + itSub('NFT: Sponsoring of transfers is rate limited', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: { + sponsorTransferTimeout: 1000, + }}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + + const token = await collection.mintToken(alice, {Substrate: alice.address}); + await token.transfer(alice, {Substrate: zeroBalance.address}); + const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); + + const transferTx = () => token.transfer(zeroBalance, {Substrate: alice.address}); + await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees'); + const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); + + expect(bobBalanceAfter === bobBalanceBefore).to.be.true; + }); + + itSub('Fungible: Sponsoring is rate limited', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: { + sponsorTransferTimeout: 1000, + }}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + + await collection.mint(alice, 100n, {Substrate: zeroBalance.address}); + await collection.transfer(zeroBalance, {Substrate: zeroBalance.address}, 1n); + const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); + + const transferTx = () => collection.transfer(zeroBalance, {Substrate: zeroBalance.address}); + await expect(transferTx()).to.be.rejected; + const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); + + expect(bobBalanceAfter === bobBalanceBefore).to.be.true; + }); + + itSub.ifWithPallets('ReFungible: Sponsoring is rate limited', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: { + sponsorTransferTimeout: 1000, + }}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + + const token = await collection.mintToken(alice, 100n, {Substrate: zeroBalance.address}); + await token.transfer(zeroBalance, {Substrate: alice.address}); + + const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); + const transferTx = () => token.transfer(zeroBalance, {Substrate: alice.address}); + await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees'); + const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); + + expect(bobBalanceAfter === bobBalanceBefore).to.be.true; + }); + + itSub('NFT: Sponsoring of createItem is rate limited', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: { + sponsoredDataRateLimit: {blocks: 1000}, + sponsorTransferTimeout: 1000, + }}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + await collection.setPermissions(alice, {mintMode: true, access: 'AllowList'}); + await collection.addToAllowList(alice, {Substrate: zeroBalance.address}); + + await collection.mintToken(zeroBalance, {Substrate: zeroBalance.address}); + + const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); + const mintTx = () => collection.mintToken(zeroBalance, {Substrate: zeroBalance.address}); + await expect(mintTx()).to.be.rejectedWith('Inability to pay some fees'); + const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); + + expect(bobBalanceAfter === bobBalanceBefore).to.be.true; + }); +}); + +describe('(!negative test!) integration test: ext. confirmSponsorship():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + let ownerZeroBalance: IKeyringPair; + let senderZeroBalance: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie, ownerZeroBalance, senderZeroBalance] = await helper.arrange.createAccounts([100n, 100n, 100n, 0n, 0n], donor); + }); + }); + + itSub('(!negative test!) Confirm sponsorship for a collection that never existed', async ({helper}) => { + const collectionId = NON_EXISTENT_COLLECTION_ID; + const confirmSponsorshipTx = () => helper.collection.confirmSponsorship(bob, collectionId); + await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('(!negative test!) Confirm sponsorship using a non-sponsor address', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.setSponsor(alice, bob.address); + const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie); + await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); + }); + + itSub('(!negative test!) Confirm sponsorship using owner address', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.setSponsor(alice, bob.address); + const confirmSponsorshipTx = () => collection.confirmSponsorship(alice); + await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); + }); + + itSub('(!negative test!) Confirm sponsorship by collection admin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.setSponsor(alice, bob.address); + await collection.addAdmin(alice, {Substrate: charlie.address}); + const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie); + await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); + }); + + itSub('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie); + await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); + }); + + itSub('(!negative test!) Confirm sponsorship in a collection that was destroyed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.burn(alice); + const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie); + await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('(!negative test!) Transfer fees are not paid by the sponsor if the transfer failed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + const token = await collection.mintToken(alice, {Substrate: ownerZeroBalance.address}); + const sponsorBalanceBefore = await helper.balance.getSubstrate(bob.address); + const transferTx = () => token.transfer(senderZeroBalance, {Substrate: alice.address}); + await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees'); + const sponsorBalanceAfter = await helper.balance.getSubstrate(bob.address); + expect(sponsorBalanceAfter).to.equal(sponsorBalanceBefore); + }); +}); --- /dev/null +++ b/js-packages/tests/connection.test.ts @@ -0,0 +1,32 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {itSub, expect, usingPlaygrounds} from './util/index.js'; + +describe('Connection smoke test', () => { + itSub('Connection can be established', async ({helper}) => { + const health = (await helper.callRpc('api.rpc.system.health')).toJSON(); + expect(health).to.be.not.empty; + }); + + it('Cannot connect to 255.255.255.255', async () => { + await expect((async () => { + await usingPlaygrounds(async helper => { + await helper.callRpc('api.rpc.system.health'); + }, 'ws://255.255.255.255:9944'); + })()).to.be.eventually.rejected; + }); +}); --- /dev/null +++ b/js-packages/tests/createCollection.test.ts @@ -0,0 +1,194 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js'; +import {CollectionFlag} from '@unique/playgrounds/types.js'; +import type {ICollectionCreationOptions, IProperty} from '@unique/playgrounds/types.js'; +import {UniqueHelper} from '@unique/playgrounds/unique.js'; + +async function mintCollectionHelper(helper: UniqueHelper, signer: IKeyringPair, options: ICollectionCreationOptions, type?: 'nft' | 'fungible' | 'refungible') { + let collection; + if(type === 'nft') { + collection = await helper.nft.mintCollection(signer, options); + } else if(type === 'fungible') { + collection = await helper.ft.mintCollection(signer, options, 0); + } else { + collection = await helper.rft.mintCollection(signer, options); + } + const data = await collection.getData(); + expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(signer.address)); + expect(data?.name).to.be.equal(options.name); + expect(data?.description).to.be.equal(options.description); + expect(data?.raw.tokenPrefix).to.be.equal(options.tokenPrefix); + if(options.properties) { + expect(data?.raw.properties).to.be.deep.equal(options.properties); + } + if(options.adminList) { + expect(data?.admins).to.be.deep.equal(options.adminList); + } + + if(options.flags) { + if((options.flags[0] & 64) != 0) + expect(data?.raw.flags.erc721metadata).to.be.true; + if((options.flags[0] & 128) != 0) + expect(data?.raw.flags.foreign).to.be.false; + } + + if(options.tokenPropertyPermissions) { + expect(data?.raw.tokenPropertyPermissions).to.be.deep.equal(options.tokenPropertyPermissions); + } + + return collection; +} + +describe('integration test: ext. createCollection():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + itSub('Create new NFT collection', async ({helper}) => { + await mintCollectionHelper(helper, alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 'nft'); + }); + itSub('Create new NFT collection whith collection_name of maximum length (64 bytes)', async ({helper}) => { + await mintCollectionHelper(helper, alice, {name: 'A'.repeat(64), description: 'descr', tokenPrefix: 'COL'}, 'nft'); + }); + itSub('Create new NFT collection whith collection_description of maximum length (256 bytes)', async ({helper}) => { + await mintCollectionHelper(helper, alice, {name: 'name', description: 'A'.repeat(256), tokenPrefix: 'COL'}, 'nft'); + }); + itSub('Create new NFT collection whith token_prefix of maximum length (16 bytes)', async ({helper}) => { + await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(16)}, 'nft'); + }); + + itSub('Create new Fungible collection', async ({helper}) => { + await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible'); + }); + + itSub.ifWithPallets('Create new ReFungible collection', [Pallets.ReFungible], async ({helper}) => { + await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'refungible'); + }); + + itSub('create new collection with properties', async ({helper}) => { + await mintCollectionHelper(helper, alice, { + name: 'name', description: 'descr', tokenPrefix: 'COL', + properties: [{key: 'key1', value: 'val1'}], + tokenPropertyPermissions: [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}], + }, 'nft'); + }); + + itSub('create new collection with admin', async ({helper}) => { + await mintCollectionHelper(helper, alice, { + name: 'name', description: 'descr', tokenPrefix: 'COL', + adminList: [{Substrate: bob.address}], + }, 'nft'); + }); + + itSub('create new collection with flags', async ({helper}) => { + await mintCollectionHelper(helper, alice, { + name: 'name', description: 'descr', tokenPrefix: 'COL', + flags: [CollectionFlag.Erc721metadata], + }, 'nft'); + + // User can not set Foreign flag itself + + await expect(mintCollectionHelper(helper, alice, { + name: 'name', description: 'descr', tokenPrefix: 'COL', + flags: [CollectionFlag.Foreign], + }, 'nft')).to.be.rejectedWith(/common.NoPermission/); + + await expect(mintCollectionHelper(helper, alice, { + name: 'name', description: 'descr', tokenPrefix: 'COL', + flags: [CollectionFlag.Erc721metadata, CollectionFlag.Foreign], + }, 'nft')).to.be.rejectedWith(/common.NoPermission/); + }); + + itSub('Create new collection with extra fields', async ({helper}) => { + const collection = await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible'); + await collection.setPermissions(alice, {access: 'AllowList'}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 3}); + const data = await collection.getData(); + const limits = await collection.getEffectiveLimits(); + const raw = data?.raw; + + expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address)); + expect(data?.name).to.be.equal('name'); + expect(data?.description).to.be.equal('descr'); + expect(raw.permissions.access).to.be.equal('AllowList'); + expect(raw.mode).to.be.deep.equal({Fungible: '0'}); + expect(limits.accountTokenOwnershipLimit).to.be.equal(3); + }); + + itSub('New collection is not external', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}); + const data = await collection.getData(); + expect(data?.raw.readOnly).to.be.false; + }); +}); + +describe('(!negative test!) integration test: ext. createCollection():', () => { + let alice: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([100n], donor); + }); + }); + + itSub('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async ({helper}) => { + const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'A'.repeat(65), description: 'descr', tokenPrefix: 'COL'}); + await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error'); + }); + itSub('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async ({helper}) => { + const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'A'.repeat(257), tokenPrefix: 'COL'}); + await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error'); + }); + itSub('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async ({helper}) => { + const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(17)}); + await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error'); + }); + + itSub('(!negative test!) fails when bad limits are set', async ({helper}) => { + const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', limits: {tokenLimit: 0}}); + await expect(mintCollectionTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/); + }); + + itSub('(!negative test!) create collection with incorrect property limit (64 elements)', async ({helper}) => { + const props: IProperty[] = []; + + for(let i = 0; i < 65; i++) { + props.push({key: `key${i}`, value: `value${i}`}); + } + const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props}); + await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error'); + }); + + itSub('(!negative test!) create collection with incorrect property limit (40 kb)', async ({helper}) => { + const props: IProperty[] = []; + + for(let i = 0; i < 32; i++) { + props.push({key: `key${i}`.repeat(80), value: `value${i}`.repeat(80)}); + } + + const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props}); + await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error'); + }); +}); --- /dev/null +++ b/js-packages/tests/createItem.test.ts @@ -0,0 +1,272 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js'; +import type {IProperty, ICrossAccountId} from '@unique/playgrounds/types.js'; +import {UniqueHelper} from '@unique/playgrounds/unique.js'; + +async function mintTokenHelper(helper: UniqueHelper, collection: any, signer: IKeyringPair, owner: ICrossAccountId, type: 'nft' | 'fungible' | 'refungible'='nft', properties?: IProperty[]) { + let token; + const itemCountBefore = await helper.collection.getLastTokenId(collection.collectionId); + const itemBalanceBefore = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt(); + if(type === 'nft') { + token = await collection.mintToken(signer, owner, properties); + } else if(type === 'fungible') { + await collection.mint(signer, 10n, owner); + } else { + token = await collection.mintToken(signer, 100n, owner, properties); + } + + const itemCountAfter = await helper.collection.getLastTokenId(collection.collectionId); + const itemBalanceAfter = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt(); + + if(type === 'fungible') { + expect(itemBalanceAfter - itemBalanceBefore).to.be.equal(10n); + } else { + expect(itemCountAfter).to.be.equal(itemCountBefore + 1); + } + + return token; +} + + +describe('integration test: ext. ():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('Create new item in NFT collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}); + }); + itSub('Create new item in Fungible collection', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}, 'fungible'); + }); + itSub('Check events on create new item in Fungible collection', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); + const to = {Substrate: alice.address}; + { + const createData = {fungible: {value: 100}}; + const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]); + const result = helper.util.extractTokensFromCreationResult(events); + expect(result.tokens[0].amount).to.be.equal(100n); + expect(result.tokens[0].collectionId).to.be.equal(collectionId); + expect(result.tokens[0].owner).to.be.deep.equal(to); + } + { + const createData = {fungible: {value: 50}}; + const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]); + const result = helper.util.extractTokensFromCreationResult(events); + expect(result.tokens[0].amount).to.be.equal(50n); + expect(result.tokens[0].collectionId).to.be.equal(collectionId); + expect(result.tokens[0].owner).to.be.deep.equal(to); + } + }); + itSub.ifWithPallets('Create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}, 'refungible'); + }); + itSub('Create new item in NFT collection with collection admin permissions', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}); + }); + itSub('Create new item in Fungible collection with collection admin permissions', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await collection.addAdmin(alice, {Substrate: bob.address}); + await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}, 'fungible'); + }); + itSub.ifWithPallets('Create new item in ReFungible collection with collection admin permissions', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}, 'refungible'); + }); + + itSub('Set property Admin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', + properties: [{key: 'k', value: 'v'}], + tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: false, mutable: true, collectionAdmin: true}}], + }); + await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]); + }); + + itSub('Set property AdminConst', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', + properties: [{key: 'k', value: 'v'}], + tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}}], + }); + await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]); + }); + + itSub('Set property itemOwnerOrAdmin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', + properties: [{key: 'k', value: 'v'}], + tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}], + }); + await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]); + }); + + itSub('Check total pieces of Fungible token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + const amount = 10n; + await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'fungible'); + { + const totalPieces = await collection.getTotalPieces(); + expect(totalPieces).to.be.equal(amount); + } + await collection.transfer(bob, {Substrate: alice.address}, 1n); + { + const totalPieces = await collection.getTotalPieces(); + expect(totalPieces).to.be.equal(amount); + } + }); + + itSub('Check total pieces of NFT token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const amount = 1n; + const token = await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}); + { + const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]); + expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount); + } + await token.transfer(bob, {Substrate: alice.address}); + { + const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]); + expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount); + } + }); + + itSub.ifWithPallets('Check total pieces of ReFungible token', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const amount = 100n; + const token = await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'refungible'); + { + const totalPieces = await token.getTotalPieces(); + expect(totalPieces).to.be.equal(amount); + } + await token.transfer(bob, {Substrate: alice.address}, 60n); + { + const totalPieces = await token.getTotalPieces(); + expect(totalPieces).to.be.equal(amount); + } + }); +}); + +describe('Negative integration test: ext. createItem():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('Regular user cannot create new item in NFT collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const mintTx = () => collection.mintToken(bob, {Substrate: bob.address}); + await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); + }); + itSub('Regular user cannot create new item in Fungible collection', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + const mintTx = () => collection.mint(bob, 10n, {Substrate: bob.address}); + await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); + }); + itSub.ifWithPallets('Regular user cannot create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const mintTx = () => collection.mintToken(bob, 100n, {Substrate: bob.address}); + await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); + }); + + itSub('No editing rights', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', + tokenPropertyPermissions: [{key: 'k', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}], + }); + const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]); + await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('User doesnt have editing rights', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', + tokenPropertyPermissions: [{key: 'k', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}], + }); + const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]); + await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Adding property without access rights', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]); + await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Adding more than 64 prps', async ({helper}) => { + const props: IProperty[] = []; + + for(let i = 0; i < 65; i++) { + props.push({key: `key${i}`, value: `value${i}`}); + } + + + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, props); + await expect(mintTx()).to.be.rejectedWith('Verification Error'); + }); + + itSub('Trying to add bigger property than allowed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'k1', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}, + {key: 'k2', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}, + ], + }); + const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [ + {key: 'k1', value: 'vvvvvv'.repeat(5000)}, + {key: 'k2', value: 'vvv'.repeat(5000)}, + ]); + await expect(mintTx()).to.be.rejectedWith(/common\.NoSpaceForProperty/); + }); + + itSub('Check total pieces for invalid Fungible token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + const invalidTokenId = 1_000_000; + expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true; + expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n); + }); + + itSub('Check total pieces for invalid NFT token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const invalidTokenId = 1_000_000; + expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true; + expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n); + }); + + itSub.ifWithPallets('Check total pieces for invalid Refungible token', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const invalidTokenId = 1_000_000; + expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true; + expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n); + }); +}); --- /dev/null +++ b/js-packages/tests/createMultipleItems.test.ts @@ -0,0 +1,374 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, Pallets, itSub} from './util/index.js'; + +describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => { + let alice: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([100n], donor); + }); + }); + + itSub('Create 0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'data', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}}, + ], + }); + const args = [ + {properties: [{key: 'data', value: '1'}]}, + {properties: [{key: 'data', value: '2'}]}, + {properties: [{key: 'data', value: '3'}]}, + ]; + const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + for(const [i, token] of tokens.entries()) { + const tokenData = await token.getData(); + expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)}); + expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value); + } + }); + + itSub('Create 0x01, 0x02, 0x03 items in active Fungible collection and verify tokens data in chain', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + }); + const args = [ + {value: 1n}, + {value: 2n}, + {value: 3n}, + ]; + await helper.ft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, args, {Substrate: alice.address}); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(6n); + }); + + itSub.ifWithPallets('Create 0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + }); + const args = [ + {pieces: 1n}, + {pieces: 2n}, + {pieces: 3n}, + ]; + const tokens = await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + + for(const [i, token] of tokens.entries()) { + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(BigInt(i + 1)); + } + }); + + itSub('Can mint amount of items equals to collection limits', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + limits: { + tokenLimit: 2, + }, + }); + const args = [{}, {}]; + await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + }); + + itSub('Create 0x31, 0x32, 0x33 items in active NFT with property Admin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'data', permission: {tokenOwner: false, mutable: true, collectionAdmin: true}}, + ], + }); + const args = [ + {properties: [{key: 'data', value: '1'}]}, + {properties: [{key: 'data', value: '2'}]}, + {properties: [{key: 'data', value: '3'}]}, + ]; + const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + for(const [i, token] of tokens.entries()) { + const tokenData = await token.getData(); + expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)}); + expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value); + } + }); + + itSub('Create 0x31, 0x32, 0x33 items in active NFT with property AdminConst', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'data', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}}, + ], + }); + const args = [ + {properties: [{key: 'data', value: '1'}]}, + {properties: [{key: 'data', value: '2'}]}, + {properties: [{key: 'data', value: '3'}]}, + ]; + const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + for(const [i, token] of tokens.entries()) { + const tokenData = await token.getData(); + expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)}); + expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value); + } + }); + + itSub('Create 0x31, 0x32, 0x33 items in active NFT with property itemOwnerOrAdmin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, + ], + }); + const args = [ + {properties: [{key: 'data', value: '1'}]}, + {properties: [{key: 'data', value: '2'}]}, + {properties: [{key: 'data', value: '3'}]}, + ]; + const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + for(const [i, token] of tokens.entries()) { + const tokenData = await token.getData(); + expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)}); + expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value); + } + }); +}); + +describe('Negative Integration Test createMultipleItems(collection_id, owner, items_data):', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('Regular user cannot create items in active NFT collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + }); + const args = [ + {}, + {}, + ]; + const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args); + await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); + }); + + itSub('Regular user cannot create items in active Fungible collection', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + }); + const args = [ + {value: 1n}, + {value: 2n}, + {value: 3n}, + ]; + const mintTx = () => helper.ft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, args, {Substrate: alice.address}); + await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); + }); + + itSub.ifWithPallets('Regular user cannot create items in active ReFungible collection', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + }); + const args = [ + {pieces: 1n}, + {pieces: 1n}, + {pieces: 1n}, + ]; + const mintTx = () => helper.rft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args); + await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); + }); + + itSub('Create token in not existing collection', async ({helper}) => { + const collectionId = 1_000_000; + const args = [ + {}, + {}, + ]; + const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(bob, collectionId, {Substrate: alice.address}, args); + await expect(mintTx()).to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('Create NFTs that has reached the maximum data limit', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, + ], + }); + const args = [ + {properties: [{key: 'data', value: 'A'.repeat(32769)}]}, + {properties: [{key: 'data', value: 'B'.repeat(32769)}]}, + {properties: [{key: 'data', value: 'C'.repeat(32769)}]}, + ]; + const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + await expect(mintTx()).to.be.rejectedWith('Verification Error'); + }); + + itSub.ifWithPallets('Create Refungible tokens that has reached the maximum data limit', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, + ], + }); + const args = [ + {pieces: 10n, properties: [{key: 'data', value: 'A'.repeat(32769)}]}, + {pieces: 10n, properties: [{key: 'data', value: 'B'.repeat(32769)}]}, + {pieces: 10n, properties: [{key: 'data', value: 'C'.repeat(32769)}]}, + ]; + const mintTx = () => helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + await expect(mintTx()).to.be.rejectedWith('Verification Error'); + }); + + itSub.ifWithPallets('Create tokens with different types', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + }); + + const types = ['NFT', 'Fungible', 'ReFungible']; + await expect(helper.executeExtrinsic( + alice, + 'api.tx.unique.createMultipleItems', + [collectionId, {Substrate: alice.address}, types], + )).to.be.rejectedWith(/nonfungible\.NotNonfungibleDataUsedToMintFungibleCollectionToken/); + }); + + itSub('Create tokens with different data limits <> maximum data limit', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, + ], + }); + const args = [ + {properties: [{key: 'data', value: 'A'}]}, + {properties: [{key: 'data', value: 'B'.repeat(32769)}]}, + ]; + const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + await expect(mintTx()).to.be.rejectedWith('Verification Error'); + }); + + itSub('Fails when minting tokens exceeds collectionLimits amount', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, + ], + limits: { + tokenLimit: 1, + }, + }); + const args = [ + {}, + {}, + ]; + const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + await expect(mintTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/); + }); + + itSub('User doesnt have editing rights', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'data', permission: {tokenOwner: false, mutable: true, collectionAdmin: false}}, + ], + }); + const args = [ + {properties: [{key: 'data', value: 'A'}]}, + ]; + const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Adding property without access rights', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + properties: [ + { + key: 'data', + value: 'v', + }, + ], + }); + const args = [ + {properties: [{key: 'data', value: 'A'}]}, + ]; + const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Adding more than 64 prps', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + }); + const prps = []; + + for(let i = 0; i < 65; i++) { + prps.push({key: `key${i}`, value: `value${i}`}); + } + + const args = [ + {properties: prps}, + {properties: prps}, + {properties: prps}, + ]; + + const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); + await expect(mintTx()).to.be.rejectedWith('Verification Error'); + }); +}); --- /dev/null +++ b/js-packages/tests/createMultipleItemsEx.test.ts @@ -0,0 +1,442 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, expect, Pallets, itSub} from './util/index.js'; +import type {IProperty} from '@unique/playgrounds/types.js'; + +describe('Integration Test: createMultipleItemsEx', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('can initialize multiple NFT with different owners', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + }); + const args = [ + { + owner: {Substrate: alice.address}, + }, + { + owner: {Substrate: bob.address}, + }, + { + owner: {Substrate: charlie.address}, + }, + ]; + + const tokens = await collection.mintMultipleTokens(alice, args); + for(const [i, token] of tokens.entries()) { + expect(await token.getOwner()).to.be.deep.equal(args[i].owner); + } + }); + + itSub('createMultipleItemsEx with property Admin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + { + key: 'k', + permission: { + mutable: true, + collectionAdmin: true, + tokenOwner: false, + }, + }, + ], + }); + + const args = [ + { + owner: {Substrate: alice.address}, + properties: [{key: 'k', value: 'v1'}], + }, + { + owner: {Substrate: bob.address}, + properties: [{key: 'k', value: 'v2'}], + }, + { + owner: {Substrate: charlie.address}, + properties: [{key: 'k', value: 'v3'}], + }, + ]; + + const tokens = await collection.mintMultipleTokens(alice, args); + for(const [i, token] of tokens.entries()) { + expect(await token.getOwner()).to.be.deep.equal(args[i].owner); + expect(await token.getData()).to.not.be.empty; + } + }); + + itSub('createMultipleItemsEx with property AdminConst', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + { + key: 'k', + permission: { + mutable: false, + collectionAdmin: true, + tokenOwner: false, + }, + }, + ], + }); + + const args = [ + { + owner: {Substrate: alice.address}, + properties: [{key: 'k', value: 'v1'}], + }, + { + owner: {Substrate: bob.address}, + properties: [{key: 'k', value: 'v2'}], + }, + { + owner: {Substrate: charlie.address}, + properties: [{key: 'k', value: 'v3'}], + }, + ]; + + const tokens = await collection.mintMultipleTokens(alice, args); + for(const [i, token] of tokens.entries()) { + expect(await token.getOwner()).to.be.deep.equal(args[i].owner); + expect(await token.getData()).to.not.be.empty; + } + }); + + itSub('createMultipleItemsEx with property itemOwnerOrAdmin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + { + key: 'k', + permission: { + mutable: false, + collectionAdmin: true, + tokenOwner: true, + }, + }, + ], + }); + + const args = [ + { + owner: {Substrate: alice.address}, + properties: [{key: 'k', value: 'v1'}], + }, + { + owner: {Substrate: bob.address}, + properties: [{key: 'k', value: 'v2'}], + }, + { + owner: {Substrate: charlie.address}, + properties: [{key: 'k', value: 'v3'}], + }, + ]; + + const tokens = await collection.mintMultipleTokens(alice, args); + for(const [i, token] of tokens.entries()) { + expect(await token.getOwner()).to.be.deep.equal(args[i].owner); + expect(await token.getData()).to.not.be.empty; + } + }); + + itSub('can initialize fungible with multiple owners', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + }, 0); + + await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx',[collection.collectionId, { + Fungible: new Map([ + [JSON.stringify({Substrate: alice.address}), 50], + [JSON.stringify({Substrate: bob.address}), 100], + ]), + }], true); + + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(50n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(100n); + }); + + itSub.ifWithPallets('can initialize an RFT with multiple owners', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'k', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}}, + ], + }); + + await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx', [collection.collectionId, { + RefungibleMultipleOwners: { + users: new Map([ + [JSON.stringify({Substrate: alice.address}), 1], + [JSON.stringify({Substrate: bob.address}), 2], + ]), + properties: [ + {key: 'k', value: 'v'}, + ], + }, + }], true); + const tokenId = await collection.getLastTokenId(); + expect(tokenId).to.be.equal(1); + expect(await collection.getTokenBalance(1, {Substrate: alice.address})).to.be.equal(1n); + expect(await collection.getTokenBalance(1, {Substrate: bob.address})).to.be.equal(2n); + }); + + itSub.ifWithPallets('can initialize multiple RFTs with the same owner', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + {key: 'k', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}}, + ], + }); + + await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx', [collection.collectionId, { + RefungibleMultipleItems: [ + { + user: {Substrate: alice.address}, pieces: 1, + properties: [ + {key: 'k', value: 'v1'}, + ], + }, + { + user: {Substrate: alice.address}, pieces: 3, + properties: [ + {key: 'k', value: 'v2'}, + ], + }, + ], + }], true); + + expect(await collection.getLastTokenId()).to.be.equal(2); + expect(await collection.getTokenBalance(1, {Substrate: alice.address})).to.be.equal(1n); + expect(await collection.getTokenBalance(2, {Substrate: alice.address})).to.be.equal(3n); + + const tokenData1 = await helper.rft.getToken(collection.collectionId, 1); + expect(tokenData1).to.not.be.null; + expect(tokenData1?.properties[0]).to.be.deep.equal({key: 'k', value: 'v1'}); + + const tokenData2 = await helper.rft.getToken(collection.collectionId, 2); + expect(tokenData2).to.not.be.null; + expect(tokenData2?.properties[0]).to.be.deep.equal({key: 'k', value: 'v2'}); + }); +}); + +describe('Negative test: createMultipleItemsEx', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('No editing rights', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + { + key: 'k', + permission: { + mutable: true, + collectionAdmin: false, + tokenOwner: false, + }, + }, + ], + }); + + const args = [ + { + owner: {Substrate: alice.address}, + properties: [{key: 'k', value: 'v1'}], + }, + { + owner: {Substrate: bob.address}, + properties: [{key: 'k', value: 'v2'}], + }, + { + owner: {Substrate: charlie.address}, + properties: [{key: 'k', value: 'v3'}], + }, + ]; + + await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('User doesnt have editing rights', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + { + key: 'k', + permission: { + mutable: false, + collectionAdmin: false, + tokenOwner: false, + }, + }, + ], + }); + + const args = [ + { + owner: {Substrate: alice.address}, + properties: [{key: 'k', value: 'v1'}], + }, + { + owner: {Substrate: bob.address}, + properties: [{key: 'k', value: 'v2'}], + }, + { + owner: {Substrate: charlie.address}, + properties: [{key: 'k', value: 'v3'}], + }, + ]; + + await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Adding property without access rights', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + }); + + const args = [ + { + owner: {Substrate: alice.address}, + properties: [{key: 'k', value: 'v1'}], + }, + { + owner: {Substrate: bob.address}, + properties: [{key: 'k', value: 'v2'}], + }, + { + owner: {Substrate: charlie.address}, + properties: [{key: 'k', value: 'v3'}], + }, + ]; + + await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Adding more than 64 properties', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + { + key: 'k', + permission: { + mutable: true, + collectionAdmin: true, + tokenOwner: true, + }, + }, + ], + }); + + const properties: IProperty[] = []; + + for(let i = 0; i < 65; i++) { + properties.push({key: `k${i}`, value: `v${i}`}); + } + + const args = [ + { + owner: {Substrate: alice.address}, + properties: properties, + }, + { + owner: {Substrate: bob.address}, + properties: properties, + }, + { + owner: {Substrate: charlie.address}, + properties: properties, + }, + ]; + + await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith('Verification Error'); + }); + + itSub('Trying to add bigger property than allowed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'name', + description: 'descr', + tokenPrefix: 'COL', + tokenPropertyPermissions: [ + { + key: 'k', + permission: { + mutable: true, + collectionAdmin: true, + tokenOwner: true, + }, + }, + ], + }); + + const args = [ + { + owner: {Substrate: alice.address}, + properties: [{key: 'k', value: 'A'.repeat(32769)}], + }, + { + owner: {Substrate: bob.address}, + properties: [{key: 'k', value: 'A'.repeat(32769)}], + }, + { + owner: {Substrate: charlie.address}, + properties: [{key: 'k', value: 'A'.repeat(32769)}], + }, + ]; + + await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith('Verification Error'); + }); +}); --- /dev/null +++ b/js-packages/tests/creditFeesToTreasury.seqtest.ts @@ -0,0 +1,166 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {ApiPromise} from '@polkadot/api'; +import {usingPlaygrounds, expect, itSub} from './util/index.js'; +import type {u32} from '@polkadot/types-codec'; + +const TREASURY = '5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z'; +const saneMinimumFee = 0.05; +const saneMaximumFee = 0.5; +const createCollectionDeposit = 100; + +// Skip the inflation block pauses if the block is close to inflation block +// until the inflation happens +/*eslint no-async-promise-executor: "off"*/ +function skipInflationBlock(api: ApiPromise): Promise { + const promise = new Promise(async (resolve) => { + const inflationBlockInterval = api.consts.inflation.inflationBlockInterval as u32; + const blockInterval = inflationBlockInterval.toNumber(); + const unsubscribe = await api.rpc.chain.subscribeNewHeads(head => { + const currentBlock = head.number.toNumber(); + if(currentBlock % blockInterval < blockInterval - 10) { + unsubscribe(); + resolve(); + } else { + console.log(`Skipping inflation block, current block: ${currentBlock}`); + } + }); + }); + + return promise; +} + +describe('integration test: Fees must be credited to Treasury:', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('Total issuance does not change', async ({helper}) => { + const api = helper.getApi(); + await skipInflationBlock(api); + await helper.wait.newBlocks(1); + + const totalBefore = (await helper.callRpc('api.query.balances.totalIssuance', [])).toBigInt(); + + await helper.balance.transferToSubstrate(alice, bob.address, 1n); + + const totalAfter = (await helper.callRpc('api.query.balances.totalIssuance', [])).toBigInt(); + + expect(totalAfter).to.be.equal(totalBefore); + }); + + itSub('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async ({helper}) => { + await skipInflationBlock(helper.getApi()); + await helper.wait.newBlocks(1); + + const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY); + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); + + const amount = 1n; + await helper.balance.transferToSubstrate(alice, bob.address, amount); + + const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY); + const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); + + const fee = aliceBalanceBefore - aliceBalanceAfter - amount; + const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore; + + expect(treasuryIncrease).to.be.equal(fee); + }); + + itSub('Treasury balance increased by failed tx fee', async ({helper}) => { + const api = helper.getApi(); + await helper.wait.newBlocks(1); + + const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY); + const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); + + await expect(helper.signTransaction(bob, api.tx.balances.forceSetBalance(alice.address, 0))).to.be.rejected; + + const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY); + const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); + + const fee = bobBalanceBefore - bobBalanceAfter; + const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore; + + expect(treasuryIncrease).to.be.equal(fee); + }); + + itSub('NFT Transactions also send fees to Treasury', async ({helper}) => { + await skipInflationBlock(helper.getApi()); + await helper.wait.newBlocks(1); + + const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY); + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); + + await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + + const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY); + const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); + const fee = aliceBalanceBefore - aliceBalanceAfter; + const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore; + + expect(treasuryIncrease).to.be.equal(fee); + }); + + itSub('Fees are sane', async ({helper}) => { + const unique = helper.balance.getOneTokenNominal(); + await skipInflationBlock(helper.getApi()); + await helper.wait.newBlocks(1); + + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); + + await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + + const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); + const fee = aliceBalanceBefore - aliceBalanceAfter; + + expect(fee / unique < BigInt(Math.ceil(saneMaximumFee + createCollectionDeposit))).to.be.true; + expect(fee / unique < BigInt(Math.ceil(saneMinimumFee + createCollectionDeposit))).to.be.true; + }); + + itSub('NFT Transfer fee is close to 0.1 Unique', async ({helper}) => { + await skipInflationBlock(helper.getApi()); + await helper.wait.newBlocks(1); + + const collection = await helper.nft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + }); + // const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); + const token = await collection.mintToken(alice, {Substrate: alice.address}); + + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); + await token.transfer(alice, {Substrate: bob.address}); + const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); + + const fee = Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal()); + const expectedTransferFee = 0.1; + // fee drifts because of NextFeeMultiplier + const tolerance = 0.001; + + expect(Math.abs(fee - expectedTransferFee)).to.be.lessThan(tolerance); + }); +}); --- /dev/null +++ b/js-packages/tests/destroyCollection.test.ts @@ -0,0 +1,120 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, expect, usingPlaygrounds, Pallets} from './util/index.js'; + +describe('integration test: ext. destroyCollection():', () => { + let alice: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([100n], donor); + }); + }); + + itSub('NFT collection can be destroyed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + }); + await collection.burn(alice); + expect(await collection.getData()).to.be.null; + }); + itSub('Fungible collection can be destroyed', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + }, 0); + await collection.burn(alice); + expect(await collection.getData()).to.be.null; + }); + itSub.ifWithPallets('ReFungible collection can be destroyed', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + }); + await collection.burn(alice); + expect(await collection.getData()).to.be.null; + }); +}); + +describe('(!negative test!) integration test: ext. destroyCollection():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('(!negative test!) Destroy a collection that never existed', async ({helper}) => { + const collectionId = 1_000_000; + await expect(helper.collection.burn(alice, collectionId)).to.be.rejectedWith(/common\.CollectionNotFound/); + }); + itSub('(!negative test!) Destroy a collection that has already been destroyed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + }); + await collection.burn(alice); + await expect(collection.burn(alice)).to.be.rejectedWith(/common\.CollectionNotFound/); + }); + itSub('(!negative test!) Destroy a collection using non-owner account', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + }); + await expect(collection.burn(bob)).to.be.rejectedWith(/common\.NoPermission/); + }); + itSub('(!negative test!) Destroy a collection using collection admin account', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + }); + await collection.addAdmin(alice, {Substrate: bob.address}); + await expect(collection.burn(bob)).to.be.rejectedWith(/common\.NoPermission/); + }); + itSub('fails when OwnerCanDestroy == false', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + limits: { + ownerCanDestroy: false, + }, + }); + await expect(collection.burn(alice)).to.be.rejectedWith(/common\.NoPermission/); + }); + itSub('fails when a collection still has a token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + }); + await collection.mintToken(alice, {Substrate: alice.address}); + await expect(collection.burn(alice)).to.be.rejectedWith(/common\.CantDestroyNotEmptyCollection/); + }); +}); --- /dev/null +++ b/js-packages/tests/enableDisableTransfer.test.ts @@ -0,0 +1,82 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from './util/index.js'; + +describe('Enable/Disable Transfers', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('User can transfer token with enabled transfer flag', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + limits: { + transfersEnabled: true, + }, + }); + const token = await collection.mintToken(alice, {Substrate: alice.address}); + await token.transfer(alice, {Substrate: bob.address}); + expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); + }); + + itSub('User can\'n transfer token with disabled transfer flag', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + limits: { + transfersEnabled: false, + }, + }); + const token = await collection.mintToken(alice, {Substrate: alice.address}); + await expect(token.transfer(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.TransferNotAllowed/); + }); +}); + +describe('Negative Enable/Disable Transfers', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('Non-owner cannot change transfer flag', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, { + name: 'test', + description: 'test', + tokenPrefix: 'test', + limits: { + transfersEnabled: true, + }, + }); + + await expect(collection.setLimits(bob, {transfersEnabled: false})).to.be.rejectedWith(/common\.NoPermission/); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/abi/collectionHelpers.json @@ -0,0 +1,267 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "collectionId", + "type": "address" + } + ], + "name": "CollectionChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "collectionId", + "type": "address" + } + ], + "name": "CollectionCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "collectionId", + "type": "address" + } + ], + "name": "CollectionDestroyed", + "type": "event" + }, + { + "inputs": [ + { "internalType": "uint32", "name": "collectionId", "type": "uint32" } + ], + "name": "collectionAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionCreationFee", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "collectionAddress", + "type": "address" + } + ], + "name": "collectionId", + "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "description", "type": "string" }, + { + "internalType": "string", + "name": "token_prefix", + "type": "string" + }, + { + "internalType": "enum CollectionMode", + "name": "mode", + "type": "uint8" + }, + { "internalType": "uint8", "name": "decimals", "type": "uint8" }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { + "components": [ + { + "internalType": "enum TokenPermissionField", + "name": "code", + "type": "uint8" + }, + { "internalType": "bool", "name": "value", "type": "bool" } + ], + "internalType": "struct PropertyPermission[]", + "name": "permissions", + "type": "tuple[]" + } + ], + "internalType": "struct TokenPropertyPermission[]", + "name": "token_property_permissions", + "type": "tuple[]" + }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress[]", + "name": "admin_list", + "type": "tuple[]" + }, + { + "components": [ + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { + "internalType": "bool", + "name": "collection_admin", + "type": "bool" + }, + { + "internalType": "address[]", + "name": "restricted", + "type": "address[]" + } + ], + "internalType": "struct CollectionNestingAndPermission", + "name": "nesting_settings", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum CollectionLimitField", + "name": "field", + "type": "uint8" + }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct CollectionLimitValue[]", + "name": "limits", + "type": "tuple[]" + }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "pending_sponsor", + "type": "tuple" + }, + { + "internalType": "CollectionFlags", + "name": "flags", + "type": "uint8" + } + ], + "internalType": "struct CreateCollectionData", + "name": "data", + "type": "tuple" + } + ], + "name": "createCollection", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "uint8", "name": "decimals", "type": "uint8" }, + { "internalType": "string", "name": "description", "type": "string" }, + { "internalType": "string", "name": "tokenPrefix", "type": "string" } + ], + "name": "createFTCollection", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "description", "type": "string" }, + { "internalType": "string", "name": "tokenPrefix", "type": "string" } + ], + "name": "createNFTCollection", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "description", "type": "string" }, + { "internalType": "string", "name": "tokenPrefix", "type": "string" } + ], + "name": "createRFTCollection", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "collectionAddress", + "type": "address" + } + ], + "name": "destroyCollection", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "collectionAddress", + "type": "address" + } + ], + "name": "isCollectionExist", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "collection", "type": "address" }, + { "internalType": "string", "name": "baseUri", "type": "string" } + ], + "name": "makeCollectionERC721MetadataCompatible", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + } +] --- /dev/null +++ b/js-packages/tests/eth/abi/contractHelpers.json @@ -0,0 +1,326 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "ContractSponsorRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "sponsor", + "type": "address" + } + ], + "name": "ContractSponsorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "sponsor", + "type": "address" + } + ], + "name": "ContractSponsorshipConfirmed", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "allowed", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "allowlistEnabled", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "confirmSponsorship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "contractOwner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "hasPendingSponsor", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "hasSponsor", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "removeSponsor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "selfSponsoredEnable", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "address", "name": "sponsor", "type": "address" } + ], + "name": "setSponsor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "uint256", "name": "feeLimit", "type": "uint256" } + ], + "name": "setSponsoringFeeLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { + "internalType": "enum SponsoringModeT", + "name": "mode", + "type": "uint8" + } + ], + "name": "setSponsoringMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "uint32", "name": "rateLimit", "type": "uint32" } + ], + "name": "setSponsoringRateLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "sponsor", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct OptionCrossAddress", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "sponsoringEnabled", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "sponsoringFeeLimit", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "sponsoringRateLimit", + "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "bool", "name": "isAllowed", "type": "bool" } + ], + "name": "toggleAllowed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, + { "internalType": "bool", "name": "enabled", "type": "bool" } + ], + "name": "toggleAllowlist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] --- /dev/null +++ b/js-packages/tests/eth/abi/fungible.json @@ -0,0 +1,722 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "newAdmin", + "type": "tuple" + } + ], + "name": "addCollectionAdminCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "addToCollectionAllowListCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "owner", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "spender", + "type": "tuple" + } + ], + "name": "allowanceCross", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "allowlistedCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "spender", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approveCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "owner", + "type": "tuple" + } + ], + "name": "balanceOfCross", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "from", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "burnFromCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "newOwner", + "type": "tuple" + } + ], + "name": "changeCollectionOwnerCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "collectionAdmins", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionHelperAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionLimits", + "outputs": [ + { + "components": [ + { + "internalType": "enum CollectionLimitField", + "name": "field", + "type": "uint8" + }, + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint256", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct CollectionLimit[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionNesting", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { + "internalType": "bool", + "name": "collection_admin", + "type": "bool" + }, + { + "internalType": "address[]", + "name": "restricted", + "type": "address[]" + } + ], + "internalType": "struct CollectionNestingAndPermission", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionOwner", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "collectionProperties", + "outputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], + "name": "collectionProperty", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionSponsor", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "confirmCollectionSponsorship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "contractAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "deleteCollectionProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hasCollectionPendingSponsor", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "isOwnerOrAdminCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "mint", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "internalType": "struct AmountForAddress[]", + "name": "amounts", + "type": "tuple[]" + } + ], + "name": "mintBulk", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "mintCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "admin", + "type": "tuple" + } + ], + "name": "removeCollectionAdminCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "removeCollectionSponsor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "removeFromCollectionAllowListCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" } + ], + "name": "setCollectionAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "enum CollectionLimitField", + "name": "field", + "type": "uint8" + }, + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint256", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct CollectionLimit", + "name": "limit", + "type": "tuple" + } + ], + "name": "setCollectionLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }], + "name": "setCollectionMintMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { + "internalType": "bool", + "name": "collection_admin", + "type": "bool" + }, + { + "internalType": "address[]", + "name": "restricted", + "type": "address[]" + } + ], + "internalType": "struct CollectionNestingAndPermission", + "name": "collectionNestingAndPermissions", + "type": "tuple" + } + ], + "name": "setCollectionNesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "name": "setCollectionProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "sponsor", + "type": "tuple" + } + ], + "name": "setCollectionSponsorCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "from", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFromCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "uniqueCollectionType", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + } +] --- /dev/null +++ b/js-packages/tests/eth/abi/fungibleDeprecated.json @@ -0,0 +1,151 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "newAdmin", "type": "address" } + ], + "name": "addCollectionAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "addToCollectionAllowList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "burnFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "changeCollectionOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], + "name": "deleteCollectionProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "isOwnerOrAdmin", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "admin", "type": "address" } + ], + "name": "removeCollectionAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "removeFromCollectionAllowList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "name": "setCollectionProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sponsor", "type": "address" } + ], + "name": "setCollectionSponsor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "collectionNestingPermissions", + "outputs": [ + { + "components": [ + { + "internalType": "enum CollectionPermissionField", + "name": "field", + "type": "uint8" + }, + { "internalType": "bool", "name": "value", "type": "bool" } + ], + "internalType": "struct CollectionNestingPermission[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionNestingRestrictedCollectionIds", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" } + ], + "internalType": "struct CollectionNesting", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }], + "name": "setCollectionNesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bool", "name": "enable", "type": "bool" }, + { + "internalType": "address[]", + "name": "collections", + "type": "address[]" + } + ], + "name": "setCollectionNesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] --- /dev/null +++ b/js-packages/tests/eth/abi/nativeFungible.json @@ -0,0 +1,201 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "owner", + "type": "tuple" + } + ], + "name": "balanceOfCross", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "from", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFromCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + } +] --- /dev/null +++ b/js-packages/tests/eth/abi/nonFungible.json @@ -0,0 +1,988 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "TokenChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "newAdmin", + "type": "tuple" + } + ], + "name": "addCollectionAdminCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "addToCollectionAllowListCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "allowlistedCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "approved", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "approved", + "type": "tuple" + }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "approveCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "owner", + "type": "tuple" + } + ], + "name": "balanceOfCross", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "from", + "type": "tuple" + }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "burnFromCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "newOwner", + "type": "tuple" + } + ], + "name": "changeCollectionOwnerCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "collectionAdmins", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionHelperAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionLimits", + "outputs": [ + { + "components": [ + { + "internalType": "enum CollectionLimitField", + "name": "field", + "type": "uint8" + }, + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint256", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct CollectionLimit[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionNesting", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { + "internalType": "bool", + "name": "collection_admin", + "type": "bool" + }, + { + "internalType": "address[]", + "name": "restricted", + "type": "address[]" + } + ], + "internalType": "struct CollectionNestingAndPermission", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionOwner", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "collectionProperties", + "outputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], + "name": "collectionProperty", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionSponsor", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "confirmCollectionSponsorship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "contractAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "deleteCollectionProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "deleteProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "getApproved", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hasCollectionPendingSponsor", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "operator", "type": "address" } + ], + "name": "isApprovedForAll", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "isOwnerOrAdminCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "to", "type": "address" }], + "name": "mint", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "owner", + "type": "tuple" + }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "internalType": "struct MintTokenData[]", + "name": "data", + "type": "tuple[]" + } + ], + "name": "mintBulkCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "name": "mintCross", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "string", "name": "tokenUri", "type": "string" } + ], + "name": "mintWithTokenURI", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nextTokenId", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "ownerOf", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "ownerOfCross", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "properties", + "outputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "string", "name": "key", "type": "string" } + ], + "name": "property", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "admin", + "type": "tuple" + } + ], + "name": "removeCollectionAdminCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "removeCollectionSponsor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "removeFromCollectionAllowListCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "bytes", "name": "data", "type": "bytes" } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "operator", "type": "address" }, + { "internalType": "bool", "name": "approved", "type": "bool" } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" } + ], + "name": "setCollectionAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "enum CollectionLimitField", + "name": "field", + "type": "uint8" + }, + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint256", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct CollectionLimit", + "name": "limit", + "type": "tuple" + } + ], + "name": "setCollectionLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }], + "name": "setCollectionMintMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { + "internalType": "bool", + "name": "collection_admin", + "type": "bool" + }, + { + "internalType": "address[]", + "name": "restricted", + "type": "address[]" + } + ], + "internalType": "struct CollectionNestingAndPermission", + "name": "collectionNestingAndPermissions", + "type": "tuple" + } + ], + "name": "setCollectionNesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "name": "setCollectionProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "sponsor", + "type": "tuple" + } + ], + "name": "setCollectionSponsorCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "name": "setProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { + "components": [ + { + "internalType": "enum TokenPermissionField", + "name": "code", + "type": "uint8" + }, + { "internalType": "bool", "name": "value", "type": "bool" } + ], + "internalType": "struct PropertyPermission[]", + "name": "permissions", + "type": "tuple[]" + } + ], + "internalType": "struct TokenPropertyPermission[]", + "name": "permissions", + "type": "tuple[]" + } + ], + "name": "setTokenPropertyPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "index", "type": "uint256" } + ], + "name": "tokenByIndex", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "uint256", "name": "index", "type": "uint256" } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokenPropertyPermissions", + "outputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { + "components": [ + { + "internalType": "enum TokenPermissionField", + "name": "code", + "type": "uint8" + }, + { "internalType": "bool", "name": "value", "type": "bool" } + ], + "internalType": "struct PropertyPermission[]", + "name": "permissions", + "type": "tuple[]" + } + ], + "internalType": "struct TokenPropertyPermission[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "tokenURI", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transferCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "from", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transferFromCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "uniqueCollectionType", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + } +] --- /dev/null +++ b/js-packages/tests/eth/abi/nonFungibleDeprecated.json @@ -0,0 +1,172 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "newAdmin", "type": "address" } + ], + "name": "addCollectionAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "addToCollectionAllowList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "burnFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], + "name": "deleteCollectionProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "isOwnerOrAdmin", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "admin", "type": "address" } + ], + "name": "removeCollectionAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "removeFromCollectionAllowList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "name": "setCollectionProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sponsor", "type": "address" } + ], + "name": "setCollectionSponsor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "name": "setProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "changeCollectionOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "string", "name": "key", "type": "string" } + ], + "name": "deleteProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "collectionNestingPermissions", + "outputs": [ + { + "components": [ + { + "internalType": "enum CollectionPermissionField", + "name": "field", + "type": "uint8" + }, + { "internalType": "bool", "name": "value", "type": "bool" } + ], + "internalType": "struct CollectionNestingPermission[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionNestingRestrictedCollectionIds", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" } + ], + "internalType": "struct CollectionNesting", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }], + "name": "setCollectionNesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bool", "name": "enable", "type": "bool" }, + { + "internalType": "address[]", + "name": "collections", + "type": "address[]" + } + ], + "name": "setCollectionNesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] --- /dev/null +++ b/js-packages/tests/eth/abi/reFungible.json @@ -0,0 +1,995 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "TokenChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "newAdmin", + "type": "tuple" + } + ], + "name": "addCollectionAdminCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "addToCollectionAllowListCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "allowlistedCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "approved", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "owner", + "type": "tuple" + } + ], + "name": "balanceOfCross", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "from", + "type": "tuple" + }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "burnFromCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "newOwner", + "type": "tuple" + } + ], + "name": "changeCollectionOwnerCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "collectionAdmins", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionHelperAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionLimits", + "outputs": [ + { + "components": [ + { + "internalType": "enum CollectionLimitField", + "name": "field", + "type": "uint8" + }, + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint256", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct CollectionLimit[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionNesting", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { + "internalType": "bool", + "name": "collection_admin", + "type": "bool" + }, + { + "internalType": "address[]", + "name": "restricted", + "type": "address[]" + } + ], + "internalType": "struct CollectionNestingAndPermission", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionOwner", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "collectionProperties", + "outputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], + "name": "collectionProperty", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionSponsor", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "confirmCollectionSponsorship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "contractAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "deleteCollectionProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "deleteProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "getApproved", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hasCollectionPendingSponsor", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "operator", "type": "address" } + ], + "name": "isApprovedForAll", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "isOwnerOrAdminCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "to", "type": "address" }], + "name": "mint", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "eth", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sub", + "type": "uint256" + } + ], + "internalType": "struct CrossAddress", + "name": "owner", + "type": "tuple" + }, + { "internalType": "uint128", "name": "pieces", "type": "uint128" } + ], + "internalType": "struct OwnerPieces[]", + "name": "owners", + "type": "tuple[]" + }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "internalType": "struct MintTokenData[]", + "name": "tokenProperties", + "type": "tuple[]" + } + ], + "name": "mintBulkCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "name": "mintCross", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "string", "name": "tokenUri", "type": "string" } + ], + "name": "mintWithTokenURI", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nextTokenId", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "ownerOf", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "ownerOfCross", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "string[]", "name": "keys", "type": "string[]" } + ], + "name": "properties", + "outputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "string", "name": "key", "type": "string" } + ], + "name": "property", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "admin", + "type": "tuple" + } + ], + "name": "removeCollectionAdminCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "removeCollectionSponsor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "user", + "type": "tuple" + } + ], + "name": "removeFromCollectionAllowListCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "bytes", "name": "data", "type": "bytes" } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "operator", "type": "address" }, + { "internalType": "bool", "name": "approved", "type": "bool" } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" } + ], + "name": "setCollectionAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "enum CollectionLimitField", + "name": "field", + "type": "uint8" + }, + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint256", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct CollectionLimit", + "name": "limit", + "type": "tuple" + } + ], + "name": "setCollectionLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }], + "name": "setCollectionMintMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { + "internalType": "bool", + "name": "collection_admin", + "type": "bool" + }, + { + "internalType": "address[]", + "name": "restricted", + "type": "address[]" + } + ], + "internalType": "struct CollectionNestingAndPermission", + "name": "collectionNestingAndPermissions", + "type": "tuple" + } + ], + "name": "setCollectionNesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "name": "setCollectionProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "sponsor", + "type": "tuple" + } + ], + "name": "setCollectionSponsorCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "internalType": "struct Property[]", + "name": "properties", + "type": "tuple[]" + } + ], + "name": "setProperties", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { + "components": [ + { + "internalType": "enum TokenPermissionField", + "name": "code", + "type": "uint8" + }, + { "internalType": "bool", "name": "value", "type": "bool" } + ], + "internalType": "struct PropertyPermission[]", + "name": "permissions", + "type": "tuple[]" + } + ], + "internalType": "struct TokenPropertyPermission[]", + "name": "permissions", + "type": "tuple[]" + } + ], + "name": "setTokenPropertyPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "index", "type": "uint256" } + ], + "name": "tokenByIndex", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "token", "type": "uint256" } + ], + "name": "tokenContractAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "uint256", "name": "index", "type": "uint256" } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokenPropertyPermissions", + "outputs": [ + { + "components": [ + { "internalType": "string", "name": "key", "type": "string" }, + { + "components": [ + { + "internalType": "enum TokenPermissionField", + "name": "code", + "type": "uint8" + }, + { "internalType": "bool", "name": "value", "type": "bool" } + ], + "internalType": "struct PropertyPermission[]", + "name": "permissions", + "type": "tuple[]" + } + ], + "internalType": "struct TokenPropertyPermission[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "tokenURI", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transferCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "from", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transferFromCross", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "uniqueCollectionType", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + } +] --- /dev/null +++ b/js-packages/tests/eth/abi/reFungibleDeprecated.json @@ -0,0 +1,200 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "newAdmin", "type": "address" } + ], + "name": "addCollectionAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "addToCollectionAllowList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "burnFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], + "name": "deleteCollectionProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "isOwnerOrAdmin", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "admin", "type": "address" } + ], + "name": "removeCollectionAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "removeFromCollectionAllowList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "name": "setCollectionProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sponsor", "type": "address" } + ], + "name": "setCollectionSponsor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "string", "name": "key", "type": "string" }, + { "internalType": "bytes", "name": "value", "type": "bytes" } + ], + "name": "setProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "changeCollectionOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "string", "name": "key", "type": "string" } + ], + "name": "deleteProperty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256[]", "name": "tokenIds", "type": "uint256[]" } + ], + "name": "mintBulk", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { + "components": [ + { "internalType": "uint256", "name": "field_0", "type": "uint256" }, + { "internalType": "string", "name": "field_1", "type": "string" } + ], + "internalType": "struct Tuple0[]", + "name": "tokens", + "type": "tuple[]" + } + ], + "name": "mintBulkWithTokenURI", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "collectionNestingPermissions", + "outputs": [ + { + "components": [ + { + "internalType": "enum CollectionPermissionField", + "name": "field", + "type": "uint8" + }, + { "internalType": "bool", "name": "value", "type": "bool" } + ], + "internalType": "struct CollectionNestingPermission[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collectionNestingRestrictedCollectionIds", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" } + ], + "internalType": "struct CollectionNesting", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }], + "name": "setCollectionNesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bool", "name": "enable", "type": "bool" }, + { + "internalType": "address[]", + "name": "collections", + "type": "address[]" + } + ], + "name": "setCollectionNesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] --- /dev/null +++ b/js-packages/tests/eth/abi/reFungibleToken.json @@ -0,0 +1,286 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "owner", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "spender", + "type": "tuple" + } + ], + "name": "allowanceCross", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "spender", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approveCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "owner", + "type": "tuple" + } + ], + "name": "balanceOfCross", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "from", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "burnFromCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "parentToken", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "parentTokenId", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "repartition", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "from", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFromCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + } +] --- /dev/null +++ b/js-packages/tests/eth/abi/reFungibleTokenDeprecated.json @@ -0,0 +1,12 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "burnFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + } +] --- /dev/null +++ b/js-packages/tests/eth/allowlist.test.ts @@ -0,0 +1,212 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {Pallets} from '../util/index.js'; +import {itEth, usingEthPlaygrounds, expect, SponsoringMode} from './util/index.js'; +import {CreateCollectionData} from './util/playgrounds/types.js'; + +describe('EVM contract allowlist', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Contract allowlist can be toggled', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const flipper = await helper.eth.deployFlipper(owner); + const helpers = helper.ethNativeContract.contractHelpers(owner); + + // Any user is allowed by default + expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.false; + + // Enable + await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); + expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.true; + + // Disable + await helpers.methods.toggleAllowlist(flipper.options.address, false).send({from: owner}); + expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.false; + }); + + itEth('Non-allowlisted user can\'t call contract with allowlist enabled', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const flipper = await helper.eth.deployFlipper(owner); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + + // User can flip with allowlist disabled + await flipper.methods.flip().send({from: caller}); + expect(await flipper.methods.getValue().call()).to.be.true; + + // Tx will be reverted if user is not in allowlist + await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); + await expect(flipper.methods.flip().send({from: caller})).to.rejected; + expect(await flipper.methods.getValue().call()).to.be.true; + + // Adding caller to allowlist will make contract callable again + await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner}); + await flipper.methods.flip().send({from: caller}); + expect(await flipper.methods.getValue().call()).to.be.false; + }); +}); + +describe('EVM collection allowlist', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + // Soft-deprecated + itEth('Collection allowlist can be added and removed by [eth] address', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const user = helper.eth.createAccount(); + const crossUser = helper.ethCrossAccount.fromAddress(user); + + const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + + expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false; + await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner}); + expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.true; + + await collectionEvm.methods.removeFromCollectionAllowList(user).send({from: owner}); + expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false; + }); + + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {mode: 'ft' as const, requiredPallets: []}, + ].map(testCase => + itEth.ifWithPallets(`Collection allowlist can be added and removed by [cross] address for ${testCase.mode}`, testCase.requiredPallets, async ({helper}) => { + const owner = (await helper.eth.createAccountWithBalance(donor)).toLowerCase(); + const [userSub] = await helper.arrange.createAccounts([10n], donor); + const userEth = await helper.eth.createAccountWithBalance(donor); + const mintParams = testCase.mode === 'ft' ? [userEth, 100] : [userEth]; + + const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub); + const userCrossEth = helper.ethCrossAccount.fromAddress(userEth); + const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner); + + // Can addToCollectionAllowListCross: + expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false; + await collectionEvm.methods.addToCollectionAllowListCross(userCrossSub).send({from: owner}); + await collectionEvm.methods.addToCollectionAllowListCross(userCrossEth).send({from: owner}); + await collectionEvm.methods.addToCollectionAllowListCross(ownerCrossEth).send({from: owner}); + + // Accounts are in allowed list: + expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true; + expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true; + expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.true; + expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.true; + + await collectionEvm.methods.mint(...mintParams).send({from: owner}); // token #1 + await collectionEvm.methods.mint(...mintParams).send({from: owner}); // token #2 + await collectionEvm.methods.setCollectionAccess(SponsoringMode.Allowlisted).send({from: owner}); + + // allowlisted account can transfer and transferCross from eth: + await collectionEvm.methods.transfer(owner, 1).send({from: userEth}); + await collectionEvm.methods.transferCross(userCrossSub, 2).send({from: userEth}); + + if(testCase.mode === 'ft') { + expect(await helper.ft.getBalance(collectionId, {Ethereum: owner})).to.eq(1n); + expect(await helper.ft.getBalance(collectionId, {Substrate: userSub.address})).to.eq(2n); + } else { + expect(await helper.nft.getTokenOwner(collectionId, 1)).to.deep.eq({Ethereum: owner}); + expect(await helper.nft.getTokenOwner(collectionId, 2)).to.deep.eq({Substrate: userSub.address}); + } + + // allowlisted cross substrate accounts can transfer from Substrate: + testCase.mode === 'ft' + ? await helper.ft.transfer(userSub, collectionId, {Ethereum: userEth}, 2n) + : await helper.collection.transferToken(userSub, collectionId, 2, {Ethereum: userEth}); + + // can removeFromCollectionAllowListCross: + await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossSub).send({from: owner}); + await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossEth).send({from: owner}); + expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false; + expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.false; + expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.false; + expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.false; + + // cannot transfer anymore + await collectionEvm.methods.mint(...mintParams).send({from: owner}); + await expect(collectionEvm.methods.transfer(owner, 2).send({from: userEth})).to.be.rejectedWith(/Transaction has been reverted/); + })); + + [ + // cross-methods + {mode: 'nft' as const, cross: true, requiredPallets: []}, + {mode: 'rft' as const, cross: true, requiredPallets: [Pallets.ReFungible]}, + {mode: 'ft' as const, cross: true, requiredPallets: []}, + // soft-deprecated + {mode: 'nft' as const, cross: false, requiredPallets: []}, + {mode: 'rft' as const, cross: false, requiredPallets: [Pallets.ReFungible]}, + {mode: 'ft' as const, cross: false, requiredPallets: []}, + ].map(testCase => + itEth.ifWithPallets(`Non-owner cannot add or remove from collection allowlist ${testCase.cross ? 'cross ' : ''}${testCase.mode}`, testCase.requiredPallets, async ({helper}) => { + // Select methods: + const addToAllowList = testCase.cross ? 'addToCollectionAllowListCross' : 'addToCollectionAllowList'; + const removeFromAllowList = testCase.cross ? 'removeFromCollectionAllowListCross' : 'removeFromCollectionAllowList'; + + const owner = await helper.eth.createAccountWithBalance(donor); + const notOwner = await helper.eth.createAccountWithBalance(donor); + const userSub = donor; + const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub); + const userEth = helper.eth.createAccount(); + const userCrossEth = helper.ethCrossAccount.fromAddress(userEth); + + const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, !testCase.cross); + + expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false; + expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.false; + + // 1. notOwner cannot add to allow list: + // 1.1 plain ethereum or cross address: + await expect(collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + // 1.2 cross-substrate address: + if(testCase.cross) + await expect(collectionEvm.methods[addToAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + + // 2. owner can add to allow list: + // 2.1 plain ethereum or cross address: + await collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).send({from: owner}); + // 2.2 cross-substrate address: + if(testCase.cross) { + await collectionEvm.methods[addToAllowList](userCrossSub).send({from: owner}); + expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true; + } + expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true; + + // 3. notOwner cannot remove from allow list: + // 3.1 plain ethereum or cross address: + await expect(collectionEvm.methods[removeFromAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + // 3.2 cross-substrate address: + if(testCase.cross) + await expect(collectionEvm.methods[removeFromAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + })); +}); --- /dev/null +++ b/js-packages/tests/eth/api/CollectionHelpers.sol @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +/// @dev common stubs holder +interface Dummy { + +} + +interface ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +/// @dev inlined interface +interface CollectionHelpersEvents { + event CollectionCreated(address indexed owner, address indexed collectionId); + event CollectionDestroyed(address indexed collectionId); + event CollectionChanged(address indexed collectionId); +} + +/// @title Contract, which allows users to operate with collections +/// @dev the ERC-165 identifier for this interface is 0x94e5af0d +interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents { + /// Create a collection + /// @return address Address of the newly created collection + /// @dev EVM selector for this function is: 0x72b5bea7, + /// or in textual repr: createCollection((string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],(address,uint256),uint8)) + function createCollection(CreateCollectionData memory data) external payable returns (address); + + /// Create an NFT collection + /// @param name Name of the collection + /// @param description Informative description of the collection + /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications + /// @return address Address of the newly created collection + /// @dev EVM selector for this function is: 0x844af658, + /// or in textual repr: createNFTCollection(string,string,string) + function createNFTCollection( + string memory name, + string memory description, + string memory tokenPrefix + ) external payable returns (address); + + // /// Create an NFT collection + // /// @param name Name of the collection + // /// @param description Informative description of the collection + // /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications + // /// @return address Address of the newly created collection + // /// @dev EVM selector for this function is: 0xe34a6844, + // /// or in textual repr: createNonfungibleCollection(string,string,string) + // function createNonfungibleCollection(string memory name, string memory description, string memory tokenPrefix) external payable returns (address); + + /// @dev EVM selector for this function is: 0xab173450, + /// or in textual repr: createRFTCollection(string,string,string) + function createRFTCollection( + string memory name, + string memory description, + string memory tokenPrefix + ) external payable returns (address); + + /// @dev EVM selector for this function is: 0x7335b79f, + /// or in textual repr: createFTCollection(string,uint8,string,string) + function createFTCollection( + string memory name, + uint8 decimals, + string memory description, + string memory tokenPrefix + ) external payable returns (address); + + /// @dev EVM selector for this function is: 0x85624258, + /// or in textual repr: makeCollectionERC721MetadataCompatible(address,string) + function makeCollectionERC721MetadataCompatible(address collection, string memory baseUri) external; + + /// @dev EVM selector for this function is: 0x564e321f, + /// or in textual repr: destroyCollection(address) + function destroyCollection(address collectionAddress) external; + + /// Check if a collection exists + /// @param collectionAddress Address of the collection in question + /// @return bool Does the collection exist? + /// @dev EVM selector for this function is: 0xc3de1494, + /// or in textual repr: isCollectionExist(address) + function isCollectionExist(address collectionAddress) external view returns (bool); + + /// @dev EVM selector for this function is: 0xd23a7ab1, + /// or in textual repr: collectionCreationFee() + function collectionCreationFee() external view returns (uint256); + + /// Returns address of a collection. + /// @param collectionId - CollectionId of the collection + /// @return eth mirror address of the collection + /// @dev EVM selector for this function is: 0x2e716683, + /// or in textual repr: collectionAddress(uint32) + function collectionAddress(uint32 collectionId) external view returns (address); + + /// Returns collectionId of a collection. + /// @param collectionAddress - Eth address of the collection + /// @return collectionId of the collection + /// @dev EVM selector for this function is: 0xb5cb7498, + /// or in textual repr: collectionId(address) + function collectionId(address collectionAddress) external view returns (uint32); +} + +/// Collection properties +struct CreateCollectionData { + /// Collection name + string name; + /// Collection description + string description; + /// Token prefix + string token_prefix; + /// Token type (NFT, FT or RFT) + CollectionMode mode; + /// Fungible token precision + uint8 decimals; + /// Custom Properties + Property[] properties; + /// Permissions for token properties + TokenPropertyPermission[] token_property_permissions; + /// Collection admins + CrossAddress[] admin_list; + /// Nesting settings + CollectionNestingAndPermission nesting_settings; + /// Collection limits + CollectionLimitValue[] limits; + /// Collection sponsor + CrossAddress pending_sponsor; + /// Extra collection flags + CollectionFlags flags; +} + +type CollectionFlags is uint8; + +library CollectionFlagsLib { + /// Tokens in foreign collections can be transferred, but not burnt + CollectionFlags constant foreignField = CollectionFlags.wrap(128); + /// Supports ERC721Metadata + CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64); + /// External collections can't be managed using `unique` api + CollectionFlags constant externalField = CollectionFlags.wrap(1); + + /// Reserved flags + function reservedField(uint8 value) public pure returns (CollectionFlags) { + require(value < 1 << 5, "out of bound value"); + return CollectionFlags.wrap(value << 1); + } +} + +/// Cross account struct +struct CrossAddress { + address eth; + uint256 sub; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. +struct CollectionLimitValue { + CollectionLimitField field; + uint256 value; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. +enum CollectionLimitField { + /// How many tokens can a user have on one account. + AccountTokenOwnership, + /// How many bytes of data are available for sponsorship. + SponsoredDataSize, + /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + SponsoredDataRateLimit, + /// How many tokens can be mined into this collection. + TokenLimit, + /// Timeouts for transfer sponsoring. + SponsorTransferTimeout, + /// Timeout for sponsoring an approval in passed blocks. + SponsorApproveTimeout, + /// Whether the collection owner of the collection can send tokens (which belong to other users). + OwnerCanTransfer, + /// Can the collection owner burn other people's tokens. + OwnerCanDestroy, + /// Is it possible to send tokens from this collection between users. + TransferEnabled +} + +/// Nested collections and permissions +struct CollectionNestingAndPermission { + /// Owner of token can nest tokens under it. + bool token_owner; + /// Admin of token collection can nest tokens under token. + bool collection_admin; + /// If set - only tokens from specified collections can be nested. + address[] restricted; +} + +/// Ethereum representation of Token Property Permissions. +struct TokenPropertyPermission { + /// Token property key. + string key; + /// Token property permissions. + PropertyPermission[] permissions; +} + +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value. +struct PropertyPermission { + /// TokenPermission field. + TokenPermissionField code; + /// TokenPermission value. + bool value; +} + +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. +enum TokenPermissionField { + /// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] + Mutable, + /// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] + TokenOwner, + /// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] + CollectionAdmin +} + +/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). +struct Property { + string key; + bytes value; +} + +/// Type of tokens in collection +enum CollectionMode { + /// Nonfungible + Nonfungible, + /// Fungible + Fungible, + /// Refungible + Refungible +} --- /dev/null +++ b/js-packages/tests/eth/api/ContractHelpers.sol @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +/// @dev common stubs holder +interface Dummy { + +} + +interface ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +/// @dev inlined interface +interface ContractHelpersEvents { + event ContractSponsorSet(address indexed contractAddress, address sponsor); + event ContractSponsorshipConfirmed(address indexed contractAddress, address sponsor); + event ContractSponsorRemoved(address indexed contractAddress); +} + +/// @title Magic contract, which allows users to reconfigure other contracts +/// @dev the ERC-165 identifier for this interface is 0x30afad04 +interface ContractHelpers is Dummy, ERC165, ContractHelpersEvents { + /// Get user, which deployed specified contract + /// @dev May return zero address in case if contract is deployed + /// using uniquenetwork evm-migration pallet, or using other terms not + /// intended by pallet-evm + /// @dev Returns zero address if contract does not exists + /// @param contractAddress Contract to get owner of + /// @return address Owner of contract + /// @dev EVM selector for this function is: 0x5152b14c, + /// or in textual repr: contractOwner(address) + function contractOwner(address contractAddress) external view returns (address); + + /// Set sponsor. + /// @param contractAddress Contract for which a sponsor is being established. + /// @param sponsor User address who set as pending sponsor. + /// @dev EVM selector for this function is: 0xf01fba93, + /// or in textual repr: setSponsor(address,address) + function setSponsor(address contractAddress, address sponsor) external; + + /// Set contract as self sponsored. + /// + /// @param contractAddress Contract for which a self sponsoring is being enabled. + /// @dev EVM selector for this function is: 0x89f7d9ae, + /// or in textual repr: selfSponsoredEnable(address) + function selfSponsoredEnable(address contractAddress) external; + + /// Remove sponsor. + /// + /// @param contractAddress Contract for which a sponsorship is being removed. + /// @dev EVM selector for this function is: 0xef784250, + /// or in textual repr: removeSponsor(address) + function removeSponsor(address contractAddress) external; + + /// Confirm sponsorship. + /// + /// @dev Caller must be same that set via [`setSponsor`]. + /// + /// @param contractAddress Сontract for which need to confirm sponsorship. + /// @dev EVM selector for this function is: 0xabc00001, + /// or in textual repr: confirmSponsorship(address) + function confirmSponsorship(address contractAddress) external; + + /// Get current sponsor. + /// + /// @param contractAddress The contract for which a sponsor is requested. + /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. + /// @dev EVM selector for this function is: 0x766c4f37, + /// or in textual repr: sponsor(address) + function sponsor(address contractAddress) external view returns (OptionCrossAddress memory); + + /// Check tat contract has confirmed sponsor. + /// + /// @param contractAddress The contract for which the presence of a confirmed sponsor is checked. + /// @return **true** if contract has confirmed sponsor. + /// @dev EVM selector for this function is: 0x97418603, + /// or in textual repr: hasSponsor(address) + function hasSponsor(address contractAddress) external view returns (bool); + + /// Check tat contract has pending sponsor. + /// + /// @param contractAddress The contract for which the presence of a pending sponsor is checked. + /// @return **true** if contract has pending sponsor. + /// @dev EVM selector for this function is: 0x39b9b242, + /// or in textual repr: hasPendingSponsor(address) + function hasPendingSponsor(address contractAddress) external view returns (bool); + + /// @dev EVM selector for this function is: 0x6027dc61, + /// or in textual repr: sponsoringEnabled(address) + function sponsoringEnabled(address contractAddress) external view returns (bool); + + /// @dev EVM selector for this function is: 0xfde8a560, + /// or in textual repr: setSponsoringMode(address,uint8) + function setSponsoringMode(address contractAddress, SponsoringModeT mode) external; + + /// Get current contract sponsoring rate limit + /// @param contractAddress Contract to get sponsoring rate limit of + /// @return uint32 Amount of blocks between two sponsored transactions + /// @dev EVM selector for this function is: 0xf29694d8, + /// or in textual repr: sponsoringRateLimit(address) + function sponsoringRateLimit(address contractAddress) external view returns (uint32); + + /// Set contract sponsoring rate limit + /// @dev Sponsoring rate limit - is a minimum amount of blocks that should + /// pass between two sponsored transactions + /// @param contractAddress Contract to change sponsoring rate limit of + /// @param rateLimit Target rate limit + /// @dev Only contract owner can change this setting + /// @dev EVM selector for this function is: 0x77b6c908, + /// or in textual repr: setSponsoringRateLimit(address,uint32) + function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) external; + + /// Set contract sponsoring fee limit + /// @dev Sponsoring fee limit - is maximum fee that could be spent by + /// single transaction + /// @param contractAddress Contract to change sponsoring fee limit of + /// @param feeLimit Fee limit + /// @dev Only contract owner can change this setting + /// @dev EVM selector for this function is: 0x03aed665, + /// or in textual repr: setSponsoringFeeLimit(address,uint256) + function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) external; + + /// Get current contract sponsoring fee limit + /// @param contractAddress Contract to get sponsoring fee limit of + /// @return uint256 Maximum amount of fee that could be spent by single + /// transaction + /// @dev EVM selector for this function is: 0x75b73606, + /// or in textual repr: sponsoringFeeLimit(address) + function sponsoringFeeLimit(address contractAddress) external view returns (uint256); + + /// Is specified user present in contract allow list + /// @dev Contract owner always implicitly included + /// @param contractAddress Contract to check allowlist of + /// @param user User to check + /// @return bool Is specified users exists in contract allowlist + /// @dev EVM selector for this function is: 0x5c658165, + /// or in textual repr: allowed(address,address) + function allowed(address contractAddress, address user) external view returns (bool); + + /// Toggle user presence in contract allowlist + /// @param contractAddress Contract to change allowlist of + /// @param user Which user presence should be toggled + /// @param isAllowed `true` if user should be allowed to be sponsored + /// or call this contract, `false` otherwise + /// @dev Only contract owner can change this setting + /// @dev EVM selector for this function is: 0x4706cc1c, + /// or in textual repr: toggleAllowed(address,address,bool) + function toggleAllowed( + address contractAddress, + address user, + bool isAllowed + ) external; + + /// Is this contract has allowlist access enabled + /// @dev Allowlist always can have users, and it is used for two purposes: + /// in case of allowlist sponsoring mode, users will be sponsored if they exist in allowlist + /// in case of allowlist access enabled, only users from allowlist may call this contract + /// @param contractAddress Contract to get allowlist access of + /// @return bool Is specified contract has allowlist access enabled + /// @dev EVM selector for this function is: 0xc772ef6c, + /// or in textual repr: allowlistEnabled(address) + function allowlistEnabled(address contractAddress) external view returns (bool); + + /// Toggle contract allowlist access + /// @param contractAddress Contract to change allowlist access of + /// @param enabled Should allowlist access to be enabled? + /// @dev EVM selector for this function is: 0x36de20f5, + /// or in textual repr: toggleAllowlist(address,bool) + function toggleAllowlist(address contractAddress, bool enabled) external; +} + +/// Available contract sponsoring modes +enum SponsoringModeT { + /// Sponsoring is disabled + Disabled, + /// Only users from allowlist will be sponsored + Allowlisted, + /// All users will be sponsored + Generous +} + +/// Optional value +struct OptionCrossAddress { + /// Shows the status of accessibility of value + bool status; + /// Actual value if `status` is true + CrossAddress value; +} + +/// Cross account struct +struct CrossAddress { + address eth; + uint256 sub; +} --- /dev/null +++ b/js-packages/tests/eth/api/UniqueFungible.sol @@ -0,0 +1,510 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +/// @dev common stubs holder +interface Dummy { + +} + +interface ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +/// @title A contract that allows you to work with collections. +/// @dev the ERC-165 identifier for this interface is 0xb34d97e9 +interface Collection is Dummy, ERC165 { + // /// Set collection property. + // /// + // /// @param key Property key. + // /// @param value Propery value. + // /// @dev EVM selector for this function is: 0x2f073f66, + // /// or in textual repr: setCollectionProperty(string,bytes) + // function setCollectionProperty(string memory key, bytes memory value) external; + + /// Set collection properties. + /// + /// @param properties Vector of properties key/value pair. + /// @dev EVM selector for this function is: 0x50b26b2a, + /// or in textual repr: setCollectionProperties((string,bytes)[]) + function setCollectionProperties(Property[] memory properties) external; + + // /// Delete collection property. + // /// + // /// @param key Property key. + // /// @dev EVM selector for this function is: 0x7b7debce, + // /// or in textual repr: deleteCollectionProperty(string) + // function deleteCollectionProperty(string memory key) external; + + /// Delete collection properties. + /// + /// @param keys Properties keys. + /// @dev EVM selector for this function is: 0xee206ee3, + /// or in textual repr: deleteCollectionProperties(string[]) + function deleteCollectionProperties(string[] memory keys) external; + + /// Get collection property. + /// + /// @dev Throws error if key not found. + /// + /// @param key Property key. + /// @return bytes The property corresponding to the key. + /// @dev EVM selector for this function is: 0xcf24fd6d, + /// or in textual repr: collectionProperty(string) + function collectionProperty(string memory key) external view returns (bytes memory); + + /// Get collection properties. + /// + /// @param keys Properties keys. Empty keys for all propertyes. + /// @return Vector of properties key/value pairs. + /// @dev EVM selector for this function is: 0x285fb8e6, + /// or in textual repr: collectionProperties(string[]) + function collectionProperties(string[] memory keys) external view returns (Property[] memory); + + // /// Set the sponsor of the collection. + // /// + // /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. + // /// + // /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract. + // /// @dev EVM selector for this function is: 0x7623402e, + // /// or in textual repr: setCollectionSponsor(address) + // function setCollectionSponsor(address sponsor) external; + + /// Set the sponsor of the collection. + /// + /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. + /// + /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. + /// @dev EVM selector for this function is: 0x84a1d5a8, + /// or in textual repr: setCollectionSponsorCross((address,uint256)) + function setCollectionSponsorCross(CrossAddress memory sponsor) external; + + /// Whether there is a pending sponsor. + /// @dev EVM selector for this function is: 0x058ac185, + /// or in textual repr: hasCollectionPendingSponsor() + function hasCollectionPendingSponsor() external view returns (bool); + + /// Collection sponsorship confirmation. + /// + /// @dev After setting the sponsor for the collection, it must be confirmed with this function. + /// @dev EVM selector for this function is: 0x3c50e97a, + /// or in textual repr: confirmCollectionSponsorship() + function confirmCollectionSponsorship() external; + + /// Remove collection sponsor. + /// @dev EVM selector for this function is: 0x6e0326a3, + /// or in textual repr: removeCollectionSponsor() + function removeCollectionSponsor() external; + + /// Get current sponsor. + /// + /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. + /// @dev EVM selector for this function is: 0x6ec0a9f1, + /// or in textual repr: collectionSponsor() + function collectionSponsor() external view returns (CrossAddress memory); + + /// Get current collection limits. + /// + /// @return Array of collection limits + /// @dev EVM selector for this function is: 0xf63bc572, + /// or in textual repr: collectionLimits() + function collectionLimits() external view returns (CollectionLimit[] memory); + + /// Set limits for the collection. + /// @dev Throws error if limit not found. + /// @param limit Some limit. + /// @dev EVM selector for this function is: 0x2316ee74, + /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) + function setCollectionLimit(CollectionLimit memory limit) external; + + /// Get contract address. + /// @dev EVM selector for this function is: 0xf6b4dfb4, + /// or in textual repr: contractAddress() + function contractAddress() external view returns (address); + + /// Add collection admin. + /// @param newAdmin Cross account administrator address. + /// @dev EVM selector for this function is: 0x859aa7d6, + /// or in textual repr: addCollectionAdminCross((address,uint256)) + function addCollectionAdminCross(CrossAddress memory newAdmin) external; + + /// Remove collection admin. + /// @param admin Cross account administrator address. + /// @dev EVM selector for this function is: 0x6c0cd173, + /// or in textual repr: removeCollectionAdminCross((address,uint256)) + function removeCollectionAdminCross(CrossAddress memory admin) external; + + // /// Add collection admin. + // /// @param newAdmin Address of the added administrator. + // /// @dev EVM selector for this function is: 0x92e462c7, + // /// or in textual repr: addCollectionAdmin(address) + // function addCollectionAdmin(address newAdmin) external; + + // /// Remove collection admin. + // /// + // /// @param admin Address of the removed administrator. + // /// @dev EVM selector for this function is: 0xfafd7b42, + // /// or in textual repr: removeCollectionAdmin(address) + // function removeCollectionAdmin(address admin) external; + + /// @dev EVM selector for this function is: 0x0b9f3890, + /// or in textual repr: setCollectionNesting((bool,bool,address[])) + function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external; + + // /// Toggle accessibility of collection nesting. + // /// + // /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled' + // /// @dev EVM selector for this function is: 0x112d4586, + // /// or in textual repr: setCollectionNesting(bool) + // function setCollectionNesting(bool enable) external; + + // /// Toggle accessibility of collection nesting. + // /// + // /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled' + // /// @param collections Addresses of collections that will be available for nesting. + // /// @dev EVM selector for this function is: 0x64872396, + // /// or in textual repr: setCollectionNesting(bool,address[]) + // function setCollectionNesting(bool enable, address[] memory collections) external; + + /// @dev EVM selector for this function is: 0x92c660a8, + /// or in textual repr: collectionNesting() + function collectionNesting() external view returns (CollectionNestingAndPermission memory); + + // /// Returns nesting for a collection + // /// @dev EVM selector for this function is: 0x22d25bfe, + // /// or in textual repr: collectionNestingRestrictedCollectionIds() + // function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory); + + // /// Returns permissions for a collection + // /// @dev EVM selector for this function is: 0x5b2eaf4b, + // /// or in textual repr: collectionNestingPermissions() + // function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory); + + /// Set the collection access method. + /// @param mode Access mode + /// @dev EVM selector for this function is: 0x41835d4c, + /// or in textual repr: setCollectionAccess(uint8) + function setCollectionAccess(AccessMode mode) external; + + /// Checks that user allowed to operate with collection. + /// + /// @param user User address to check. + /// @dev EVM selector for this function is: 0x91b6df49, + /// or in textual repr: allowlistedCross((address,uint256)) + function allowlistedCross(CrossAddress memory user) external view returns (bool); + + // /// Add the user to the allowed list. + // /// + // /// @param user Address of a trusted user. + // /// @dev EVM selector for this function is: 0x67844fe6, + // /// or in textual repr: addToCollectionAllowList(address) + // function addToCollectionAllowList(address user) external; + + /// Add user to allowed list. + /// + /// @param user User cross account address. + /// @dev EVM selector for this function is: 0xa0184a3a, + /// or in textual repr: addToCollectionAllowListCross((address,uint256)) + function addToCollectionAllowListCross(CrossAddress memory user) external; + + // /// Remove the user from the allowed list. + // /// + // /// @param user Address of a removed user. + // /// @dev EVM selector for this function is: 0x85c51acb, + // /// or in textual repr: removeFromCollectionAllowList(address) + // function removeFromCollectionAllowList(address user) external; + + /// Remove user from allowed list. + /// + /// @param user User cross account address. + /// @dev EVM selector for this function is: 0x09ba452a, + /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) + function removeFromCollectionAllowListCross(CrossAddress memory user) external; + + /// Switch permission for minting. + /// + /// @param mode Enable if "true". + /// @dev EVM selector for this function is: 0x00018e84, + /// or in textual repr: setCollectionMintMode(bool) + function setCollectionMintMode(bool mode) external; + + // /// Check that account is the owner or admin of the collection + // /// + // /// @param user account to verify + // /// @return "true" if account is the owner or admin + // /// @dev EVM selector for this function is: 0x9811b0c7, + // /// or in textual repr: isOwnerOrAdmin(address) + // function isOwnerOrAdmin(address user) external view returns (bool); + + /// Check that account is the owner or admin of the collection + /// + /// @param user User cross account to verify + /// @return "true" if account is the owner or admin + /// @dev EVM selector for this function is: 0x3e75a905, + /// or in textual repr: isOwnerOrAdminCross((address,uint256)) + function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool); + + /// Returns collection type + /// + /// @return `Fungible` or `NFT` or `ReFungible` + /// @dev EVM selector for this function is: 0xd34b55b8, + /// or in textual repr: uniqueCollectionType() + function uniqueCollectionType() external view returns (string memory); + + /// Get collection owner. + /// + /// @return Tuble with sponsor address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0xdf727d3b, + /// or in textual repr: collectionOwner() + function collectionOwner() external view returns (CrossAddress memory); + + // /// Changes collection owner to another account + // /// + // /// @dev Owner can be changed only by current owner + // /// @param newOwner new owner account + // /// @dev EVM selector for this function is: 0x4f53e226, + // /// or in textual repr: changeCollectionOwner(address) + // function changeCollectionOwner(address newOwner) external; + + /// Get collection administrators + /// + /// @return Vector of tuples with admins address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0x5813216b, + /// or in textual repr: collectionAdmins() + function collectionAdmins() external view returns (CrossAddress[] memory); + + /// Changes collection owner to another account + /// + /// @dev Owner can be changed only by current owner + /// @param newOwner new owner cross account + /// @dev EVM selector for this function is: 0x6496c497, + /// or in textual repr: changeCollectionOwnerCross((address,uint256)) + function changeCollectionOwnerCross(CrossAddress memory newOwner) external; +} + +/// Cross account struct +struct CrossAddress { + address eth; + uint256 sub; +} + +/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]). +enum AccessMode { + /// Access grant for owner and admins. Used as default. + Normal, + /// Like a [`Normal`](AccessMode::Normal) but also users in allow list. + AllowList +} + +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. +struct CollectionNestingPermission { + CollectionPermissionField field; + bool value; +} + +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. +enum CollectionPermissionField { + /// Owner of token can nest tokens under it. + TokenOwner, + /// Admin of token collection can nest tokens under token. + CollectionAdmin +} + +/// Nested collections. +struct CollectionNesting { + bool token_owner; + uint256[] ids; +} + +/// Nested collections and permissions +struct CollectionNestingAndPermission { + /// Owner of token can nest tokens under it. + bool token_owner; + /// Admin of token collection can nest tokens under token. + bool collection_admin; + /// If set - only tokens from specified collections can be nested. + address[] restricted; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. +struct CollectionLimit { + CollectionLimitField field; + OptionUint256 value; +} + +/// Optional value +struct OptionUint256 { + /// Shows the status of accessibility of value + bool status; + /// Actual value if `status` is true + uint256 value; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. +enum CollectionLimitField { + /// How many tokens can a user have on one account. + AccountTokenOwnership, + /// How many bytes of data are available for sponsorship. + SponsoredDataSize, + /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + SponsoredDataRateLimit, + /// How many tokens can be mined into this collection. + TokenLimit, + /// Timeouts for transfer sponsoring. + SponsorTransferTimeout, + /// Timeout for sponsoring an approval in passed blocks. + SponsorApproveTimeout, + /// Whether the collection owner of the collection can send tokens (which belong to other users). + OwnerCanTransfer, + /// Can the collection owner burn other people's tokens. + OwnerCanDestroy, + /// Is it possible to send tokens from this collection between users. + TransferEnabled +} + +/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). +struct Property { + string key; + bytes value; +} + +/// @dev the ERC-165 identifier for this interface is 0x69d14d3e +interface ERC20UniqueExtensions is Dummy, ERC165 { + /// @dev Function to check the amount of tokens that an owner allowed to a spender. + /// @param owner crossAddress The address which owns the funds. + /// @param spender crossAddress The address which will spend the funds. + /// @return A uint256 specifying the amount of tokens still available for the spender. + /// @dev EVM selector for this function is: 0xe0af4bd7, + /// or in textual repr: allowanceCross((address,uint256),(address,uint256)) + function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) external view returns (uint256); + + /// @notice A description for the collection. + /// @dev EVM selector for this function is: 0x7284e416, + /// or in textual repr: description() + function description() external view returns (string memory); + + /// @dev EVM selector for this function is: 0x269e6158, + /// or in textual repr: mintCross((address,uint256),uint256) + function mintCross(CrossAddress memory to, uint256 amount) external returns (bool); + + /// @dev EVM selector for this function is: 0x0ecd0ab0, + /// or in textual repr: approveCross((address,uint256),uint256) + function approveCross(CrossAddress memory spender, uint256 amount) external returns (bool); + + // /// Burn tokens from account + // /// @dev Function that burns an `amount` of the tokens of a given account, + // /// deducting from the sender's allowance for said account. + // /// @param from The account whose tokens will be burnt. + // /// @param amount The amount that will be burnt. + // /// @dev EVM selector for this function is: 0x79cc6790, + // /// or in textual repr: burnFrom(address,uint256) + // function burnFrom(address from, uint256 amount) external returns (bool); + + /// Burn tokens from account + /// @dev Function that burns an `amount` of the tokens of a given account, + /// deducting from the sender's allowance for said account. + /// @param from The account whose tokens will be burnt. + /// @param amount The amount that will be burnt. + /// @dev EVM selector for this function is: 0xbb2f5a58, + /// or in textual repr: burnFromCross((address,uint256),uint256) + function burnFromCross(CrossAddress memory from, uint256 amount) external returns (bool); + + /// Mint tokens for multiple accounts. + /// @param amounts array of pairs of account address and amount + /// @dev EVM selector for this function is: 0x1acf2d55, + /// or in textual repr: mintBulk((address,uint256)[]) + function mintBulk(AmountForAddress[] memory amounts) external returns (bool); + + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(CrossAddress memory to, uint256 amount) external returns (bool); + + /// @dev EVM selector for this function is: 0xd5cf430b, + /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) + function transferFromCross( + CrossAddress memory from, + CrossAddress memory to, + uint256 amount + ) external returns (bool); + + /// @notice Returns collection helper contract address + /// @dev EVM selector for this function is: 0x1896cce6, + /// or in textual repr: collectionHelperAddress() + function collectionHelperAddress() external view returns (address); + + /// @notice Balance of account + /// @param owner An cross address for whom to query the balance + /// @return The number of fingibles owned by `owner`, possibly zero + /// @dev EVM selector for this function is: 0xec069398, + /// or in textual repr: balanceOfCross((address,uint256)) + function balanceOfCross(CrossAddress memory owner) external view returns (uint256); +} + +struct AmountForAddress { + address to; + uint256 amount; +} + +/// @dev the ERC-165 identifier for this interface is 0x40c10f19 +interface ERC20Mintable is Dummy, ERC165 { + /// Mint tokens for `to` account. + /// @param to account that will receive minted tokens + /// @param amount amount of tokens to mint + /// @dev EVM selector for this function is: 0x40c10f19, + /// or in textual repr: mint(address,uint256) + function mint(address to, uint256 amount) external returns (bool); +} + +/// @dev inlined interface +interface ERC20Events { + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +/// @dev the ERC-165 identifier for this interface is 0x942e8b22 +interface ERC20 is Dummy, ERC165, ERC20Events { + /// @dev EVM selector for this function is: 0x06fdde03, + /// or in textual repr: name() + function name() external view returns (string memory); + + /// @dev EVM selector for this function is: 0x95d89b41, + /// or in textual repr: symbol() + function symbol() external view returns (string memory); + + /// @dev EVM selector for this function is: 0x18160ddd, + /// or in textual repr: totalSupply() + function totalSupply() external view returns (uint256); + + /// @dev EVM selector for this function is: 0x313ce567, + /// or in textual repr: decimals() + function decimals() external view returns (uint8); + + /// @dev EVM selector for this function is: 0x70a08231, + /// or in textual repr: balanceOf(address) + function balanceOf(address owner) external view returns (uint256); + + /// @dev EVM selector for this function is: 0xa9059cbb, + /// or in textual repr: transfer(address,uint256) + function transfer(address to, uint256 amount) external returns (bool); + + /// @dev EVM selector for this function is: 0x23b872dd, + /// or in textual repr: transferFrom(address,address,uint256) + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); + + /// @dev EVM selector for this function is: 0x095ea7b3, + /// or in textual repr: approve(address,uint256) + function approve(address spender, uint256 amount) external returns (bool); + + /// @dev EVM selector for this function is: 0xdd62ed3e, + /// or in textual repr: allowance(address,address) + function allowance(address owner, address spender) external view returns (uint256); +} + +interface UniqueFungible is Dummy, ERC165, ERC20, ERC20Mintable, ERC20UniqueExtensions, Collection {} --- /dev/null +++ b/js-packages/tests/eth/api/UniqueNFT.sol @@ -0,0 +1,855 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +/// @dev common stubs holder +interface Dummy { + +} + +interface ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +/// @dev inlined interface +interface ERC721TokenEvent { + event TokenChanged(uint256 indexed tokenId); +} + +/// @title A contract that allows to set and delete token properties and change token property permissions. +/// @dev the ERC-165 identifier for this interface is 0xde0695c2 +interface TokenProperties is Dummy, ERC165, ERC721TokenEvent { + // /// @notice Set permissions for token property. + // /// @dev Throws error if `msg.sender` is not admin or owner of the collection. + // /// @param key Property key. + // /// @param isMutable Permission to mutate property. + // /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable. + // /// @param tokenOwner Permission to mutate property by token owner if property is mutable. + // /// @dev EVM selector for this function is: 0x222d97fa, + // /// or in textual repr: setTokenPropertyPermission(string,bool,bool,bool) + // function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) external; + + /// @notice Set permissions for token property. + /// @dev Throws error if `msg.sender` is not admin or owner of the collection. + /// @param permissions Permissions for keys. + /// @dev EVM selector for this function is: 0xbd92983a, + /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) + function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) external; + + /// @notice Get permissions for token properties. + /// @dev EVM selector for this function is: 0xf23d7790, + /// or in textual repr: tokenPropertyPermissions() + function tokenPropertyPermissions() external view returns (TokenPropertyPermission[] memory); + + // /// @notice Set token property value. + // /// @dev Throws error if `msg.sender` has no permission to edit the property. + // /// @param tokenId ID of the token. + // /// @param key Property key. + // /// @param value Property value. + // /// @dev EVM selector for this function is: 0x1752d67b, + // /// or in textual repr: setProperty(uint256,string,bytes) + // function setProperty(uint256 tokenId, string memory key, bytes memory value) external; + + /// @notice Set token properties value. + /// @dev Throws error if `msg.sender` has no permission to edit the property. + /// @param tokenId ID of the token. + /// @param properties settable properties + /// @dev EVM selector for this function is: 0x14ed3a6e, + /// or in textual repr: setProperties(uint256,(string,bytes)[]) + function setProperties(uint256 tokenId, Property[] memory properties) external; + + // /// @notice Delete token property value. + // /// @dev Throws error if `msg.sender` has no permission to edit the property. + // /// @param tokenId ID of the token. + // /// @param key Property key. + // /// @dev EVM selector for this function is: 0x066111d1, + // /// or in textual repr: deleteProperty(uint256,string) + // function deleteProperty(uint256 tokenId, string memory key) external; + + /// @notice Delete token properties value. + /// @dev Throws error if `msg.sender` has no permission to edit the property. + /// @param tokenId ID of the token. + /// @param keys Properties key. + /// @dev EVM selector for this function is: 0xc472d371, + /// or in textual repr: deleteProperties(uint256,string[]) + function deleteProperties(uint256 tokenId, string[] memory keys) external; + + /// @notice Get token property value. + /// @dev Throws error if key not found + /// @param tokenId ID of the token. + /// @param key Property key. + /// @return Property value bytes + /// @dev EVM selector for this function is: 0x7228c327, + /// or in textual repr: property(uint256,string) + function property(uint256 tokenId, string memory key) external view returns (bytes memory); +} + +/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). +struct Property { + string key; + bytes value; +} + +/// Ethereum representation of Token Property Permissions. +struct TokenPropertyPermission { + /// Token property key. + string key; + /// Token property permissions. + PropertyPermission[] permissions; +} + +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value. +struct PropertyPermission { + /// TokenPermission field. + TokenPermissionField code; + /// TokenPermission value. + bool value; +} + +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. +enum TokenPermissionField { + /// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] + Mutable, + /// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] + TokenOwner, + /// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] + CollectionAdmin +} + +/// @title A contract that allows you to work with collections. +/// @dev the ERC-165 identifier for this interface is 0xb34d97e9 +interface Collection is Dummy, ERC165 { + // /// Set collection property. + // /// + // /// @param key Property key. + // /// @param value Propery value. + // /// @dev EVM selector for this function is: 0x2f073f66, + // /// or in textual repr: setCollectionProperty(string,bytes) + // function setCollectionProperty(string memory key, bytes memory value) external; + + /// Set collection properties. + /// + /// @param properties Vector of properties key/value pair. + /// @dev EVM selector for this function is: 0x50b26b2a, + /// or in textual repr: setCollectionProperties((string,bytes)[]) + function setCollectionProperties(Property[] memory properties) external; + + // /// Delete collection property. + // /// + // /// @param key Property key. + // /// @dev EVM selector for this function is: 0x7b7debce, + // /// or in textual repr: deleteCollectionProperty(string) + // function deleteCollectionProperty(string memory key) external; + + /// Delete collection properties. + /// + /// @param keys Properties keys. + /// @dev EVM selector for this function is: 0xee206ee3, + /// or in textual repr: deleteCollectionProperties(string[]) + function deleteCollectionProperties(string[] memory keys) external; + + /// Get collection property. + /// + /// @dev Throws error if key not found. + /// + /// @param key Property key. + /// @return bytes The property corresponding to the key. + /// @dev EVM selector for this function is: 0xcf24fd6d, + /// or in textual repr: collectionProperty(string) + function collectionProperty(string memory key) external view returns (bytes memory); + + /// Get collection properties. + /// + /// @param keys Properties keys. Empty keys for all propertyes. + /// @return Vector of properties key/value pairs. + /// @dev EVM selector for this function is: 0x285fb8e6, + /// or in textual repr: collectionProperties(string[]) + function collectionProperties(string[] memory keys) external view returns (Property[] memory); + + // /// Set the sponsor of the collection. + // /// + // /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. + // /// + // /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract. + // /// @dev EVM selector for this function is: 0x7623402e, + // /// or in textual repr: setCollectionSponsor(address) + // function setCollectionSponsor(address sponsor) external; + + /// Set the sponsor of the collection. + /// + /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. + /// + /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. + /// @dev EVM selector for this function is: 0x84a1d5a8, + /// or in textual repr: setCollectionSponsorCross((address,uint256)) + function setCollectionSponsorCross(CrossAddress memory sponsor) external; + + /// Whether there is a pending sponsor. + /// @dev EVM selector for this function is: 0x058ac185, + /// or in textual repr: hasCollectionPendingSponsor() + function hasCollectionPendingSponsor() external view returns (bool); + + /// Collection sponsorship confirmation. + /// + /// @dev After setting the sponsor for the collection, it must be confirmed with this function. + /// @dev EVM selector for this function is: 0x3c50e97a, + /// or in textual repr: confirmCollectionSponsorship() + function confirmCollectionSponsorship() external; + + /// Remove collection sponsor. + /// @dev EVM selector for this function is: 0x6e0326a3, + /// or in textual repr: removeCollectionSponsor() + function removeCollectionSponsor() external; + + /// Get current sponsor. + /// + /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. + /// @dev EVM selector for this function is: 0x6ec0a9f1, + /// or in textual repr: collectionSponsor() + function collectionSponsor() external view returns (CrossAddress memory); + + /// Get current collection limits. + /// + /// @return Array of collection limits + /// @dev EVM selector for this function is: 0xf63bc572, + /// or in textual repr: collectionLimits() + function collectionLimits() external view returns (CollectionLimit[] memory); + + /// Set limits for the collection. + /// @dev Throws error if limit not found. + /// @param limit Some limit. + /// @dev EVM selector for this function is: 0x2316ee74, + /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) + function setCollectionLimit(CollectionLimit memory limit) external; + + /// Get contract address. + /// @dev EVM selector for this function is: 0xf6b4dfb4, + /// or in textual repr: contractAddress() + function contractAddress() external view returns (address); + + /// Add collection admin. + /// @param newAdmin Cross account administrator address. + /// @dev EVM selector for this function is: 0x859aa7d6, + /// or in textual repr: addCollectionAdminCross((address,uint256)) + function addCollectionAdminCross(CrossAddress memory newAdmin) external; + + /// Remove collection admin. + /// @param admin Cross account administrator address. + /// @dev EVM selector for this function is: 0x6c0cd173, + /// or in textual repr: removeCollectionAdminCross((address,uint256)) + function removeCollectionAdminCross(CrossAddress memory admin) external; + + // /// Add collection admin. + // /// @param newAdmin Address of the added administrator. + // /// @dev EVM selector for this function is: 0x92e462c7, + // /// or in textual repr: addCollectionAdmin(address) + // function addCollectionAdmin(address newAdmin) external; + + // /// Remove collection admin. + // /// + // /// @param admin Address of the removed administrator. + // /// @dev EVM selector for this function is: 0xfafd7b42, + // /// or in textual repr: removeCollectionAdmin(address) + // function removeCollectionAdmin(address admin) external; + + /// @dev EVM selector for this function is: 0x0b9f3890, + /// or in textual repr: setCollectionNesting((bool,bool,address[])) + function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external; + + // /// Toggle accessibility of collection nesting. + // /// + // /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled' + // /// @dev EVM selector for this function is: 0x112d4586, + // /// or in textual repr: setCollectionNesting(bool) + // function setCollectionNesting(bool enable) external; + + // /// Toggle accessibility of collection nesting. + // /// + // /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled' + // /// @param collections Addresses of collections that will be available for nesting. + // /// @dev EVM selector for this function is: 0x64872396, + // /// or in textual repr: setCollectionNesting(bool,address[]) + // function setCollectionNesting(bool enable, address[] memory collections) external; + + /// @dev EVM selector for this function is: 0x92c660a8, + /// or in textual repr: collectionNesting() + function collectionNesting() external view returns (CollectionNestingAndPermission memory); + + // /// Returns nesting for a collection + // /// @dev EVM selector for this function is: 0x22d25bfe, + // /// or in textual repr: collectionNestingRestrictedCollectionIds() + // function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory); + + // /// Returns permissions for a collection + // /// @dev EVM selector for this function is: 0x5b2eaf4b, + // /// or in textual repr: collectionNestingPermissions() + // function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory); + + /// Set the collection access method. + /// @param mode Access mode + /// @dev EVM selector for this function is: 0x41835d4c, + /// or in textual repr: setCollectionAccess(uint8) + function setCollectionAccess(AccessMode mode) external; + + /// Checks that user allowed to operate with collection. + /// + /// @param user User address to check. + /// @dev EVM selector for this function is: 0x91b6df49, + /// or in textual repr: allowlistedCross((address,uint256)) + function allowlistedCross(CrossAddress memory user) external view returns (bool); + + // /// Add the user to the allowed list. + // /// + // /// @param user Address of a trusted user. + // /// @dev EVM selector for this function is: 0x67844fe6, + // /// or in textual repr: addToCollectionAllowList(address) + // function addToCollectionAllowList(address user) external; + + /// Add user to allowed list. + /// + /// @param user User cross account address. + /// @dev EVM selector for this function is: 0xa0184a3a, + /// or in textual repr: addToCollectionAllowListCross((address,uint256)) + function addToCollectionAllowListCross(CrossAddress memory user) external; + + // /// Remove the user from the allowed list. + // /// + // /// @param user Address of a removed user. + // /// @dev EVM selector for this function is: 0x85c51acb, + // /// or in textual repr: removeFromCollectionAllowList(address) + // function removeFromCollectionAllowList(address user) external; + + /// Remove user from allowed list. + /// + /// @param user User cross account address. + /// @dev EVM selector for this function is: 0x09ba452a, + /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) + function removeFromCollectionAllowListCross(CrossAddress memory user) external; + + /// Switch permission for minting. + /// + /// @param mode Enable if "true". + /// @dev EVM selector for this function is: 0x00018e84, + /// or in textual repr: setCollectionMintMode(bool) + function setCollectionMintMode(bool mode) external; + + // /// Check that account is the owner or admin of the collection + // /// + // /// @param user account to verify + // /// @return "true" if account is the owner or admin + // /// @dev EVM selector for this function is: 0x9811b0c7, + // /// or in textual repr: isOwnerOrAdmin(address) + // function isOwnerOrAdmin(address user) external view returns (bool); + + /// Check that account is the owner or admin of the collection + /// + /// @param user User cross account to verify + /// @return "true" if account is the owner or admin + /// @dev EVM selector for this function is: 0x3e75a905, + /// or in textual repr: isOwnerOrAdminCross((address,uint256)) + function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool); + + /// Returns collection type + /// + /// @return `Fungible` or `NFT` or `ReFungible` + /// @dev EVM selector for this function is: 0xd34b55b8, + /// or in textual repr: uniqueCollectionType() + function uniqueCollectionType() external view returns (string memory); + + /// Get collection owner. + /// + /// @return Tuble with sponsor address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0xdf727d3b, + /// or in textual repr: collectionOwner() + function collectionOwner() external view returns (CrossAddress memory); + + // /// Changes collection owner to another account + // /// + // /// @dev Owner can be changed only by current owner + // /// @param newOwner new owner account + // /// @dev EVM selector for this function is: 0x4f53e226, + // /// or in textual repr: changeCollectionOwner(address) + // function changeCollectionOwner(address newOwner) external; + + /// Get collection administrators + /// + /// @return Vector of tuples with admins address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0x5813216b, + /// or in textual repr: collectionAdmins() + function collectionAdmins() external view returns (CrossAddress[] memory); + + /// Changes collection owner to another account + /// + /// @dev Owner can be changed only by current owner + /// @param newOwner new owner cross account + /// @dev EVM selector for this function is: 0x6496c497, + /// or in textual repr: changeCollectionOwnerCross((address,uint256)) + function changeCollectionOwnerCross(CrossAddress memory newOwner) external; +} + +/// Cross account struct +struct CrossAddress { + address eth; + uint256 sub; +} + +/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]). +enum AccessMode { + /// Access grant for owner and admins. Used as default. + Normal, + /// Like a [`Normal`](AccessMode::Normal) but also users in allow list. + AllowList +} + +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. +struct CollectionNestingPermission { + CollectionPermissionField field; + bool value; +} + +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. +enum CollectionPermissionField { + /// Owner of token can nest tokens under it. + TokenOwner, + /// Admin of token collection can nest tokens under token. + CollectionAdmin +} + +/// Nested collections. +struct CollectionNesting { + bool token_owner; + uint256[] ids; +} + +/// Nested collections and permissions +struct CollectionNestingAndPermission { + /// Owner of token can nest tokens under it. + bool token_owner; + /// Admin of token collection can nest tokens under token. + bool collection_admin; + /// If set - only tokens from specified collections can be nested. + address[] restricted; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. +struct CollectionLimit { + CollectionLimitField field; + OptionUint256 value; +} + +/// Optional value +struct OptionUint256 { + /// Shows the status of accessibility of value + bool status; + /// Actual value if `status` is true + uint256 value; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. +enum CollectionLimitField { + /// How many tokens can a user have on one account. + AccountTokenOwnership, + /// How many bytes of data are available for sponsorship. + SponsoredDataSize, + /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + SponsoredDataRateLimit, + /// How many tokens can be mined into this collection. + TokenLimit, + /// Timeouts for transfer sponsoring. + SponsorTransferTimeout, + /// Timeout for sponsoring an approval in passed blocks. + SponsorApproveTimeout, + /// Whether the collection owner of the collection can send tokens (which belong to other users). + OwnerCanTransfer, + /// Can the collection owner burn other people's tokens. + OwnerCanDestroy, + /// Is it possible to send tokens from this collection between users. + TransferEnabled +} + +/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension +/// @dev See https://eips.ethereum.org/EIPS/eip-721 +/// @dev the ERC-165 identifier for this interface is 0x5b5e139f +interface ERC721Metadata is Dummy, ERC165 { + // /// @notice A descriptive name for a collection of NFTs in this contract + // /// @dev real implementation of this function lies in `ERC721UniqueExtensions` + // /// @dev EVM selector for this function is: 0x06fdde03, + // /// or in textual repr: name() + // function name() external view returns (string memory); + + // /// @notice An abbreviated name for NFTs in this contract + // /// @dev real implementation of this function lies in `ERC721UniqueExtensions` + // /// @dev EVM selector for this function is: 0x95d89b41, + // /// or in textual repr: symbol() + // function symbol() external view returns (string memory); + + /// @notice A distinct Uniform Resource Identifier (URI) for a given asset. + /// + /// @dev If the token has a `url` property and it is not empty, it is returned. + /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`. + /// If the collection property `baseURI` is empty or absent, return "" (empty string) + /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix + /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings). + /// + /// @return token's const_metadata + /// @dev EVM selector for this function is: 0xc87b56dd, + /// or in textual repr: tokenURI(uint256) + function tokenURI(uint256 tokenId) external view returns (string memory); +} + +/// @title ERC721 Token that can be irreversibly burned (destroyed). +/// @dev the ERC-165 identifier for this interface is 0x42966c68 +interface ERC721Burnable is Dummy, ERC165 { + /// @notice Burns a specific ERC721 token. + /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized + /// operator of the current owner. + /// @param tokenId The NFT to approve + /// @dev EVM selector for this function is: 0x42966c68, + /// or in textual repr: burn(uint256) + function burn(uint256 tokenId) external; +} + +/// @title ERC721 minting logic. +/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6 +interface ERC721UniqueMintable is Dummy, ERC165 { + /// @notice Function to mint a token. + /// @param to The new owner + /// @return uint256 The id of the newly minted token + /// @dev EVM selector for this function is: 0x6a627842, + /// or in textual repr: mint(address) + function mint(address to) external returns (uint256); + + // /// @notice Function to mint a token. + // /// @dev `tokenId` should be obtained with `nextTokenId` method, + // /// unlike standard, you can't specify it manually + // /// @param to The new owner + // /// @param tokenId ID of the minted NFT + // /// @dev EVM selector for this function is: 0x40c10f19, + // /// or in textual repr: mint(address,uint256) + // function mint(address to, uint256 tokenId) external returns (bool); + + /// @notice Function to mint token with the given tokenUri. + /// @param to The new owner + /// @param tokenUri Token URI that would be stored in the NFT properties + /// @return uint256 The id of the newly minted token + /// @dev EVM selector for this function is: 0x45c17782, + /// or in textual repr: mintWithTokenURI(address,string) + function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256); + // /// @notice Function to mint token with the given tokenUri. + // /// @dev `tokenId` should be obtained with `nextTokenId` method, + // /// unlike standard, you can't specify it manually + // /// @param to The new owner + // /// @param tokenId ID of the minted NFT + // /// @param tokenUri Token URI that would be stored in the NFT properties + // /// @dev EVM selector for this function is: 0x50bb4e7f, + // /// or in textual repr: mintWithTokenURI(address,uint256,string) + // function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool); + +} + +/// @title Unique extensions for ERC721. +/// @dev the ERC-165 identifier for this interface is 0x9b397d16 +interface ERC721UniqueExtensions is Dummy, ERC165 { + /// @notice A descriptive name for a collection of NFTs in this contract + /// @dev EVM selector for this function is: 0x06fdde03, + /// or in textual repr: name() + function name() external view returns (string memory); + + /// @notice An abbreviated name for NFTs in this contract + /// @dev EVM selector for this function is: 0x95d89b41, + /// or in textual repr: symbol() + function symbol() external view returns (string memory); + + /// @notice A description for the collection. + /// @dev EVM selector for this function is: 0x7284e416, + /// or in textual repr: description() + function description() external view returns (string memory); + + // /// Returns the owner (in cross format) of the token. + // /// + // /// @param tokenId Id for the token. + // /// @dev EVM selector for this function is: 0x2b29dace, + // /// or in textual repr: crossOwnerOf(uint256) + // function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory); + + /// Returns the owner (in cross format) of the token. + /// + /// @param tokenId Id for the token. + /// @dev EVM selector for this function is: 0xcaa3a4d0, + /// or in textual repr: ownerOfCross(uint256) + function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory); + + /// @notice Count all NFTs assigned to an owner + /// @param owner An cross address for whom to query the balance + /// @return The number of NFTs owned by `owner`, possibly zero + /// @dev EVM selector for this function is: 0xec069398, + /// or in textual repr: balanceOfCross((address,uint256)) + function balanceOfCross(CrossAddress memory owner) external view returns (uint256); + + /// Returns the token properties. + /// + /// @param tokenId Id for the token. + /// @param keys Properties keys. Empty keys for all propertyes. + /// @return Vector of properties key/value pairs. + /// @dev EVM selector for this function is: 0xe07ede7e, + /// or in textual repr: properties(uint256,string[]) + function properties(uint256 tokenId, string[] memory keys) external view returns (Property[] memory); + + /// @notice Set or reaffirm the approved address for an NFT + /// @dev The zero address indicates there is no approved address. + /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized + /// operator of the current owner. + /// @param approved The new substrate address approved NFT controller + /// @param tokenId The NFT to approve + /// @dev EVM selector for this function is: 0x0ecd0ab0, + /// or in textual repr: approveCross((address,uint256),uint256) + function approveCross(CrossAddress memory approved, uint256 tokenId) external; + + /// @notice Transfer ownership of an NFT + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid NFT. + /// @param to The new owner + /// @param tokenId The NFT to transfer + /// @dev EVM selector for this function is: 0xa9059cbb, + /// or in textual repr: transfer(address,uint256) + function transfer(address to, uint256 tokenId) external; + + /// @notice Transfer ownership of an NFT + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid NFT. + /// @param to The new owner + /// @param tokenId The NFT to transfer + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(CrossAddress memory to, uint256 tokenId) external; + + /// @notice Transfer ownership of an NFT from cross account address to cross account address + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid NFT. + /// @param from Cross acccount address of current owner + /// @param to Cross acccount address of new owner + /// @param tokenId The NFT to transfer + /// @dev EVM selector for this function is: 0xd5cf430b, + /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) + function transferFromCross( + CrossAddress memory from, + CrossAddress memory to, + uint256 tokenId + ) external; + + // /// @notice Burns a specific ERC721 token. + // /// @dev Throws unless `msg.sender` is the current owner or an authorized + // /// operator for this NFT. Throws if `from` is not the current owner. Throws + // /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT. + // /// @param from The current owner of the NFT + // /// @param tokenId The NFT to transfer + // /// @dev EVM selector for this function is: 0x79cc6790, + // /// or in textual repr: burnFrom(address,uint256) + // function burnFrom(address from, uint256 tokenId) external; + + /// @notice Burns a specific ERC721 token. + /// @dev Throws unless `msg.sender` is the current owner or an authorized + /// operator for this NFT. Throws if `from` is not the current owner. Throws + /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT. + /// @param from The current owner of the NFT + /// @param tokenId The NFT to transfer + /// @dev EVM selector for this function is: 0xbb2f5a58, + /// or in textual repr: burnFromCross((address,uint256),uint256) + function burnFromCross(CrossAddress memory from, uint256 tokenId) external; + + /// @notice Returns next free NFT ID. + /// @dev EVM selector for this function is: 0x75794a3c, + /// or in textual repr: nextTokenId() + function nextTokenId() external view returns (uint256); + + // /// @notice Function to mint multiple tokens. + // /// @dev `tokenIds` should be an array of consecutive numbers and first number + // /// should be obtained with `nextTokenId` method + // /// @param to The new owner + // /// @param tokenIds IDs of the minted NFTs + // /// @dev EVM selector for this function is: 0x44a9945e, + // /// or in textual repr: mintBulk(address,uint256[]) + // function mintBulk(address to, uint256[] memory tokenIds) external returns (bool); + + /// @notice Function to mint a token. + /// @param data Array of pairs of token owner and token's properties for minted token + /// @dev EVM selector for this function is: 0xab427b0c, + /// or in textual repr: mintBulkCross(((address,uint256),(string,bytes)[])[]) + function mintBulkCross(MintTokenData[] memory data) external returns (bool); + + // /// @notice Function to mint multiple tokens with the given tokenUris. + // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive + // /// numbers and first number should be obtained with `nextTokenId` method + // /// @param to The new owner + // /// @param tokens array of pairs of token ID and token URI for minted tokens + // /// @dev EVM selector for this function is: 0x36543006, + // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) + // function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) external returns (bool); + + /// @notice Function to mint a token. + /// @param to The new owner crossAccountId + /// @param properties Properties of minted token + /// @return uint256 The id of the newly minted token + /// @dev EVM selector for this function is: 0xb904db03, + /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) + function mintCross(CrossAddress memory to, Property[] memory properties) external returns (uint256); + + /// @notice Returns collection helper contract address + /// @dev EVM selector for this function is: 0x1896cce6, + /// or in textual repr: collectionHelperAddress() + function collectionHelperAddress() external view returns (address); +} + +/// Data for creation token with uri. +struct TokenUri { + /// Id of new token. + uint256 id; + /// Uri of new token. + string uri; +} + +/// Token minting parameters +struct MintTokenData { + /// Minted token owner + CrossAddress owner; + /// Minted token properties + Property[] properties; +} + +/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension +/// @dev See https://eips.ethereum.org/EIPS/eip-721 +/// @dev the ERC-165 identifier for this interface is 0x780e9d63 +interface ERC721Enumerable is Dummy, ERC165 { + /// @notice Enumerate valid NFTs + /// @param index A counter less than `totalSupply()` + /// @return The token identifier for the `index`th NFT, + /// (sort order not specified) + /// @dev EVM selector for this function is: 0x4f6ccce7, + /// or in textual repr: tokenByIndex(uint256) + function tokenByIndex(uint256 index) external view returns (uint256); + + /// @dev Not implemented + /// @dev EVM selector for this function is: 0x2f745c59, + /// or in textual repr: tokenOfOwnerByIndex(address,uint256) + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + + /// @notice Count NFTs tracked by this contract + /// @return A count of valid NFTs tracked by this contract, where each one of + /// them has an assigned and queryable owner not equal to the zero address + /// @dev EVM selector for this function is: 0x18160ddd, + /// or in textual repr: totalSupply() + function totalSupply() external view returns (uint256); +} + +/// @dev inlined interface +interface ERC721Events { + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); +} + +/// @title ERC-721 Non-Fungible Token Standard +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md +/// @dev the ERC-165 identifier for this interface is 0x80ac58cd +interface ERC721 is Dummy, ERC165, ERC721Events { + /// @notice Count all NFTs assigned to an owner + /// @dev NFTs assigned to the zero address are considered invalid, and this + /// function throws for queries about the zero address. + /// @param owner An address for whom to query the balance + /// @return The number of NFTs owned by `owner`, possibly zero + /// @dev EVM selector for this function is: 0x70a08231, + /// or in textual repr: balanceOf(address) + function balanceOf(address owner) external view returns (uint256); + + /// @notice Find the owner of an NFT + /// @dev NFTs assigned to zero address are considered invalid, and queries + /// about them do throw. + /// @param tokenId The identifier for an NFT + /// @return The address of the owner of the NFT + /// @dev EVM selector for this function is: 0x6352211e, + /// or in textual repr: ownerOf(uint256) + function ownerOf(uint256 tokenId) external view returns (address); + + /// @dev Not implemented + /// @dev EVM selector for this function is: 0xb88d4fde, + /// or in textual repr: safeTransferFrom(address,address,uint256,bytes) + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes memory data + ) external; + + /// @dev Not implemented + /// @dev EVM selector for this function is: 0x42842e0e, + /// or in textual repr: safeTransferFrom(address,address,uint256) + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE + /// TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE + /// THEY MAY BE PERMANENTLY LOST + /// @dev Throws unless `msg.sender` is the current owner or an authorized + /// operator for this NFT. Throws if `from` is not the current owner. Throws + /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT. + /// @param from The current owner of the NFT + /// @param to The new owner + /// @param tokenId The NFT to transfer + /// @dev EVM selector for this function is: 0x23b872dd, + /// or in textual repr: transferFrom(address,address,uint256) + function transferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /// @notice Set or reaffirm the approved address for an NFT + /// @dev The zero address indicates there is no approved address. + /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized + /// operator of the current owner. + /// @param approved The new approved NFT controller + /// @param tokenId The NFT to approve + /// @dev EVM selector for this function is: 0x095ea7b3, + /// or in textual repr: approve(address,uint256) + function approve(address approved, uint256 tokenId) external; + + /// @notice Sets or unsets the approval of a given operator. + /// The `operator` is allowed to transfer all tokens of the `caller` on their behalf. + /// @param operator Operator + /// @param approved Should operator status be granted or revoked? + /// @dev EVM selector for this function is: 0xa22cb465, + /// or in textual repr: setApprovalForAll(address,bool) + function setApprovalForAll(address operator, bool approved) external; + + /// @notice Get the approved address for a single NFT + /// @dev Throws if `tokenId` is not a valid NFT + /// @param tokenId The NFT to find the approved address for + /// @return The approved address for this NFT, or the zero address if there is none + /// @dev EVM selector for this function is: 0x081812fc, + /// or in textual repr: getApproved(uint256) + function getApproved(uint256 tokenId) external view returns (address); + + /// @notice Tells whether the given `owner` approves the `operator`. + /// @dev EVM selector for this function is: 0xe985e9c5, + /// or in textual repr: isApprovedForAll(address,address) + function isApprovedForAll(address owner, address operator) external view returns (bool); +} + +interface UniqueNFT is + Dummy, + ERC165, + ERC721, + ERC721Enumerable, + ERC721UniqueExtensions, + ERC721UniqueMintable, + ERC721Burnable, + ERC721Metadata, + Collection, + TokenProperties +{} --- /dev/null +++ b/js-packages/tests/eth/api/UniqueNativeFungible.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +/// @dev common stubs holder +interface Dummy { + +} + +interface ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +/// @dev the ERC-165 identifier for this interface is 0x1313556c +interface ERC20UniqueExtensions is Dummy, ERC165 { + /// @dev EVM selector for this function is: 0xec069398, + /// or in textual repr: balanceOfCross((address,uint256)) + function balanceOfCross(CrossAddress memory owner) external view returns (uint256); + + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(CrossAddress memory to, uint256 amount) external returns (bool); + + /// @dev EVM selector for this function is: 0xd5cf430b, + /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) + function transferFromCross( + CrossAddress memory from, + CrossAddress memory to, + uint256 amount + ) external returns (bool); +} + +/// Cross account struct +struct CrossAddress { + address eth; + uint256 sub; +} + +/// @dev inlined interface +interface ERC20Events { + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +/// @dev the ERC-165 identifier for this interface is 0x942e8b22 +interface ERC20 is Dummy, ERC165, ERC20Events { + /// @dev EVM selector for this function is: 0xdd62ed3e, + /// or in textual repr: allowance(address,address) + function allowance(address owner, address spender) external view returns (uint256); + + /// @dev EVM selector for this function is: 0x095ea7b3, + /// or in textual repr: approve(address,uint256) + function approve(address spender, uint256 amount) external returns (bool); + + /// @dev EVM selector for this function is: 0x70a08231, + /// or in textual repr: balanceOf(address) + function balanceOf(address owner) external view returns (uint256); + + /// @dev EVM selector for this function is: 0x313ce567, + /// or in textual repr: decimals() + function decimals() external view returns (uint8); + + /// @dev EVM selector for this function is: 0x06fdde03, + /// or in textual repr: name() + function name() external view returns (string memory); + + /// @dev EVM selector for this function is: 0x95d89b41, + /// or in textual repr: symbol() + function symbol() external view returns (string memory); + + /// @dev EVM selector for this function is: 0x18160ddd, + /// or in textual repr: totalSupply() + function totalSupply() external view returns (uint256); + + /// @dev EVM selector for this function is: 0xa9059cbb, + /// or in textual repr: transfer(address,uint256) + function transfer(address to, uint256 amount) external returns (bool); + + /// @dev EVM selector for this function is: 0x23b872dd, + /// or in textual repr: transferFrom(address,address,uint256) + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); +} + +interface UniqueNativeFungible is Dummy, ERC165, ERC20, ERC20UniqueExtensions {} --- /dev/null +++ b/js-packages/tests/eth/api/UniqueRefungible.sol @@ -0,0 +1,859 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +/// @dev common stubs holder +interface Dummy { + +} + +interface ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +/// @dev inlined interface +interface ERC721TokenEvent { + event TokenChanged(uint256 indexed tokenId); +} + +/// @title A contract that allows to set and delete token properties and change token property permissions. +/// @dev the ERC-165 identifier for this interface is 0xde0695c2 +interface TokenProperties is Dummy, ERC165, ERC721TokenEvent { + // /// @notice Set permissions for token property. + // /// @dev Throws error if `msg.sender` is not admin or owner of the collection. + // /// @param key Property key. + // /// @param isMutable Permission to mutate property. + // /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable. + // /// @param tokenOwner Permission to mutate property by token owner if property is mutable. + // /// @dev EVM selector for this function is: 0x222d97fa, + // /// or in textual repr: setTokenPropertyPermission(string,bool,bool,bool) + // function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) external; + + /// @notice Set permissions for token property. + /// @dev Throws error if `msg.sender` is not admin or owner of the collection. + /// @param permissions Permissions for keys. + /// @dev EVM selector for this function is: 0xbd92983a, + /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) + function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) external; + + /// @notice Get permissions for token properties. + /// @dev EVM selector for this function is: 0xf23d7790, + /// or in textual repr: tokenPropertyPermissions() + function tokenPropertyPermissions() external view returns (TokenPropertyPermission[] memory); + + // /// @notice Set token property value. + // /// @dev Throws error if `msg.sender` has no permission to edit the property. + // /// @param tokenId ID of the token. + // /// @param key Property key. + // /// @param value Property value. + // /// @dev EVM selector for this function is: 0x1752d67b, + // /// or in textual repr: setProperty(uint256,string,bytes) + // function setProperty(uint256 tokenId, string memory key, bytes memory value) external; + + /// @notice Set token properties value. + /// @dev Throws error if `msg.sender` has no permission to edit the property. + /// @param tokenId ID of the token. + /// @param properties settable properties + /// @dev EVM selector for this function is: 0x14ed3a6e, + /// or in textual repr: setProperties(uint256,(string,bytes)[]) + function setProperties(uint256 tokenId, Property[] memory properties) external; + + // /// @notice Delete token property value. + // /// @dev Throws error if `msg.sender` has no permission to edit the property. + // /// @param tokenId ID of the token. + // /// @param key Property key. + // /// @dev EVM selector for this function is: 0x066111d1, + // /// or in textual repr: deleteProperty(uint256,string) + // function deleteProperty(uint256 tokenId, string memory key) external; + + /// @notice Delete token properties value. + /// @dev Throws error if `msg.sender` has no permission to edit the property. + /// @param tokenId ID of the token. + /// @param keys Properties key. + /// @dev EVM selector for this function is: 0xc472d371, + /// or in textual repr: deleteProperties(uint256,string[]) + function deleteProperties(uint256 tokenId, string[] memory keys) external; + + /// @notice Get token property value. + /// @dev Throws error if key not found + /// @param tokenId ID of the token. + /// @param key Property key. + /// @return Property value bytes + /// @dev EVM selector for this function is: 0x7228c327, + /// or in textual repr: property(uint256,string) + function property(uint256 tokenId, string memory key) external view returns (bytes memory); +} + +/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). +struct Property { + string key; + bytes value; +} + +/// Ethereum representation of Token Property Permissions. +struct TokenPropertyPermission { + /// Token property key. + string key; + /// Token property permissions. + PropertyPermission[] permissions; +} + +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value. +struct PropertyPermission { + /// TokenPermission field. + TokenPermissionField code; + /// TokenPermission value. + bool value; +} + +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. +enum TokenPermissionField { + /// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] + Mutable, + /// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] + TokenOwner, + /// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] + CollectionAdmin +} + +/// @title A contract that allows you to work with collections. +/// @dev the ERC-165 identifier for this interface is 0xb34d97e9 +interface Collection is Dummy, ERC165 { + // /// Set collection property. + // /// + // /// @param key Property key. + // /// @param value Propery value. + // /// @dev EVM selector for this function is: 0x2f073f66, + // /// or in textual repr: setCollectionProperty(string,bytes) + // function setCollectionProperty(string memory key, bytes memory value) external; + + /// Set collection properties. + /// + /// @param properties Vector of properties key/value pair. + /// @dev EVM selector for this function is: 0x50b26b2a, + /// or in textual repr: setCollectionProperties((string,bytes)[]) + function setCollectionProperties(Property[] memory properties) external; + + // /// Delete collection property. + // /// + // /// @param key Property key. + // /// @dev EVM selector for this function is: 0x7b7debce, + // /// or in textual repr: deleteCollectionProperty(string) + // function deleteCollectionProperty(string memory key) external; + + /// Delete collection properties. + /// + /// @param keys Properties keys. + /// @dev EVM selector for this function is: 0xee206ee3, + /// or in textual repr: deleteCollectionProperties(string[]) + function deleteCollectionProperties(string[] memory keys) external; + + /// Get collection property. + /// + /// @dev Throws error if key not found. + /// + /// @param key Property key. + /// @return bytes The property corresponding to the key. + /// @dev EVM selector for this function is: 0xcf24fd6d, + /// or in textual repr: collectionProperty(string) + function collectionProperty(string memory key) external view returns (bytes memory); + + /// Get collection properties. + /// + /// @param keys Properties keys. Empty keys for all propertyes. + /// @return Vector of properties key/value pairs. + /// @dev EVM selector for this function is: 0x285fb8e6, + /// or in textual repr: collectionProperties(string[]) + function collectionProperties(string[] memory keys) external view returns (Property[] memory); + + // /// Set the sponsor of the collection. + // /// + // /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. + // /// + // /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract. + // /// @dev EVM selector for this function is: 0x7623402e, + // /// or in textual repr: setCollectionSponsor(address) + // function setCollectionSponsor(address sponsor) external; + + /// Set the sponsor of the collection. + /// + /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. + /// + /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. + /// @dev EVM selector for this function is: 0x84a1d5a8, + /// or in textual repr: setCollectionSponsorCross((address,uint256)) + function setCollectionSponsorCross(CrossAddress memory sponsor) external; + + /// Whether there is a pending sponsor. + /// @dev EVM selector for this function is: 0x058ac185, + /// or in textual repr: hasCollectionPendingSponsor() + function hasCollectionPendingSponsor() external view returns (bool); + + /// Collection sponsorship confirmation. + /// + /// @dev After setting the sponsor for the collection, it must be confirmed with this function. + /// @dev EVM selector for this function is: 0x3c50e97a, + /// or in textual repr: confirmCollectionSponsorship() + function confirmCollectionSponsorship() external; + + /// Remove collection sponsor. + /// @dev EVM selector for this function is: 0x6e0326a3, + /// or in textual repr: removeCollectionSponsor() + function removeCollectionSponsor() external; + + /// Get current sponsor. + /// + /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. + /// @dev EVM selector for this function is: 0x6ec0a9f1, + /// or in textual repr: collectionSponsor() + function collectionSponsor() external view returns (CrossAddress memory); + + /// Get current collection limits. + /// + /// @return Array of collection limits + /// @dev EVM selector for this function is: 0xf63bc572, + /// or in textual repr: collectionLimits() + function collectionLimits() external view returns (CollectionLimit[] memory); + + /// Set limits for the collection. + /// @dev Throws error if limit not found. + /// @param limit Some limit. + /// @dev EVM selector for this function is: 0x2316ee74, + /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) + function setCollectionLimit(CollectionLimit memory limit) external; + + /// Get contract address. + /// @dev EVM selector for this function is: 0xf6b4dfb4, + /// or in textual repr: contractAddress() + function contractAddress() external view returns (address); + + /// Add collection admin. + /// @param newAdmin Cross account administrator address. + /// @dev EVM selector for this function is: 0x859aa7d6, + /// or in textual repr: addCollectionAdminCross((address,uint256)) + function addCollectionAdminCross(CrossAddress memory newAdmin) external; + + /// Remove collection admin. + /// @param admin Cross account administrator address. + /// @dev EVM selector for this function is: 0x6c0cd173, + /// or in textual repr: removeCollectionAdminCross((address,uint256)) + function removeCollectionAdminCross(CrossAddress memory admin) external; + + // /// Add collection admin. + // /// @param newAdmin Address of the added administrator. + // /// @dev EVM selector for this function is: 0x92e462c7, + // /// or in textual repr: addCollectionAdmin(address) + // function addCollectionAdmin(address newAdmin) external; + + // /// Remove collection admin. + // /// + // /// @param admin Address of the removed administrator. + // /// @dev EVM selector for this function is: 0xfafd7b42, + // /// or in textual repr: removeCollectionAdmin(address) + // function removeCollectionAdmin(address admin) external; + + /// @dev EVM selector for this function is: 0x0b9f3890, + /// or in textual repr: setCollectionNesting((bool,bool,address[])) + function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external; + + // /// Toggle accessibility of collection nesting. + // /// + // /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled' + // /// @dev EVM selector for this function is: 0x112d4586, + // /// or in textual repr: setCollectionNesting(bool) + // function setCollectionNesting(bool enable) external; + + // /// Toggle accessibility of collection nesting. + // /// + // /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled' + // /// @param collections Addresses of collections that will be available for nesting. + // /// @dev EVM selector for this function is: 0x64872396, + // /// or in textual repr: setCollectionNesting(bool,address[]) + // function setCollectionNesting(bool enable, address[] memory collections) external; + + /// @dev EVM selector for this function is: 0x92c660a8, + /// or in textual repr: collectionNesting() + function collectionNesting() external view returns (CollectionNestingAndPermission memory); + + // /// Returns nesting for a collection + // /// @dev EVM selector for this function is: 0x22d25bfe, + // /// or in textual repr: collectionNestingRestrictedCollectionIds() + // function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory); + + // /// Returns permissions for a collection + // /// @dev EVM selector for this function is: 0x5b2eaf4b, + // /// or in textual repr: collectionNestingPermissions() + // function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory); + + /// Set the collection access method. + /// @param mode Access mode + /// @dev EVM selector for this function is: 0x41835d4c, + /// or in textual repr: setCollectionAccess(uint8) + function setCollectionAccess(AccessMode mode) external; + + /// Checks that user allowed to operate with collection. + /// + /// @param user User address to check. + /// @dev EVM selector for this function is: 0x91b6df49, + /// or in textual repr: allowlistedCross((address,uint256)) + function allowlistedCross(CrossAddress memory user) external view returns (bool); + + // /// Add the user to the allowed list. + // /// + // /// @param user Address of a trusted user. + // /// @dev EVM selector for this function is: 0x67844fe6, + // /// or in textual repr: addToCollectionAllowList(address) + // function addToCollectionAllowList(address user) external; + + /// Add user to allowed list. + /// + /// @param user User cross account address. + /// @dev EVM selector for this function is: 0xa0184a3a, + /// or in textual repr: addToCollectionAllowListCross((address,uint256)) + function addToCollectionAllowListCross(CrossAddress memory user) external; + + // /// Remove the user from the allowed list. + // /// + // /// @param user Address of a removed user. + // /// @dev EVM selector for this function is: 0x85c51acb, + // /// or in textual repr: removeFromCollectionAllowList(address) + // function removeFromCollectionAllowList(address user) external; + + /// Remove user from allowed list. + /// + /// @param user User cross account address. + /// @dev EVM selector for this function is: 0x09ba452a, + /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) + function removeFromCollectionAllowListCross(CrossAddress memory user) external; + + /// Switch permission for minting. + /// + /// @param mode Enable if "true". + /// @dev EVM selector for this function is: 0x00018e84, + /// or in textual repr: setCollectionMintMode(bool) + function setCollectionMintMode(bool mode) external; + + // /// Check that account is the owner or admin of the collection + // /// + // /// @param user account to verify + // /// @return "true" if account is the owner or admin + // /// @dev EVM selector for this function is: 0x9811b0c7, + // /// or in textual repr: isOwnerOrAdmin(address) + // function isOwnerOrAdmin(address user) external view returns (bool); + + /// Check that account is the owner or admin of the collection + /// + /// @param user User cross account to verify + /// @return "true" if account is the owner or admin + /// @dev EVM selector for this function is: 0x3e75a905, + /// or in textual repr: isOwnerOrAdminCross((address,uint256)) + function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool); + + /// Returns collection type + /// + /// @return `Fungible` or `NFT` or `ReFungible` + /// @dev EVM selector for this function is: 0xd34b55b8, + /// or in textual repr: uniqueCollectionType() + function uniqueCollectionType() external view returns (string memory); + + /// Get collection owner. + /// + /// @return Tuble with sponsor address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0xdf727d3b, + /// or in textual repr: collectionOwner() + function collectionOwner() external view returns (CrossAddress memory); + + // /// Changes collection owner to another account + // /// + // /// @dev Owner can be changed only by current owner + // /// @param newOwner new owner account + // /// @dev EVM selector for this function is: 0x4f53e226, + // /// or in textual repr: changeCollectionOwner(address) + // function changeCollectionOwner(address newOwner) external; + + /// Get collection administrators + /// + /// @return Vector of tuples with admins address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0x5813216b, + /// or in textual repr: collectionAdmins() + function collectionAdmins() external view returns (CrossAddress[] memory); + + /// Changes collection owner to another account + /// + /// @dev Owner can be changed only by current owner + /// @param newOwner new owner cross account + /// @dev EVM selector for this function is: 0x6496c497, + /// or in textual repr: changeCollectionOwnerCross((address,uint256)) + function changeCollectionOwnerCross(CrossAddress memory newOwner) external; +} + +/// Cross account struct +struct CrossAddress { + address eth; + uint256 sub; +} + +/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]). +enum AccessMode { + /// Access grant for owner and admins. Used as default. + Normal, + /// Like a [`Normal`](AccessMode::Normal) but also users in allow list. + AllowList +} + +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. +struct CollectionNestingPermission { + CollectionPermissionField field; + bool value; +} + +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. +enum CollectionPermissionField { + /// Owner of token can nest tokens under it. + TokenOwner, + /// Admin of token collection can nest tokens under token. + CollectionAdmin +} + +/// Nested collections. +struct CollectionNesting { + bool token_owner; + uint256[] ids; +} + +/// Nested collections and permissions +struct CollectionNestingAndPermission { + /// Owner of token can nest tokens under it. + bool token_owner; + /// Admin of token collection can nest tokens under token. + bool collection_admin; + /// If set - only tokens from specified collections can be nested. + address[] restricted; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. +struct CollectionLimit { + CollectionLimitField field; + OptionUint256 value; +} + +/// Optional value +struct OptionUint256 { + /// Shows the status of accessibility of value + bool status; + /// Actual value if `status` is true + uint256 value; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. +enum CollectionLimitField { + /// How many tokens can a user have on one account. + AccountTokenOwnership, + /// How many bytes of data are available for sponsorship. + SponsoredDataSize, + /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + SponsoredDataRateLimit, + /// How many tokens can be mined into this collection. + TokenLimit, + /// Timeouts for transfer sponsoring. + SponsorTransferTimeout, + /// Timeout for sponsoring an approval in passed blocks. + SponsorApproveTimeout, + /// Whether the collection owner of the collection can send tokens (which belong to other users). + OwnerCanTransfer, + /// Can the collection owner burn other people's tokens. + OwnerCanDestroy, + /// Is it possible to send tokens from this collection between users. + TransferEnabled +} + +/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension +/// @dev See https://eips.ethereum.org/EIPS/eip-721 +/// @dev the ERC-165 identifier for this interface is 0x5b5e139f +interface ERC721Metadata is Dummy, ERC165 { + // /// @notice A descriptive name for a collection of NFTs in this contract + // /// @dev real implementation of this function lies in `ERC721UniqueExtensions` + // /// @dev EVM selector for this function is: 0x06fdde03, + // /// or in textual repr: name() + // function name() external view returns (string memory); + + // /// @notice An abbreviated name for NFTs in this contract + // /// @dev real implementation of this function lies in `ERC721UniqueExtensions` + // /// @dev EVM selector for this function is: 0x95d89b41, + // /// or in textual repr: symbol() + // function symbol() external view returns (string memory); + + /// @notice A distinct Uniform Resource Identifier (URI) for a given asset. + /// + /// @dev If the token has a `url` property and it is not empty, it is returned. + /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`. + /// If the collection property `baseURI` is empty or absent, return "" (empty string) + /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix + /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings). + /// + /// @return token's const_metadata + /// @dev EVM selector for this function is: 0xc87b56dd, + /// or in textual repr: tokenURI(uint256) + function tokenURI(uint256 tokenId) external view returns (string memory); +} + +/// @title ERC721 Token that can be irreversibly burned (destroyed). +/// @dev the ERC-165 identifier for this interface is 0x42966c68 +interface ERC721Burnable is Dummy, ERC165 { + /// @notice Burns a specific ERC721 token. + /// @dev Throws unless `msg.sender` is the current RFT owner, or an authorized + /// operator of the current owner. + /// @param tokenId The RFT to approve + /// @dev EVM selector for this function is: 0x42966c68, + /// or in textual repr: burn(uint256) + function burn(uint256 tokenId) external; +} + +/// @title ERC721 minting logic. +/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6 +interface ERC721UniqueMintable is Dummy, ERC165 { + /// @notice Function to mint a token. + /// @param to The new owner + /// @return uint256 The id of the newly minted token + /// @dev EVM selector for this function is: 0x6a627842, + /// or in textual repr: mint(address) + function mint(address to) external returns (uint256); + + // /// @notice Function to mint a token. + // /// @dev `tokenId` should be obtained with `nextTokenId` method, + // /// unlike standard, you can't specify it manually + // /// @param to The new owner + // /// @param tokenId ID of the minted RFT + // /// @dev EVM selector for this function is: 0x40c10f19, + // /// or in textual repr: mint(address,uint256) + // function mint(address to, uint256 tokenId) external returns (bool); + + /// @notice Function to mint token with the given tokenUri. + /// @param to The new owner + /// @param tokenUri Token URI that would be stored in the NFT properties + /// @return uint256 The id of the newly minted token + /// @dev EVM selector for this function is: 0x45c17782, + /// or in textual repr: mintWithTokenURI(address,string) + function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256); + // /// @notice Function to mint token with the given tokenUri. + // /// @dev `tokenId` should be obtained with `nextTokenId` method, + // /// unlike standard, you can't specify it manually + // /// @param to The new owner + // /// @param tokenId ID of the minted RFT + // /// @param tokenUri Token URI that would be stored in the RFT properties + // /// @dev EVM selector for this function is: 0x50bb4e7f, + // /// or in textual repr: mintWithTokenURI(address,uint256,string) + // function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool); + +} + +/// @title Unique extensions for ERC721. +/// @dev the ERC-165 identifier for this interface is 0x4abaabdb +interface ERC721UniqueExtensions is Dummy, ERC165 { + /// @notice A descriptive name for a collection of NFTs in this contract + /// @dev EVM selector for this function is: 0x06fdde03, + /// or in textual repr: name() + function name() external view returns (string memory); + + /// @notice An abbreviated name for NFTs in this contract + /// @dev EVM selector for this function is: 0x95d89b41, + /// or in textual repr: symbol() + function symbol() external view returns (string memory); + + /// @notice A description for the collection. + /// @dev EVM selector for this function is: 0x7284e416, + /// or in textual repr: description() + function description() external view returns (string memory); + + // /// Returns the owner (in cross format) of the token. + // /// + // /// @param tokenId Id for the token. + // /// @dev EVM selector for this function is: 0x2b29dace, + // /// or in textual repr: crossOwnerOf(uint256) + // function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory); + + /// Returns the owner (in cross format) of the token. + /// + /// @param tokenId Id for the token. + /// @dev EVM selector for this function is: 0xcaa3a4d0, + /// or in textual repr: ownerOfCross(uint256) + function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory); + + /// @notice Count all RFTs assigned to an owner + /// @param owner An cross address for whom to query the balance + /// @return The number of RFTs owned by `owner`, possibly zero + /// @dev EVM selector for this function is: 0xec069398, + /// or in textual repr: balanceOfCross((address,uint256)) + function balanceOfCross(CrossAddress memory owner) external view returns (uint256); + + /// Returns the token properties. + /// + /// @param tokenId Id for the token. + /// @param keys Properties keys. Empty keys for all propertyes. + /// @return Vector of properties key/value pairs. + /// @dev EVM selector for this function is: 0xe07ede7e, + /// or in textual repr: properties(uint256,string[]) + function properties(uint256 tokenId, string[] memory keys) external view returns (Property[] memory); + + /// @notice Transfer ownership of an RFT + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid RFT. + /// Throws if RFT pieces have multiple owners. + /// @param to The new owner + /// @param tokenId The RFT to transfer + /// @dev EVM selector for this function is: 0xa9059cbb, + /// or in textual repr: transfer(address,uint256) + function transfer(address to, uint256 tokenId) external; + + /// @notice Transfer ownership of an RFT + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid RFT. + /// Throws if RFT pieces have multiple owners. + /// @param to The new owner + /// @param tokenId The RFT to transfer + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(CrossAddress memory to, uint256 tokenId) external; + + /// @notice Transfer ownership of an RFT + /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` + /// is the zero address. Throws if `tokenId` is not a valid RFT. + /// Throws if RFT pieces have multiple owners. + /// @param to The new owner + /// @param tokenId The RFT to transfer + /// @dev EVM selector for this function is: 0xd5cf430b, + /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) + function transferFromCross( + CrossAddress memory from, + CrossAddress memory to, + uint256 tokenId + ) external; + + // /// @notice Burns a specific ERC721 token. + // /// @dev Throws unless `msg.sender` is the current owner or an authorized + // /// operator for this RFT. Throws if `from` is not the current owner. Throws + // /// if `to` is the zero address. Throws if `tokenId` is not a valid RFT. + // /// Throws if RFT pieces have multiple owners. + // /// @param from The current owner of the RFT + // /// @param tokenId The RFT to transfer + // /// @dev EVM selector for this function is: 0x79cc6790, + // /// or in textual repr: burnFrom(address,uint256) + // function burnFrom(address from, uint256 tokenId) external; + + /// @notice Burns a specific ERC721 token. + /// @dev Throws unless `msg.sender` is the current owner or an authorized + /// operator for this RFT. Throws if `from` is not the current owner. Throws + /// if `to` is the zero address. Throws if `tokenId` is not a valid RFT. + /// Throws if RFT pieces have multiple owners. + /// @param from The current owner of the RFT + /// @param tokenId The RFT to transfer + /// @dev EVM selector for this function is: 0xbb2f5a58, + /// or in textual repr: burnFromCross((address,uint256),uint256) + function burnFromCross(CrossAddress memory from, uint256 tokenId) external; + + /// @notice Returns next free RFT ID. + /// @dev EVM selector for this function is: 0x75794a3c, + /// or in textual repr: nextTokenId() + function nextTokenId() external view returns (uint256); + + // /// @notice Function to mint multiple tokens. + // /// @dev `tokenIds` should be an array of consecutive numbers and first number + // /// should be obtained with `nextTokenId` method + // /// @param to The new owner + // /// @param tokenIds IDs of the minted RFTs + // /// @dev EVM selector for this function is: 0x44a9945e, + // /// or in textual repr: mintBulk(address,uint256[]) + // function mintBulk(address to, uint256[] memory tokenIds) external returns (bool); + + /// @notice Function to mint a token. + /// @param tokenProperties Properties of minted token + /// @dev EVM selector for this function is: 0xdf7a5db7, + /// or in textual repr: mintBulkCross((((address,uint256),uint128)[],(string,bytes)[])[]) + function mintBulkCross(MintTokenData[] memory tokenProperties) external returns (bool); + + // /// @notice Function to mint multiple tokens with the given tokenUris. + // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive + // /// numbers and first number should be obtained with `nextTokenId` method + // /// @param to The new owner + // /// @param tokens array of pairs of token ID and token URI for minted tokens + // /// @dev EVM selector for this function is: 0x36543006, + // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) + // function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) external returns (bool); + + /// @notice Function to mint a token. + /// @param to The new owner crossAccountId + /// @param properties Properties of minted token + /// @return uint256 The id of the newly minted token + /// @dev EVM selector for this function is: 0xb904db03, + /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) + function mintCross(CrossAddress memory to, Property[] memory properties) external returns (uint256); + + /// Returns EVM address for refungible token + /// + /// @param token ID of the token + /// @dev EVM selector for this function is: 0xab76fac6, + /// or in textual repr: tokenContractAddress(uint256) + function tokenContractAddress(uint256 token) external view returns (address); + + /// @notice Returns collection helper contract address + /// @dev EVM selector for this function is: 0x1896cce6, + /// or in textual repr: collectionHelperAddress() + function collectionHelperAddress() external view returns (address); +} + +/// Data for creation token with uri. +struct TokenUri { + /// Id of new token. + uint256 id; + /// Uri of new token. + string uri; +} + +/// Token minting parameters +struct MintTokenData { + /// Minted token owner and number of pieces + OwnerPieces[] owners; + /// Minted token properties + Property[] properties; +} + +/// Token minting parameters +struct OwnerPieces { + /// Minted token owner + CrossAddress owner; + /// Number of token pieces + uint128 pieces; +} + +/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension +/// @dev See https://eips.ethereum.org/EIPS/eip-721 +/// @dev the ERC-165 identifier for this interface is 0x780e9d63 +interface ERC721Enumerable is Dummy, ERC165 { + /// @notice Enumerate valid RFTs + /// @param index A counter less than `totalSupply()` + /// @return The token identifier for the `index`th NFT, + /// (sort order not specified) + /// @dev EVM selector for this function is: 0x4f6ccce7, + /// or in textual repr: tokenByIndex(uint256) + function tokenByIndex(uint256 index) external view returns (uint256); + + /// Not implemented + /// @dev EVM selector for this function is: 0x2f745c59, + /// or in textual repr: tokenOfOwnerByIndex(address,uint256) + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + + /// @notice Count RFTs tracked by this contract + /// @return A count of valid RFTs tracked by this contract, where each one of + /// them has an assigned and queryable owner not equal to the zero address + /// @dev EVM selector for this function is: 0x18160ddd, + /// or in textual repr: totalSupply() + function totalSupply() external view returns (uint256); +} + +/// @dev inlined interface +interface ERC721Events { + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); +} + +/// @title ERC-721 Non-Fungible Token Standard +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md +/// @dev the ERC-165 identifier for this interface is 0x80ac58cd +interface ERC721 is Dummy, ERC165, ERC721Events { + /// @notice Count all RFTs assigned to an owner + /// @dev RFTs assigned to the zero address are considered invalid, and this + /// function throws for queries about the zero address. + /// @param owner An address for whom to query the balance + /// @return The number of RFTs owned by `owner`, possibly zero + /// @dev EVM selector for this function is: 0x70a08231, + /// or in textual repr: balanceOf(address) + function balanceOf(address owner) external view returns (uint256); + + /// @notice Find the owner of an RFT + /// @dev RFTs assigned to zero address are considered invalid, and queries + /// about them do throw. + /// Returns special 0xffffffffffffffffffffffffffffffffffffffff address for + /// the tokens that are partially owned. + /// @param tokenId The identifier for an RFT + /// @return The address of the owner of the RFT + /// @dev EVM selector for this function is: 0x6352211e, + /// or in textual repr: ownerOf(uint256) + function ownerOf(uint256 tokenId) external view returns (address); + + /// @dev Not implemented + /// @dev EVM selector for this function is: 0xb88d4fde, + /// or in textual repr: safeTransferFrom(address,address,uint256,bytes) + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes memory data + ) external; + + /// @dev Not implemented + /// @dev EVM selector for this function is: 0x42842e0e, + /// or in textual repr: safeTransferFrom(address,address,uint256) + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /// @notice Transfer ownership of an RFT -- THE CALLER IS RESPONSIBLE + /// TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE + /// THEY MAY BE PERMANENTLY LOST + /// @dev Throws unless `msg.sender` is the current owner or an authorized + /// operator for this RFT. Throws if `from` is not the current owner. Throws + /// if `to` is the zero address. Throws if `tokenId` is not a valid RFT. + /// Throws if RFT pieces have multiple owners. + /// @param from The current owner of the NFT + /// @param to The new owner + /// @param tokenId The NFT to transfer + /// @dev EVM selector for this function is: 0x23b872dd, + /// or in textual repr: transferFrom(address,address,uint256) + function transferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /// @dev Not implemented + /// @dev EVM selector for this function is: 0x095ea7b3, + /// or in textual repr: approve(address,uint256) + function approve(address approved, uint256 tokenId) external; + + /// @notice Sets or unsets the approval of a given operator. + /// The `operator` is allowed to transfer all token pieces of the `caller` on their behalf. + /// @param operator Operator + /// @param approved Should operator status be granted or revoked? + /// @dev EVM selector for this function is: 0xa22cb465, + /// or in textual repr: setApprovalForAll(address,bool) + function setApprovalForAll(address operator, bool approved) external; + + /// @dev Not implemented + /// @dev EVM selector for this function is: 0x081812fc, + /// or in textual repr: getApproved(uint256) + function getApproved(uint256 tokenId) external view returns (address); + + /// @notice Tells whether the given `owner` approves the `operator`. + /// @dev EVM selector for this function is: 0xe985e9c5, + /// or in textual repr: isApprovedForAll(address,address) + function isApprovedForAll(address owner, address operator) external view returns (bool); +} + +interface UniqueRefungible is + Dummy, + ERC165, + ERC721, + ERC721Enumerable, + ERC721UniqueExtensions, + ERC721UniqueMintable, + ERC721Burnable, + ERC721Metadata, + Collection, + TokenProperties +{} --- /dev/null +++ b/js-packages/tests/eth/api/UniqueRefungibleToken.sol @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: OTHER +// This code is automatically generated + +pragma solidity >=0.8.0 <0.9.0; + +/// @dev common stubs holder +interface Dummy { + +} + +interface ERC165 is Dummy { + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +/// @dev the ERC-165 identifier for this interface is 0x5755c3f2 +interface ERC1633 is Dummy, ERC165 { + /// @dev EVM selector for this function is: 0x80a54001, + /// or in textual repr: parentToken() + function parentToken() external view returns (address); + + /// @dev EVM selector for this function is: 0xd7f083f3, + /// or in textual repr: parentTokenId() + function parentTokenId() external view returns (uint256); +} + +/// @dev the ERC-165 identifier for this interface is 0xedd3a564 +interface ERC20UniqueExtensions is Dummy, ERC165 { + /// @dev Function to check the amount of tokens that an owner allowed to a spender. + /// @param owner crossAddress The address which owns the funds. + /// @param spender crossAddress The address which will spend the funds. + /// @return A uint256 specifying the amount of tokens still available for the spender. + /// @dev EVM selector for this function is: 0xe0af4bd7, + /// or in textual repr: allowanceCross((address,uint256),(address,uint256)) + function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) external view returns (uint256); + + // /// @dev Function that burns an amount of the token of a given account, + // /// deducting from the sender's allowance for said account. + // /// @param from The account whose tokens will be burnt. + // /// @param amount The amount that will be burnt. + // /// @dev EVM selector for this function is: 0x79cc6790, + // /// or in textual repr: burnFrom(address,uint256) + // function burnFrom(address from, uint256 amount) external returns (bool); + + /// @dev Function that burns an amount of the token of a given account, + /// deducting from the sender's allowance for said account. + /// @param from The account whose tokens will be burnt. + /// @param amount The amount that will be burnt. + /// @dev EVM selector for this function is: 0xbb2f5a58, + /// or in textual repr: burnFromCross((address,uint256),uint256) + function burnFromCross(CrossAddress memory from, uint256 amount) external returns (bool); + + /// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`. + /// Beware that changing an allowance with this method brings the risk that someone may use both the old + /// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + /// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + /// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + /// @param spender The crossaccount which will spend the funds. + /// @param amount The amount of tokens to be spent. + /// @dev EVM selector for this function is: 0x0ecd0ab0, + /// or in textual repr: approveCross((address,uint256),uint256) + function approveCross(CrossAddress memory spender, uint256 amount) external returns (bool); + + /// @notice Balance of account + /// @param owner An cross address for whom to query the balance + /// @return The number of fingibles owned by `owner`, possibly zero + /// @dev EVM selector for this function is: 0xec069398, + /// or in textual repr: balanceOfCross((address,uint256)) + function balanceOfCross(CrossAddress memory owner) external view returns (uint256); + + /// @dev Function that changes total amount of the tokens. + /// Throws if `msg.sender` doesn't owns all of the tokens. + /// @param amount New total amount of the tokens. + /// @dev EVM selector for this function is: 0xd2418ca7, + /// or in textual repr: repartition(uint256) + function repartition(uint256 amount) external returns (bool); + + /// @dev Transfer token for a specified address + /// @param to The crossaccount to transfer to. + /// @param amount The amount to be transferred. + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(CrossAddress memory to, uint256 amount) external returns (bool); + + /// @dev Transfer tokens from one address to another + /// @param from The address which you want to send tokens from + /// @param to The address which you want to transfer to + /// @param amount the amount of tokens to be transferred + /// @dev EVM selector for this function is: 0xd5cf430b, + /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) + function transferFromCross( + CrossAddress memory from, + CrossAddress memory to, + uint256 amount + ) external returns (bool); +} + +/// Cross account struct +struct CrossAddress { + address eth; + uint256 sub; +} + +/// @dev inlined interface +interface ERC20Events { + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +/// @title Standard ERC20 token +/// +/// @dev Implementation of the basic standard token. +/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md +/// @dev the ERC-165 identifier for this interface is 0x942e8b22 +interface ERC20 is Dummy, ERC165, ERC20Events { + /// @return the name of the token. + /// @dev EVM selector for this function is: 0x06fdde03, + /// or in textual repr: name() + function name() external view returns (string memory); + + /// @return the symbol of the token. + /// @dev EVM selector for this function is: 0x95d89b41, + /// or in textual repr: symbol() + function symbol() external view returns (string memory); + + /// @dev Total number of tokens in existence + /// @dev EVM selector for this function is: 0x18160ddd, + /// or in textual repr: totalSupply() + function totalSupply() external view returns (uint256); + + /// @dev Not supported + /// @dev EVM selector for this function is: 0x313ce567, + /// or in textual repr: decimals() + function decimals() external view returns (uint8); + + /// @dev Gets the balance of the specified address. + /// @param owner The address to query the balance of. + /// @return An uint256 representing the amount owned by the passed address. + /// @dev EVM selector for this function is: 0x70a08231, + /// or in textual repr: balanceOf(address) + function balanceOf(address owner) external view returns (uint256); + + /// @dev Transfer token for a specified address + /// @param to The address to transfer to. + /// @param amount The amount to be transferred. + /// @dev EVM selector for this function is: 0xa9059cbb, + /// or in textual repr: transfer(address,uint256) + function transfer(address to, uint256 amount) external returns (bool); + + /// @dev Transfer tokens from one address to another + /// @param from address The address which you want to send tokens from + /// @param to address The address which you want to transfer to + /// @param amount uint256 the amount of tokens to be transferred + /// @dev EVM selector for this function is: 0x23b872dd, + /// or in textual repr: transferFrom(address,address,uint256) + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); + + /// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`. + /// Beware that changing an allowance with this method brings the risk that someone may use both the old + /// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + /// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + /// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + /// @param spender The address which will spend the funds. + /// @param amount The amount of tokens to be spent. + /// @dev EVM selector for this function is: 0x095ea7b3, + /// or in textual repr: approve(address,uint256) + function approve(address spender, uint256 amount) external returns (bool); + + /// @dev Function to check the amount of tokens that an owner allowed to a spender. + /// @param owner address The address which owns the funds. + /// @param spender address The address which will spend the funds. + /// @return A uint256 specifying the amount of tokens still available for the spender. + /// @dev EVM selector for this function is: 0xdd62ed3e, + /// or in textual repr: allowance(address,address) + function allowance(address owner, address spender) external view returns (uint256); +} + +interface UniqueRefungibleToken is Dummy, ERC165, ERC20, ERC20UniqueExtensions, ERC1633 {} --- /dev/null +++ b/js-packages/tests/eth/base.test.ts @@ -0,0 +1,127 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; +import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; + + +describe('Contract calls', () => { + let donor: IKeyringPair; + + before(async function () { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Call of simple contract fee is less than 0.2 UNQ', async ({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const flipper = await helper.eth.deployFlipper(deployer); + + const cost = await helper.eth.calculateFee({Ethereum: deployer}, () => flipper.methods.flip().send({from: deployer})); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))).to.be.true; + }); + + itEth('Balance transfer fee is less than 0.2 UNQ', async ({helper}) => { + const userA = await helper.eth.createAccountWithBalance(donor); + const userB = helper.eth.createAccount(); + const cost = await helper.eth.calculateFee({Ethereum: userA}, () => helper.getWeb3().eth.sendTransaction({ + from: userA, + to: userB, + value: '1000000', + gas: helper.eth.DEFAULT_GAS, + })); + const balanceB = await helper.balance.getEthereum(userB); + expect(cost - balanceB < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))).to.be.true; + }); + + itEth('NFT transfer is close to 0.15 UNQ', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const [alice] = await helper.arrange.createAccounts([10n], donor); + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const {tokenId} = await collection.mintToken(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft', caller); + + const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, tokenId).send(caller)); + + const fee = Number(cost) / Number(helper.balance.getOneTokenNominal()); + const expectedFee = 0.15; + const tolerance = 0.001; + + expect(Math.abs(fee - expectedFee)).to.be.lessThan(tolerance); + }); +}); + +describe('ERC165 tests', () => { + // https://eips.ethereum.org/EIPS/eip-165 + + let erc721MetadataCompatibleNftCollectionId: number; + let simpleNftCollectionId: number; + let minter: string; + + const BASE_URI = 'base/'; + + async function checkInterface(helper: EthUniqueHelper, interfaceId: string, simpleResult: boolean, compatibleResult: boolean) { + const simple = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(simpleNftCollectionId), 'nft', minter); + const compatible = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(erc721MetadataCompatibleNftCollectionId), 'nft', minter); + + expect(await simple.methods.supportsInterface(interfaceId).call()).to.equal(simpleResult, `empty (not ERC721Metadata compatible) NFT collection returns not ${simpleResult}`); + expect(await compatible.methods.supportsInterface(interfaceId).call()).to.equal(compatibleResult, `ERC721Metadata compatible NFT collection returns not ${compatibleResult}`); + } + + before(async () => { + await usingEthPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + const [alice] = await helper.arrange.createAccounts([10n], donor); + ({collectionId: simpleNftCollectionId} = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'})); + minter = await helper.eth.createAccountWithBalance(donor); + ({collectionId: erc721MetadataCompatibleNftCollectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(minter, 'n', 'd', 'p', BASE_URI)); + }); + }); + + itEth('nonexistent interfaceID - 0xffffffff - always false', async ({helper}) => { + await checkInterface(helper, '0xffffffff', false, false); + }); + + itEth('ERC721 - 0x780e9d63 - support', async ({helper}) => { + await checkInterface(helper, '0x780e9d63', true, true); + }); + + itEth('ERC721Metadata - 0x5b5e139f - support', async ({helper}) => { + await checkInterface(helper, '0x5b5e139f', false, true); + }); + + itEth('ERC721Enumerable - 0x780e9d63 - support', async ({helper}) => { + await checkInterface(helper, '0x780e9d63', true, true); + }); + + itEth.skip('ERC721UniqueExtensions support', async ({helper}) => { + await checkInterface(helper, '0xb74c26b7', true, true); + }); + + itEth('ERC721Burnable - 0x42966c68 - support', async ({helper}) => { + await checkInterface(helper, '0x42966c68', true, true); + }); + + itEth('ERC165 - 0x01ffc9a7 - support', async ({helper}) => { + await checkInterface(helper, '0x01ffc9a7', true, true); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/collectionAdmin.test.ts @@ -0,0 +1,469 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect} from 'chai'; +import {Pallets} from '../util/index.js'; +import type {IEthCrossAccountId} from '@unique/playgrounds/types.js'; +import {usingEthPlaygrounds, itEth} from './util/index.js'; +import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; +import {CreateCollectionData} from './util/playgrounds/types.js'; + +async function recordEthFee(helper: EthUniqueHelper, userAddress: string, call: () => Promise) { + const before = await helper.balance.getSubstrate(helper.address.ethToSubstrate(userAddress)); + await call(); + await helper.wait.newBlocks(1); + const after = await helper.balance.getSubstrate(helper.address.ethToSubstrate(userAddress)); + + expect(after < before).to.be.true; + + return before - after; +} + +describe('Add collection admins', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {mode: 'ft' as const, requiredPallets: []}, + ].map(testCase => { + itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => { + // arrange + const owner = await helper.eth.createAccountWithBalance(donor); + const adminSub = await privateKey('//admin2'); + const adminEth = helper.eth.createAccount().toLowerCase(); + + const adminDeprecated = helper.eth.createAccount().toLowerCase(); + const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); + const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); + + const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true); + + // Check isOwnerOrAdminCross returns false: + expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.false; + expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.false; + expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.false; + + // Soft-deprecated: can addCollectionAdmin + await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send(); + // Can addCollectionAdminCross for substrate and ethereum address + await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send(); + await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send(); + + // 1. Expect api.rpc.unique.adminlist returns admins: + const adminListRpc = await helper.collection.getAdmins(collectionId); + expect(adminListRpc).to.has.length(3); + expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}, {Ethereum: adminDeprecated}]); + + // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist + let adminListEth = await collectionEvm.methods.collectionAdmins().call(); + adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element)); + expect(adminListRpc).to.be.like(adminListEth); + + // 3. check isOwnerOrAdminCross returns true: + expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true; + expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true; + expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.true; + }); + }); + + itEth('cross account admin can mint', async ({helper}) => { + // arrange: create collection and accounts + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', 'uri'); + const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase(); + const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); + const [adminSub] = await helper.arrange.createAccounts([100n], donor); + const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + + // cannot mint while not admin + await expect(collectionEvm.methods.mint(owner).send({from: adminEth})).to.be.rejected; + await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/); + + // admin (sub and eth) can mint token: + await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send(); + await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send(); + await collectionEvm.methods.mint(owner).send({from: adminEth}); + await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}}); + + expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2); + }); + + itEth('cannot add invalid cross account admin', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const [admin] = await helper.arrange.createAccounts([100n, 100n], donor); + + const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + const adminCross = { + eth: helper.address.substrateToEth(admin.address), + sub: admin.addressRaw, + }; + await expect(collectionEvm.methods.addCollectionAdminCross(adminCross).send()).to.be.rejected; + }); + + itEth('can verify owner with methods.isOwnerOrAdmin[Cross]', async ({helper, privateKey}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const adminDeprecated = helper.eth.createAccount(); + const admin1Cross = helper.ethCrossAccount.fromKeyringPair(await privateKey('admin')); + const admin2Cross = helper.ethCrossAccount.fromAddress(helper.address.substrateToEth((await privateKey('admin3')).address)); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + + // Soft-deprecated: + expect(await collectionEvm.methods.isOwnerOrAdmin(adminDeprecated).call()).to.be.false; + expect(await collectionEvm.methods.isOwnerOrAdminCross(admin1Cross).call()).to.be.false; + expect(await collectionEvm.methods.isOwnerOrAdminCross(admin2Cross).call()).to.be.false; + + await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send(); + await collectionEvm.methods.addCollectionAdminCross(admin1Cross).send(); + await collectionEvm.methods.addCollectionAdminCross(admin2Cross).send(); + + // Soft-deprecated: isOwnerOrAdmin returns true + expect(await collectionEvm.methods.isOwnerOrAdmin(adminDeprecated).call()).to.be.true; + // Expect isOwnerOrAdminCross return true + expect(await collectionEvm.methods.isOwnerOrAdminCross(admin1Cross).call()).to.be.true; + expect(await collectionEvm.methods.isOwnerOrAdminCross(admin2Cross).call()).to.be.true; + }); + + // Soft-deprecated + itEth('(!negative tests!) Add admin by ADMIN is not allowed', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const admin = await helper.eth.createAccountWithBalance(donor); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + await collectionEvm.methods.addCollectionAdmin(admin).send(); + + const user = helper.eth.createAccount(); + await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: admin})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); + expect(adminList.length).to.be.eq(1); + expect(adminList[0].asEthereum.toString().toLocaleLowerCase()) + .to.be.eq(admin.toLocaleLowerCase()); + }); + + // Soft-deprecated + itEth('(!negative tests!) Add admin by USER is not allowed', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const notAdmin = await helper.eth.createAccountWithBalance(donor); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + + const user = helper.eth.createAccount(); + await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: notAdmin})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); + expect(adminList.length).to.be.eq(0); + }); + + itEth('(!negative tests!) Add [cross] admin by ADMIN is not allowed', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const [admin, notAdmin] = await helper.arrange.createAccounts([10n, 10n], donor); + const adminCross = helper.ethCrossAccount.fromKeyringPair(admin); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + await collectionEvm.methods.addCollectionAdminCross(adminCross).send(); + + const notAdminCross = helper.ethCrossAccount.fromKeyringPair(notAdmin); + await expect(collectionEvm.methods.addCollectionAdminCross(notAdminCross).call({from: adminCross.eth})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); + expect(adminList.length).to.be.eq(1); + + const admin0Cross = helper.ethCrossAccount.fromKeyringPair(adminList[0]); + expect(admin0Cross.eth.toLocaleLowerCase()) + .to.be.eq(adminCross.eth.toLocaleLowerCase()); + }); + + itEth('(!negative tests!) Add [cross] admin by USER is not allowed', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const notAdmin0 = await helper.eth.createAccountWithBalance(donor); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + const [notAdmin1] = await helper.arrange.createAccounts([10n], donor); + const notAdmin1Cross = helper.ethCrossAccount.fromKeyringPair(notAdmin1); + await expect(collectionEvm.methods.addCollectionAdminCross(notAdmin1Cross).call({from: notAdmin0})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); + expect(adminList.length).to.be.eq(0); + }); +}); + +describe('Remove collection admins', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + // Soft-deprecated + itEth('Remove admin by owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const newAdmin = helper.eth.createAccount(); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + await collectionEvm.methods.addCollectionAdmin(newAdmin).send(); + + { + const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); + expect(adminList.length).to.be.eq(1); + expect(adminList[0].asEthereum.toString().toLocaleLowerCase()) + .to.be.eq(newAdmin.toLocaleLowerCase()); + } + + await collectionEvm.methods.removeCollectionAdmin(newAdmin).send(); + const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); + expect(adminList.length).to.be.eq(0); + }); + + itEth('Remove [cross] admin by owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const [adminSub] = await helper.arrange.createAccounts([10n], donor); + const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase(); + const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); + const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); + + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send(); + await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send(); + + { + const adminList = await helper.collection.getAdmins(collectionId); + expect(adminList).to.deep.include({Substrate: adminSub.address}); + expect(adminList).to.deep.include({Ethereum: adminEth}); + } + + await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send(); + await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send(); + const adminList = await helper.collection.getAdmins(collectionId); + expect(adminList.length).to.be.eq(0); + + // Non admin cannot mint: + await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/); + await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected; + }); + + // Soft-deprecated + itEth('(!negative tests!) Remove admin by ADMIN is not allowed', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + + const admin0 = await helper.eth.createAccountWithBalance(donor); + await collectionEvm.methods.addCollectionAdmin(admin0).send(); + const admin1 = await helper.eth.createAccountWithBalance(donor); + await collectionEvm.methods.addCollectionAdmin(admin1).send(); + + await expect(collectionEvm.methods.removeCollectionAdmin(admin1).call({from: admin0})) + .to.be.rejectedWith('NoPermission'); + { + const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); + expect(adminList.length).to.be.eq(2); + expect(adminList.toString().toLocaleLowerCase()) + .to.be.deep.contains(admin0.toLocaleLowerCase()) + .to.be.deep.contains(admin1.toLocaleLowerCase()); + } + }); + + // Soft-deprecated + itEth('(!negative tests!) Remove admin by USER is not allowed', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + + const admin = await helper.eth.createAccountWithBalance(donor); + await collectionEvm.methods.addCollectionAdmin(admin).send(); + const notAdmin = helper.eth.createAccount(); + + await expect(collectionEvm.methods.removeCollectionAdmin(admin).call({from: notAdmin})) + .to.be.rejectedWith('NoPermission'); + { + const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); + expect(adminList[0].asEthereum.toString().toLocaleLowerCase()) + .to.be.eq(admin.toLocaleLowerCase()); + expect(adminList.length).to.be.eq(1); + } + }); + + itEth('(!negative tests!) Remove [cross] admin by ADMIN is not allowed', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const [admin1] = await helper.arrange.createAccounts([10n], donor); + const admin1Cross = helper.ethCrossAccount.fromKeyringPair(admin1); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + await collectionEvm.methods.addCollectionAdminCross(admin1Cross).send(); + + const [admin2] = await helper.arrange.createAccounts([10n], donor); + const admin2Cross = helper.ethCrossAccount.fromKeyringPair(admin2); + await collectionEvm.methods.addCollectionAdminCross(admin2Cross).send(); + + await expect(collectionEvm.methods.removeCollectionAdminCross(admin1Cross).call({from: admin2Cross.eth})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); + expect(adminList.length).to.be.eq(2); + expect(adminList.toString().toLocaleLowerCase()) + .to.be.deep.contains(admin1.address.toLocaleLowerCase()) + .to.be.deep.contains(admin2.address.toLocaleLowerCase()); + }); + + itEth('(!negative tests!) Remove [cross] admin by USER is not allowed', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const [adminSub] = await helper.arrange.createAccounts([10n], donor); + const adminSubCross = helper.ethCrossAccount.fromKeyringPair(adminSub); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + await collectionEvm.methods.addCollectionAdminCross(adminSubCross).send(); + const notAdminEth = await helper.eth.createAccountWithBalance(donor); + + await expect(collectionEvm.methods.removeCollectionAdminCross(adminSubCross).call({from: notAdminEth})) + .to.be.rejectedWith('NoPermission'); + + const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); + expect(adminList.length).to.be.eq(1); + expect(adminList[0].asSubstrate.toString().toLocaleLowerCase()) + .to.be.eq(adminSub.address.toLocaleLowerCase()); + }); +}); + +// Soft-deprecated +describe('Change owner tests', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Change owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const newOwner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + + await collectionEvm.methods.changeCollectionOwner(newOwner).send(); + + expect(await collectionEvm.methods.isOwnerOrAdmin(owner).call()).to.be.false; + expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.true; + }); + + itEth('change owner call fee', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const newOwner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.changeCollectionOwner(newOwner).send()); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + expect(cost > 0); + }); + + itEth('(!negative tests!) call setOwner by non owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const newOwner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + + await expect(collectionEvm.methods.changeCollectionOwner(newOwner).send({from: newOwner})).to.be.rejected; + expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.false; + }); +}); + +describe('Change substrate owner tests', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Change owner [cross]', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const ownerEth = await helper.eth.createAccountWithBalance(donor); + const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth); + const [ownerSub] = await helper.arrange.createAccounts([10n], donor); + const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub); + + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.false; + + // Can set ethereum owner: + await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossEth).send({from: owner}); + expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossEth).call()).to.be.true; + expect(await helper.collection.getData(collectionId)) + .to.have.property('normalizedOwner').that.is.eq(helper.address.ethToSubstrate(ownerEth)); + + // Can set Substrate owner: + await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossSub).send({from: ownerEth}); + expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.true; + expect(await helper.collection.getData(collectionId)) + .to.have.property('normalizedOwner').that.is.eq(helper.address.normalizeSubstrate(ownerSub.address)); + }); + + itEth.skip('change owner call fee', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const [newOwner] = await helper.arrange.createAccounts([10n], donor); + const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.setOwnerSubstrate(newOwner.addressRaw).send()); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + expect(cost > 0); + }); + + itEth('(!negative tests!) call setOwner by non owner [cross]', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const otherReceiver = await helper.eth.createAccountWithBalance(donor); + const [newOwner] = await helper.arrange.createAccounts([10n], donor); + const newOwnerCross = helper.ethCrossAccount.fromKeyringPair(newOwner); + const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + await expect(collectionEvm.methods.changeCollectionOwnerCross(newOwnerCross).send({from: otherReceiver})).to.be.rejected; + expect(await collectionEvm.methods.isOwnerOrAdminCross(newOwnerCross).call()).to.be.false; + }); +}); --- /dev/null +++ b/js-packages/tests/eth/collectionHelperAddress.test.ts @@ -0,0 +1,70 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {COLLECTION_HELPER, Pallets} from '../util/index.js'; + +describe('[eth]CollectionHelperAddress test: ERC20/ERC721 ', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('NFT', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionAddress: nftCollectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC'); + const nftCollection = await helper.ethNativeContract.collection(nftCollectionAddress, 'nft', owner); + + expect((await nftCollection.methods.collectionHelperAddress().call()) + .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER); + }); + + itEth.ifWithPallets('RFT ', [Pallets.ReFungible], async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionAddress: rftCollectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC'); + + const rftCollection = await helper.ethNativeContract.collection(rftCollectionAddress, 'rft', owner); + expect((await rftCollection.methods.collectionHelperAddress().call()) + .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER); + }); + + itEth('FT', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', 18, 'absolutely anything', 'ROC'); + const collection = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + expect((await collection.methods.collectionHelperAddress().call()) + .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER); + }); + + itEth('[collectionHelpers] convert collectionId into address', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionId = 7; + const collectionAddress = helper.ethAddress.fromCollectionId(collectionId); + const helperContract = await helper.ethNativeContract.collectionHelpers(owner); + + expect(await helperContract.methods.collectionAddress(collectionId).call()).to.be.equal(collectionAddress); + expect(parseInt(await helperContract.methods.collectionId(collectionAddress).call())).to.be.equal(collectionId); + }); + +}); --- /dev/null +++ b/js-packages/tests/eth/collectionLimits.test.ts @@ -0,0 +1,145 @@ +import type {IKeyringPair} from '@polkadot/types/types'; +import {Pallets} from '../util/index.js'; +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import {CollectionLimitField, CreateCollectionData} from './util/playgrounds/types.js'; + + +describe('Can set collection limits', () => { + let donor: IKeyringPair; + + before(async () => { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + [ + {case: 'nft' as const}, + {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {case: 'ft' as const}, + ].map(testCase => + itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'FLO', testCase.case, 18)).send(); + const limits = { + accountTokenOwnershipLimit: 1000, + sponsoredDataSize: 1024, + sponsoredDataRateLimit: 30, + tokenLimit: 1000000, + sponsorTransferTimeout: 6, + sponsorApproveTimeout: 6, + ownerCanTransfer: 1, + ownerCanDestroy: 0, + transfersEnabled: 0, + }; + + const expectedLimits = { + accountTokenOwnershipLimit: 1000, + sponsoredDataSize: 1024, + sponsoredDataRateLimit: {blocks: 30}, + tokenLimit: 1000000, + sponsorTransferTimeout: 6, + sponsorApproveTimeout: 6, + ownerCanTransfer: true, + ownerCanDestroy: false, + transfersEnabled: false, + }; + + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: limits.accountTokenOwnershipLimit}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataSize, value: {status: true, value: limits.sponsoredDataSize}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: limits.sponsoredDataRateLimit}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.TokenLimit, value: {status: true, value: limits.tokenLimit}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsorTransferTimeout, value: {status: true, value: limits.sponsorTransferTimeout}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsorApproveTimeout, value: {status: true, value: limits.sponsorApproveTimeout}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanTransfer, value: {status: true, value: limits.ownerCanTransfer}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanDestroy, value: {status: true, value: limits.ownerCanDestroy}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.TransferEnabled, value: {status: true, value: limits.transfersEnabled}}).send(); + + // Check limits from sub: + const data = (await helper.rft.getData(collectionId))!; + expect(data.raw.limits).to.deep.eq(expectedLimits); + expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits); + // Check limits from eth: + const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner}); + expect(limitsEvm).to.have.length(9); + expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]); + expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]); + expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]); + expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]); + expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]); + expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]); + expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]); + expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]); + expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]); + })); +}); + +describe('Cannot set invalid collection limits', () => { + let donor: IKeyringPair; + + before(async () => { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + [ + {case: 'nft' as const}, + {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {case: 'ft' as const}, + ].map(testCase => + itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { + const invalidLimits = { + accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER), + transfersEnabled: 3, + }; + + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'ISNI', testCase.case, 18)).send(); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); + + // Cannot set non-existing limit + await expect(collectionEvm.methods + .setCollectionLimit({field: 9, value: {status: true, value: 1}}) + .call()).to.be.rejectedWith('Returned error: VM Exception while processing transaction: revert value not convertible into enum "CollectionLimitField"'); + + // Cannot disable limits + await expect(collectionEvm.methods + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: false, value: 0}}) + .call()).to.be.rejectedWith('user can\'t disable limits'); + + await expect(collectionEvm.methods + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: invalidLimits.accountTokenOwnershipLimit}}) + .call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`); + + await expect(collectionEvm.methods + .setCollectionLimit({field: CollectionLimitField.TransferEnabled, value: {status: true, value: 3}}) + .call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`); + + expect(() => collectionEvm.methods + .setCollectionLimit({field: CollectionLimitField.SponsoredDataSize, value: {status: true, value: -1}}).send()).to.throw('value out-of-bounds'); + })); + + [ + {case: 'nft' as const, requiredPallets: []}, + {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {case: 'ft' as const, requiredPallets: []}, + ].map(testCase => + itEth.ifWithPallets(`Non-owner and non-admin cannot set collection limits for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const nonOwner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'FLO', testCase.case, 18)).send(); + + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); + await expect(collectionEvm.methods + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) + .call({from: nonOwner})) + .to.be.rejectedWith('NoPermission'); + + await expect(collectionEvm.methods + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) + .send({from: nonOwner})) + .to.be.rejected; + })); +}); --- /dev/null +++ b/js-packages/tests/eth/collectionProperties.test.ts @@ -0,0 +1,215 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; +import {Pallets} from '../util/index.js'; +import type {IProperty, ITokenPropertyPermission} from '@unique/playgrounds/types.js'; +import type {IKeyringPair} from '@polkadot/types/types'; + +describe('EVM collection properties', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await _helper.arrange.createAccounts([50n], donor); + }); + }); + + // Soft-deprecated: setCollectionProperty + [ + {method: 'setCollectionProperties', mode: 'nft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, + {method: 'setCollectionProperties', mode: 'rft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, + {method: 'setCollectionProperties', mode: 'ft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, + {method: 'setCollectionProperty', mode: 'nft' as const, methodParams: ['testKey', Buffer.from('testValue')], expectedProps: [{key: 'testKey', value: 'testValue'}]}, + ].map(testCase => + itEth.ifWithPallets(`Collection properties can be set: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []}); + await collection.addAdmin(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'setCollectionProperty'); + + // collectionProperties returns an empty array if no properties: + expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like([]); + expect(await collectionEvm.methods.collectionProperties(['NonExistingKey']).call()).to.be.like([]); + + await collectionEvm.methods[testCase.method](...testCase.methodParams).send({from: caller}); + + const raw = (await collection.getData())?.raw; + expect(raw.properties).to.deep.equal(testCase.expectedProps); + + // collectionProperties returns properties: + expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value))); + })); + + itEth('Cannot set invalid properties', async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []}); + await collection.addAdmin(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft', caller); + + await expect(contract.methods.setCollectionProperties([{key: '', value: Buffer.from('val1')}]).send({from: caller})).to.be.rejected; + await expect(contract.methods.setCollectionProperties([{key: 'déjà vu', value: Buffer.from('hmm...')}]).send({from: caller})).to.be.rejected; + await expect(contract.methods.setCollectionProperties([{key: 'a'.repeat(257), value: Buffer.from('val3')}]).send({from: caller})).to.be.rejected; + // TODO add more expects + const raw = (await collection.getData())?.raw; + expect(raw.properties).to.deep.equal([]); + }); + + + // Soft-deprecated: deleteCollectionProperty + [ + {method: 'deleteCollectionProperties', mode: 'nft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]}, + {method: 'deleteCollectionProperties', mode: 'rft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]}, + {method: 'deleteCollectionProperties', mode: 'ft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]}, + {method: 'deleteCollectionProperty', mode: 'nft' as const, methodParams: ['testKey1'], expectedProps: [{key: 'testKey2', value: 'testValue2'}, {key: 'testKey3', value: 'testValue3'}]}, + ].map(testCase => + itEth.ifWithPallets(`Collection properties can be deleted: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => { + const properties = [ + {key: 'testKey1', value: 'testValue1'}, + {key: 'testKey2', value: 'testValue2'}, + {key: 'testKey3', value: 'testValue3'}]; + const caller = await helper.eth.createAccountWithBalance(donor); + const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties}); + + await collection.addAdmin(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'deleteCollectionProperty'); + + await contract.methods[testCase.method](...testCase.methodParams).send({from: caller}); + + const raw = (await collection.getData())?.raw; + + expect(raw.properties.length).to.equal(testCase.expectedProps.length); + expect(raw.properties).to.deep.equal(testCase.expectedProps); + })); + + [ + {method: 'deleteCollectionProperties', methodParams: [['testKey2']]}, + {method: 'deleteCollectionProperty', methodParams: ['testKey2']}, + ].map(testCase => + itEth(`cannot ${testCase.method}() of non-owned collections`, async ({helper}) => { + const properties = [ + {key: 'testKey1', value: 'testValue1'}, + {key: 'testKey2', value: 'testValue2'}, + ]; + const caller = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'deleteCollectionProperty'); + + await expect(collectionEvm.methods[testCase.method](...testCase.methodParams).send({from: caller})).to.be.rejected; + expect(await collection.getProperties()).to.deep.eq(properties); + })); + + itEth('Can be read', async({helper}) => { + const caller = helper.eth.createAccount(); + const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: [{key: 'testKey', value: 'testValue'}]}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft', caller); + + const value = await contract.methods.collectionProperty('testKey').call(); + expect(value).to.equal(helper.getWeb3().utils.toHex('testValue')); + }); +}); + +describe('Supports ERC721Metadata', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + [ + {case: 'nft' as const}, + {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`ERC721Metadata property can be set for ${testCase.case} collection`, testCase.requiredPallets || [], async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const bruh = await helper.eth.createAccountWithBalance(donor); + + const BASE_URI = 'base/'; + const SUFFIX = 'suffix1'; + const URI = 'uri1'; + + const collectionHelpers = await helper.ethNativeContract.collectionHelpers(caller); + const creatorMethod = testCase.case === 'rft' ? 'createRFTCollection' : 'createNFTCollection'; + + const {collectionId, collectionAddress} = await helper.eth[creatorMethod](caller, 'n', 'd', 'p'); + const bruhCross = helper.ethCrossAccount.fromAddress(bruh); + + const contract = await helper.ethNativeContract.collectionById(collectionId, testCase.case, caller); + await contract.methods.addCollectionAdminCross(bruhCross).send(); // to check that admin will work too + + const collection1 = helper.nft.getCollectionObject(collectionId); + const data1 = await collection1.getData(); + expect(data1?.raw.flags.erc721metadata).to.be.false; + expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.false; + + await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, BASE_URI) + .send({from: bruh}); + + expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.true; + + const collection2 = helper.nft.getCollectionObject(collectionId); + const data2 = await collection2.getData(); + expect(data2?.raw.flags.erc721metadata).to.be.true; + + const propertyPermissions = data2?.raw.tokenPropertyPermissions; + expect(propertyPermissions?.length).to.equal(2); + + expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => tpp.key === 'URI' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner)).to.be.not.null; + + expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => tpp.key === 'URISuffix' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner)).to.be.not.null; + + expect(data2?.raw.properties?.find((property: IProperty) => property.key === 'baseURI' && property.value === BASE_URI)).to.be.not.null; + + const token1Result = await contract.methods.mint(bruh).send(); + const tokenId1 = token1Result.events.Transfer.returnValues.tokenId; + + expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI); + + await contract.methods.setProperties(tokenId1, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send(); + expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX); + + await contract.methods.setProperties(tokenId1, [{key: 'URI', value: Buffer.from(URI)}]).send(); + expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(URI); + + await contract.methods.deleteProperties(tokenId1, ['URI']).send(); + expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX); + + const token2Result = await contract.methods.mintWithTokenURI(bruh, URI).send(); + const tokenId2 = token2Result.events.Transfer.returnValues.tokenId; + + expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(URI); + + await contract.methods.deleteProperties(tokenId2, ['URI']).send(); + expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI); + + await contract.methods.setProperties(tokenId2, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send(); + expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI + SUFFIX); + })); +}); --- /dev/null +++ b/js-packages/tests/eth/collectionSponsoring.test.ts @@ -0,0 +1,955 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../util/index.js'; +import {itEth, expect} from './util/index.js'; +import {CollectionLimitField, TokenPermissionField} from './util/playgrounds/types.js'; + +describe('evm nft collection sponsoring', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let nominal: bigint; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([100n], donor); + nominal = helper.balance.getOneTokenNominal(); + }); + }); + + // TODO: move to substrate tests + itEth('sponsors mint transactions', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}}); + await collection.setSponsor(alice, alice.address); + await collection.confirmSponsorship(alice); + + const minter = helper.eth.createAccount(); + expect(await helper.balance.getEthereum(minter)).to.equal(0n); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', minter); + + await collection.addToAllowList(alice, {Ethereum: minter}); + + const result = await contract.methods.mint(minter).send(); + + const events = helper.eth.normalizeEvents(result.events); + expect(events).to.be.deep.equal([ + { + address: collectionAddress, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: minter, + tokenId: '1', + }, + }, + ]); + }); + + // TODO: Temprorary off. Need refactor + // itWeb3('Set substrate sponsor', async ({api, web3, privateKeyWrapper}) => { + // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + // const collectionHelpers = evmCollectionHelpers(web3, owner); + // let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send(); + // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + // const sponsor = privateKeyWrapper('//Alice'); + // const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + + // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; + // result = await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner}); + // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true; + + // const confirmTx = await api.tx.unique.confirmSponsorship(collectionId); + // await submitTransactionAsync(sponsor, confirmTx); + // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; + + // const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner}); + // expect(bigIntToSub(api, BigInt(sponsorTuple[1]))).to.be.eq(sponsor.address); + // }); + + [ + 'setCollectionSponsorCross', + 'setCollectionSponsor', // Soft-deprecated + ].map(testCase => + itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner); + + let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)}); + const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner, testCase === 'setCollectionSponsor'); + + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; + result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner}); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true; + + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); + expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; + + await collectionEvm.methods.removeCollectionSponsor().send({from: owner}); + + sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); + expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000'); + })); + + [ + 'setCollectionSponsorCross', + 'setCollectionSponsor', // Soft-deprecated + ].map(testCase => + itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsorEth = await helper.eth.createAccountWithBalance(donor); + const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); + + const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', ''); + + const collectionSub = helper.nft.getCollectionObject(collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor'); + + // Set collection sponsor: + await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner}); + let sponsorship = (await collectionSub.getData())!.raw.sponsorship; + expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); + // Account cannot confirm sponsorship if it is not set as a sponsor + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + // Sponsor can confirm sponsorship: + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); + sponsorship = (await collectionSub.getData())!.raw.sponsorship; + expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); + + // Create user with no balance: + const user = helper.ethCrossAccount.createAccount(); + const nextTokenId = await collectionEvm.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + + // Set collection permissions: + const oldPermissions = (await collectionSub.getData())!.raw.permissions; + expect(oldPermissions.mintMode).to.be.false; + expect(oldPermissions.access).to.be.equal('Normal'); + + await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); + await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner}); + await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: 30}}).send(); + + const newPermissions = (await collectionSub.getData())!.raw.permissions; + expect(newPermissions.mintMode).to.be.true; + expect(newPermissions.access).to.be.equal('AllowList'); + + // Set token permissions + await collectionEvm.methods.setTokenPropertyPermissions([ + ['key', [ + [TokenPermissionField.TokenOwner, true], + ], + ], + ]).send({from: owner}); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); + const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); + + // User can mint token without balance: + { + const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth}); + const event = helper.eth.normalizeEvents(result.events) + .find(event => event.event === 'Transfer'); + + expect(event).to.be.deep.equal({ + address: collectionAddress, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user.eth, + tokenId: '1', + }, + }); + + // await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value1')}]).send({from: user.eth}); + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); + const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); + + expect(await collectionEvm.methods.properties(nextTokenId, []).call()) + .to.be.like([ + [ + 'key', + '0x' + Buffer.from('Value').toString('hex'), + ], + ]); + expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); + expect(userBalanceAfter).to.be.eq(userBalanceBefore); + expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; + } + })); + + itEth('Can sponsor [set token properties] via access list', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsorEth = await helper.eth.createAccountWithBalance(donor); + const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); + + const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', ''); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, false); + + // Set collection sponsor: + await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner}); + + // Sponsor can confirm sponsorship: + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); + + // Create user with no balance: + const user = helper.ethCrossAccount.createAccount(); + const nextTokenId = await collectionEvm.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + + // Set collection permissions: + await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); + await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner}); + await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: 30}}).send(); + + // Set token permissions + await collectionEvm.methods.setTokenPropertyPermissions([ + ['key', [ + [TokenPermissionField.TokenOwner, true], + ], + ], + ]).send({from: owner}); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); + const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); + + // User can mint token without balance: + { + const result = await collectionEvm.methods.mintCross(user, []).send({from: user.eth}); + const event = helper.eth.normalizeEvents(result.events) + .find(event => event.event === 'Transfer'); + + expect(event).to.be.deep.equal({ + address: collectionAddress, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user.eth, + tokenId: '1', + }, + }); + + await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth}); + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); + const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); + + expect(await collectionEvm.methods.properties(nextTokenId, []).call()) + .to.be.like([ + [ + 'key', + '0x' + Buffer.from('Value').toString('hex'), + ], + ]); + expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); + expect(userBalanceAfter).to.be.eq(userBalanceBefore); + expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; + } + }); + + // TODO: Temprorary off. Need refactor + // itWeb3('Sponsoring collection from substrate address via access list', async ({api, web3, privateKeyWrapper}) => { + // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + // const collectionHelpers = evmCollectionHelpers(web3, owner); + // const result = await collectionHelpers.methods.createERC721MetadataCompatibleNFTCollection('Sponsor collection', '1', '1', '').send(); + // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + // const sponsor = privateKeyWrapper('//Alice'); + // const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + + // await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner}); + + // const confirmTx = await api.tx.unique.confirmSponsorship(collectionId); + // await submitTransactionAsync(sponsor, confirmTx); + + // const user = createEthAccount(web3); + // const nextTokenId = await collectionEvm.methods.nextTokenId().call(); + // expect(nextTokenId).to.be.equal('1'); + + // await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); + // await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner}); + // await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); + + // const ownerBalanceBefore = await ethBalanceViaSub(api, owner); + // const sponsorBalanceBefore = (await getBalance(api, [sponsor.address]))[0]; + + // { + // const nextTokenId = await collectionEvm.methods.nextTokenId().call(); + // expect(nextTokenId).to.be.equal('1'); + // const result = await collectionEvm.methods.mintWithTokenURI( + // user, + // nextTokenId, + // 'Test URI', + // ).send({from: user}); + // const events = normalizeEvents(result.events); + + // expect(events).to.be.deep.equal([ + // { + // address: collectionIdAddress, + // event: 'Transfer', + // args: { + // from: '0x0000000000000000000000000000000000000000', + // to: user, + // tokenId: nextTokenId, + // }, + // }, + // ]); + + // const ownerBalanceAfter = await ethBalanceViaSub(api, owner); + // const sponsorBalanceAfter = (await getBalance(api, [sponsor.address]))[0]; + + // expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); + // expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); + // expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; + // } + // }); + + [ + 'setCollectionSponsorCross', + 'setCollectionSponsor', // Soft-deprecated + ].map(testCase => + itEth(`[${testCase}] Check that transaction via EVM spend money from sponsor address`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', ''); + + const collectionSub = helper.nft.getCollectionObject(collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor'); + // Set collection sponsor: + await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send(); + let collectionData = (await collectionSub.getData())!; + expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + collectionData = (await collectionSub.getData())!; + expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); + + const user = helper.eth.createAccount(); + const userCross = helper.ethCrossAccount.fromAddress(user); + await collectionEvm.methods.addCollectionAdminCross(userCross).send(); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + + const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); + const tokenId = mintingResult.events.Transfer.returnValues.tokenId; + + const event = helper.eth.normalizeEvents(mintingResult.events) + .find(event => event.event === 'Transfer'); + const address = helper.ethAddress.fromCollectionId(collectionId); + + expect(event).to.be.deep.equal({ + address, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user, + tokenId: '1', + }, + }); + expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI'); + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + })); + + itEth('Can reassign collection sponsor', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsorEth = await helper.eth.createAccountWithBalance(donor); + const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); + const [sponsorSub] = await helper.arrange.createAccounts([100n], donor); + const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub); + + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', ''); + const collectionSub = helper.nft.getCollectionObject(collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + // Set and confirm sponsor: + await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner}); + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); + + // Can reassign sponsor: + await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner}); + const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship; + expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address}); + }); +}); + +describe('evm RFT collection sponsoring', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let nominal: bigint; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([100n], donor); + nominal = helper.balance.getOneTokenNominal(); + }); + }); + + [ + 'mintCross', + 'mintWithTokenURI', + ].map(testCase => + itEth(`[${testCase}] sponsors mint transactions`, async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}, tokenPropertyPermissions: [ + {key: 'URI', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, + ]}); + + const owner = await helper.eth.createAccountWithBalance(donor); + await collection.setSponsor(alice, alice.address); + await collection.confirmSponsorship(alice); + + const minter = helper.eth.createAccount(); + const minterCross = helper.ethCrossAccount.fromAddress(minter); + expect(await helper.balance.getEthereum(minter)).to.equal(0n); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', minter, true); + + await collection.addToAllowList(alice, {Ethereum: minter}); + await collection.addAdmin(alice, {Ethereum: owner}); + const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner); + await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, 'base/') + .send(); + + let mintingResult; + let tokenId; + switch (testCase) { + case 'mintCross': + mintingResult = await contract.methods.mintCross(minterCross, []).send(); + break; + case 'mintWithTokenURI': + mintingResult = await contract.methods.mintWithTokenURI(minter, 'Test URI').send(); + tokenId = mintingResult.events.Transfer.returnValues.tokenId; + expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); + break; + } + + const events = helper.eth.normalizeEvents(mintingResult.events); + expect(events).to.deep.include({ + address: collectionAddress, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: minter, + tokenId: '1', + }, + }); + })); + + [ + 'setCollectionSponsorCross', + 'setCollectionSponsor', // Soft-deprecated + ].map(testCase => + itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner); + + let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)}); + const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'rft', owner, testCase === 'setCollectionSponsor'); + + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; + result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner}); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true; + + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; + + await collectionEvm.methods.removeCollectionSponsor().send({from: owner}); + + const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); + expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000'); + })); + + [ + 'setCollectionSponsorCross', + 'setCollectionSponsor', // Soft-deprecated + ].map(testCase => + itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsorEth = await helper.eth.createAccountWithBalance(donor); + const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); + + const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Sponsor collection', '1', '1', ''); + + const collectionSub = helper.rft.getCollectionObject(collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor'); + + // Set collection sponsor: + await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner}); + let sponsorship = (await collectionSub.getData())!.raw.sponsorship; + expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); + // Account cannot confirm sponsorship if it is not set as a sponsor + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + // Sponsor can confirm sponsorship: + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); + sponsorship = (await collectionSub.getData())!.raw.sponsorship; + expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); + + // Create user with no balance: + const user = helper.eth.createAccount(); + const userCross = helper.ethCrossAccount.fromAddress(user); + const nextTokenId = await collectionEvm.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + + // Set collection permissions: + const oldPermissions = (await collectionSub.getData())!.raw.permissions; + expect(oldPermissions.mintMode).to.be.false; + expect(oldPermissions.access).to.be.equal('Normal'); + + await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); + await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner}); + await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); + + const newPermissions = (await collectionSub.getData())!.raw.permissions; + expect(newPermissions.mintMode).to.be.true; + expect(newPermissions.access).to.be.equal('AllowList'); + + // Set token permissions + await collectionEvm.methods.setTokenPropertyPermissions([ + ['URI', [ + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true], + ], + ], + ]).send({from: owner}); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); + const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + + // User can mint token without balance: + { + const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); + const events = helper.eth.normalizeEvents(result.events); + + expect(events).to.deep.include({ + address: collectionAddress, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user, + tokenId: '1', + }, + }); + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); + const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + + expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); + expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); + expect(userBalanceAfter).to.be.eq(userBalanceBefore); + expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; + } + })); + + [ + 'setCollectionSponsorCross', + 'setCollectionSponsor', // Soft-deprecated + ].map(testCase => + itEth(`[${testCase}] Check that collection admin EVM transaction spend money from sponsor eth address`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); + + const collectionSub = helper.rft.getCollectionObject(collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor'); + // Set collection sponsor: + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false; + await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send(); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true; + let collectionData = (await collectionSub.getData())!; + expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true; + + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + collectionData = (await collectionSub.getData())!; + expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false; + const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); + const sponsorSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.ethToSubstrate(sponsor)); + const actualSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))); + expect(actualSubAddress).to.be.equal(sponsorSubAddress); + + const user = helper.eth.createAccount(); + const userCross = helper.ethCrossAccount.fromAddress(user); + await collectionEvm.methods.addCollectionAdminCross(userCross).send(); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + + const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); + const tokenId = mintingResult.events.Transfer.returnValues.tokenId; + + const events = helper.eth.normalizeEvents(mintingResult.events); + const address = helper.ethAddress.fromCollectionId(collectionId); + + expect(events).to.deep.include({ + address, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user, + tokenId: '1', + }, + }); + expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI'); + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + })); + + itEth('Check that collection admin EVM transaction spend money from sponsor sub address', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = alice; + const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor); + + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); + + const collectionSub = helper.rft.getCollectionObject(collectionId); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false); + // Set collection sponsor: + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false; + await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send(); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true; + let collectionData = (await collectionSub.getData())!; + expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(sponsor.address); + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + await collectionSub.confirmSponsorship(sponsor); + collectionData = (await collectionSub.getData())!; + expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(sponsor.address); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false; + const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); + expect(BigInt(sponsorStruct.sub)).to.be.equal(BigInt('0x' + Buffer.from(sponsor.addressRaw).toString('hex'))); + + const user = helper.eth.createAccount(); + const userCross = helper.ethCrossAccount.fromAddress(user); + await collectionEvm.methods.addCollectionAdminCross(userCross).send(); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address); + + const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); + const tokenId = mintingResult.events.Transfer.returnValues.tokenId; + + const events = helper.eth.normalizeEvents(mintingResult.events); + + expect(events).to.deep.include({ + address: collectionAddress, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user, + tokenId: '1', + }, + }); + expect(await collectionEvm.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); + const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + }); + + itEth('Sponsoring collection from substrate address via access list', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const user = helper.eth.createAccount(); + const userCross = helper.ethCrossAccount.fromAddress(user); + const sponsor = alice; + const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor); + + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false); + + await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner}); + + const collectionSub = helper.rft.getCollectionObject(collectionId); + await collectionSub.confirmSponsorship(sponsor); + + const nextTokenId = await collectionEvm.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + + await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); + + await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner}); + await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); + + // Set token permissions + await collectionEvm.methods.setTokenPropertyPermissions([ + ['URI', [ + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true], + ], + ], + ]).send({from: owner}); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address); + const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + + { + const nextTokenId = await collectionEvm.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + const mintingResult = await collectionEvm.methods.mintWithTokenURI( + user, + 'Test URI', + ).send({from: user}); + + const events = helper.eth.normalizeEvents(mintingResult.events); + + + expect(events).to.deep.include({ + address: collectionAddress, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user, + tokenId: nextTokenId, + }, + }); + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address); + const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + + expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); + expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); + expect(userBalanceAfter).to.be.eq(userBalanceBefore); + expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; + } + }); + + itEth('Can reassign collection sponsor', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsorEth = await helper.eth.createAccountWithBalance(donor); + const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); + const [sponsorSub] = await helper.arrange.createAccounts([100n], donor); + const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub); + + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); + const collectionSub = helper.rft.getCollectionObject(collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + + // Set and confirm sponsor: + await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner}); + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); + + // Can reassign sponsor: + await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner}); + const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship; + expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address}); + }); + + [ + 'transfer', + 'transferCross', + 'transferFrom', + 'transferFromCross', + ].map(testCase => + itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + + await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send(); + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + + const user = await helper.eth.createAccountWithBalance(donor); + const userCross = helper.ethCrossAccount.fromAddress(user); + await collectionEvm.methods.addCollectionAdminCross(userCross).send(); + + const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); + const tokenId = result.events.Transfer.returnValues.tokenId; + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + + switch (testCase) { + case 'transfer': + await collectionEvm.methods.transfer(receiver, tokenId).send({from: user}); + break; + case 'transferCross': + await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user}); + break; + case 'transferFrom': + await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user}); + break; + case 'transferFromCross': + await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user}); + break; + } + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + expect(userBalanceAfter).to.be.eq(userBalanceBefore); + })); +}); + +describe('evm RFT token sponsoring', () => { + let donor: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + donor = await privateKey({url: import.meta.url}); + }); + }); + + [ + 'transfer', + 'transferCross', + 'transferFrom', + 'transferFromCross', + ].map(testCase => + itEth(`[${testCase}] Check that token piece transfer via EVM spend money from sponsor address`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + + await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send(); + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + + const user = await helper.eth.createAccountWithBalance(donor); + const userCross = helper.ethCrossAccount.fromAddress(user); + await collectionEvm.methods.addCollectionAdminCross(userCross).send(); + + const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); + const tokenId = result.events.Transfer.returnValues.tokenId; + + const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user); + await tokenContract.methods.repartition(2).send(); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + + switch (testCase) { + case 'transfer': + await tokenContract.methods.transfer(receiver, 1).send(); + break; + case 'transferCross': + await tokenContract.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), 1).send(); + break; + case 'transferFrom': + await tokenContract.methods.transferFrom(user, receiver, 1).send(); + break; + case 'transferFromCross': + await tokenContract.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), 1).send(); + break; + } + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + expect(userBalanceAfter).to.be.eq(userBalanceBefore); + })); + + [ + 'approve', + 'approveCross', + ].map(testCase => + itEth(`[${testCase}] Check that approve via EVM spend money from sponsor address`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + + await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send(); + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + + const user = await helper.eth.createAccountWithBalance(donor); + const userCross = helper.ethCrossAccount.fromAddress(user); + await collectionEvm.methods.addCollectionAdminCross(userCross).send(); + + const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); + const tokenId = result.events.Transfer.returnValues.tokenId; + + const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user); + await tokenContract.methods.repartition(2).send(); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + + switch (testCase) { + case 'approve': + await tokenContract.methods.approve(receiver, 1).send(); + break; + case 'approveCross': + await tokenContract.methods.approveCross(helper.ethCrossAccount.fromAddress(receiver), 1).send(); + break; + } + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + expect(userBalanceAfter).to.be.eq(userBalanceBefore); + })); +}); + --- /dev/null +++ b/js-packages/tests/eth/contractSponsoring.test.ts @@ -0,0 +1,579 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; +import {itEth, expect, SponsoringMode, usingEthPlaygrounds} from './util/index.js'; +import {usingPlaygrounds} from '../util/index.js'; +import type {CompiledContract} from './util/playgrounds/types.js'; + +describe('Sponsoring EVM contracts', () => { + let donor: IKeyringPair; + let nominal: bigint; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + nominal = helper.balance.getOneTokenNominal(); + }); + }); + + itEth('Self sponsoring can be set by the address that deployed the contract', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const flipper = await helper.eth.deployFlipper(owner); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + + // 1. owner can set selfSponsoring: + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; + const result = await helpers.methods.selfSponsoredEnable(flipper.options.address).send({from: owner}); + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; + + // 1.1 Can get sponsor using methods.sponsor: + const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call(); + expect(actualSponsorOpt.status).to.be.true; + const actualSponsor = actualSponsorOpt.value; + expect(actualSponsor.eth).to.eq(flipper.options.address); + expect(actualSponsor.sub).to.eq('0'); + + // 2. Events should be: + const ethEvents = helper.eth.helper.eth.normalizeEvents(result.events); + expect(ethEvents).to.be.deep.equal([ + { + address: flipper.options.address, + event: 'ContractSponsorSet', + args: { + contractAddress: flipper.options.address, + sponsor: flipper.options.address, + }, + }, + { + address: flipper.options.address, + event: 'ContractSponsorshipConfirmed', + args: { + contractAddress: flipper.options.address, + sponsor: flipper.options.address, + }, + }, + ]); + }); + + itEth('Self sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const notOwner = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; + await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; + }); + + itEth('Sponsoring can be set by the address that has deployed the contract', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false; + await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner}); + expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true; + }); + + itEth('Sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const notOwner = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false; + await expect(helpers.methods.setSponsoringMode(notOwner, SponsoringMode.Allowlisted).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false; + }); + + itEth('Sponsor can be set by the address that deployed the contract', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + // 1. owner can set a sponsor: + expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false; + const result = await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); + expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true; + + // 2. Events should be: + const events = helper.eth.normalizeEvents(result.events); + expect(events).to.be.deep.equal([ + { + address: flipper.options.address, + event: 'ContractSponsorSet', + args: { + contractAddress: flipper.options.address, + sponsor: sponsor, + }, + }, + ]); + }); + + itEth('Sponsor cannot be set by the address that did not deployed the contract', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const notOwner = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false; + await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false; + }); + + itEth('Sponsorship can be confirmed by the address that pending as sponsor', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; + await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); + + // 1. sponsor can confirm sponsorship: + const result = await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; + + // 1.1 Can get sponsor using methods.sponsor: + const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call(); + expect(actualSponsorOpt.status).to.be.true; + const actualSponsor = actualSponsorOpt.value; + expect(actualSponsor.eth).to.eq(sponsor); + expect(actualSponsor.sub).to.eq('0'); + + // 2. Events should be: + const events = helper.eth.normalizeEvents(result.events); + expect(events).to.be.deep.equal([ + { + address: flipper.options.address, + event: 'ContractSponsorshipConfirmed', + args: { + contractAddress: flipper.options.address, + sponsor: sponsor, + }, + }, + ]); + }); + + itEth('Sponsorship can not be confirmed by the address that not pending as sponsor', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const notSponsor = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; + await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected; + await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPermission'); + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; + }); + + itEth('Sponsorship can not be confirmed by the address that not set as sponsor', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const notSponsor = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; + await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPendingSponsor'); + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; + }); + + itEth('Sponsor can be removed by the address that deployed the contract', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; + await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; + // 1. Can remove sponsor: + const result = await helpers.methods.removeSponsor(flipper.options.address).send(); + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; + + // 2. Events should be: + const events = helper.eth.normalizeEvents(result.events); + expect(events).to.be.deep.equal([ + { + address: flipper.options.address, + event: 'ContractSponsorRemoved', + args: { + contractAddress: flipper.options.address, + }, + }, + ]); + + // TODO: why call method reverts? + // const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call(); + // expect(actualSponsor.eth).to.eq(sponsor); + // expect(actualSponsor.sub).to.eq('0'); + }); + + itEth('Sponsor can not be removed by the address that did not deployed the contract', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const notOwner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; + await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; + + await expect(helpers.methods.removeSponsor(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + await expect(helpers.methods.removeSponsor(flipper.options.address).send({from: notOwner})).to.be.rejected; + expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; + }); + + itEth('In generous mode, non-allowlisted user transaction will be sponsored', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); + + await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); + + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + const callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller)); + + await flipper.methods.flip().send({from: caller}); + expect(await flipper.methods.getValue().call()).to.be.true; + + // Balance should be taken from sponsor instead of caller + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller)); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + expect(callerBalanceAfter).to.be.eq(callerBalanceBefore); + }); + + itEth('In generous mode, non-allowlisted user transaction will be self sponsored', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + await helpers.methods.selfSponsoredEnable(flipper.options.address).send(); + + await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); + + await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address); + + const contractBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address)); + const callerBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller)); + + await flipper.methods.flip().send({from: caller}); + expect(await flipper.methods.getValue().call()).to.be.true; + + // Balance should be taken from sponsor instead of caller + const contractBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address)); + const callerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller)); + expect(contractBalanceAfter < contractBalanceBefore).to.be.true; + expect(callerBalanceAfter).to.be.eq(callerBalanceBefore); + }); + + [ + {balance: 0n, label: '0'}, + {balance: 10n, label: '10'}, + ].map(testCase => { + itEth(`Allow-listed address that has ${testCase.label} UNQ can call a contract. Sponsor balance should decrease`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const caller = helper.eth.createAccount(); + await helper.eth.transferBalanceFromSubstrate(donor, caller, testCase.balance); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); + await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner}); + + await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); + + await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); + + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + expect(sponsorBalanceBefore > 0n).to.be.true; + + await flipper.methods.flip().send({from: caller}); + expect(await flipper.methods.getValue().call()).to.be.true; + + // Balance should be taken from flipper instead of caller + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + // Caller's balance does not change: + const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller)); + expect(callerBalanceAfter).to.eq(testCase.balance * nominal); + }); + }); + + itEth('Non-allow-listed address can call a contract. Sponsor balance should not decrease', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const caller = helper.eth.createAccount(); + const contractHelpers = await helper.ethNativeContract.contractHelpers(owner); + + // Deploy flipper and send some tokens: + const flipper = await helper.eth.deployFlipper(owner); + await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address); + expect(await flipper.methods.getValue().call()).to.be.false; + // flipper address has some tokens: + const originalFlipperBalance = await helper.balance.getEthereum(flipper.options.address); + expect(originalFlipperBalance > 0n).to.be.true; + + // Set Allowlisted sponsoring mode. caller is not in allow list: + await contractHelpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); + await contractHelpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner}); + await contractHelpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); + + // 1. Caller has no UNQ and is not in allow list. So he cannot flip: + await expect(flipper.methods.flip().send({from: caller})).to.be.rejectedWith(/Returned error: insufficient funds for gas \* price \+ value/); + expect(await flipper.methods.getValue().call()).to.be.false; + + // Flipper's balance does not change: + const balanceAfter = await helper.balance.getEthereum(flipper.options.address); + expect(balanceAfter).to.be.equal(originalFlipperBalance); + }); + + itEth('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + const originalCallerBalance = await helper.balance.getEthereum(caller); + await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); + await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner}); + + await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(flipper.options.address, 10).send({from: owner}); + + await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); + + const originalFlipperBalance = await helper.balance.getEthereum(sponsor); + expect(originalFlipperBalance > 0n).to.be.true; + + await flipper.methods.flip().send({from: caller}); + expect(await flipper.methods.getValue().call()).to.be.true; + expect(await helper.balance.getEthereum(caller)).to.be.equal(originalCallerBalance); + + const newFlipperBalance = await helper.balance.getEthereum(sponsor); + expect(newFlipperBalance).to.be.not.equal(originalFlipperBalance); + + await flipper.methods.flip().send({from: caller}); + // todo:playgrounds fails rarely (expected 99893341659775672580n to equal 99912598679356033129n) (again, 99893341659775672580n) + expect(await helper.balance.getEthereum(sponsor)).to.be.equal(newFlipperBalance); + expect(await helper.balance.getEthereum(caller)).to.be.not.equal(originalCallerBalance); + }); + + // TODO: Find a way to calculate default rate limit + itEth('Default rate limit equal 7200', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helpers.methods.sponsoringRateLimit(flipper.options.address).call()).to.be.equal('7200'); + }); + + itEth('Gas price boundaries', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); + + await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); + + let sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + let callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller)); + + let expectValue = await flipper.methods.getValue().call(); + + const flip = async (gasPrice: bigint, shouldPass = true) => { + await flipper.methods.flip().send({from: caller, gasPrice: gasPrice}); + expectValue = !expectValue; + expect(await flipper.methods.getValue().call()).to.be.eq(expectValue); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller)); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.eq(shouldPass); + expect(callerBalanceAfter === callerBalanceBefore).to.be.eq(shouldPass); + sponsorBalanceBefore = sponsorBalanceAfter; + callerBalanceBefore = callerBalanceAfter; + }; + + const gasPrice = BigInt((await helper.eth.getGasPrice())!); + await flip(gasPrice); + await flip(gasPrice * 2n); + await flip(gasPrice * 21n / 10n); + await flip(gasPrice * 22n / 10n, false); + }); +}); + +describe('Sponsoring Fee Limit', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let testContract: CompiledContract; + + async function compileTestContract(helper: EthUniqueHelper) { + if(!testContract) { + testContract = await helper.ethContract.compile( + 'TestContract', + ` + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.0; + + contract TestContract { + event Result(bool); + + function test(uint32 cycles) public { + uint256 counter = 0; + while(true) { + counter ++; + if (counter > cycles){ + break; + } + } + emit Result(true); + } + } + `, + ); + } + return testContract; + } + + async function deployTestContract(helper: EthUniqueHelper, owner: string) { + const compiled = await compileTestContract(helper); + return await helper.ethContract.deployByAbi(owner, compiled.abi, compiled.object); + } + + before(async () => { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([100n], donor); + }); + }); + + itEth('Default fee limit', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('115792089237316195423570985008687907853269984665640564039457584007913129639935'); + }); + + itEth('Set fee limit', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + await helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send(); + expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('100'); + }); + + itEth('Negative test - set fee limit by non-owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const stranger = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + const flipper = await helper.eth.deployFlipper(owner); + + await expect(helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send({from: stranger})).to.be.rejected; + }); + + itEth('Negative test - check that eth transactions exceeding fee limit are not executed', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const user = await helper.eth.createAccountWithBalance(donor); + const helpers = helper.ethNativeContract.contractHelpers(owner); + + const testContract = await deployTestContract(helper, owner); + + await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner}); + + await helpers.methods.setSponsor(testContract.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor}); + + const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice()); + + await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send(); + + const originalUserBalance = await helper.balance.getEthereum(user); + await testContract.methods.test(100).send({from: user, gas: 2_000_000, maxFeePerGas: gasPrice.toString()}); + expect(await helper.balance.getEthereum(user)).to.be.equal(originalUserBalance); + + await testContract.methods.test(100).send({from: user, gas: 2_100_000, maxFeePerGas: gasPrice.toString()}); + expect(await helper.balance.getEthereum(user)).to.not.be.equal(originalUserBalance); + }); + + itEth('Negative test - check that evm.call transactions exceeding fee limit are not executed', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(owner); + + const testContract = await deployTestContract(helper, owner); + + await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner}); + + await helpers.methods.setSponsor(testContract.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor}); + + const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice()); + + await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send(); + + const originalAliceBalance = await helper.balance.getSubstrate(alice.address); + + await helper.eth.sendEVM( + alice, + testContract.options.address, + testContract.methods.test(100).encodeABI(), + '0', + 2_000_000, + ); + // expect((await api.query.system.account(alice.address)).data.free.toBigInt()).to.be.equal(originalAliceBalance); + expect(await helper.balance.getSubstrate(alice.address)).to.be.equal(originalAliceBalance); + + await helper.eth.sendEVM( + alice, + testContract.options.address, + testContract.methods.test(100).encodeABI(), + '0', + 2_100_000, + ); + expect(await helper.balance.getSubstrate(alice.address)).to.not.be.equal(originalAliceBalance); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/createCollection.test.ts @@ -0,0 +1,1449 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {evmToAddress} from '@polkadot/util-crypto'; +import {Pallets, requirePalletsOrSkip} from '../util/index.js'; +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import {CREATE_COLLECTION_DATA_DEFAULTS, CollectionLimitField, CollectionMode, CreateCollectionData, TokenPermissionField, emptyAddress} from './util/playgrounds/types.js'; +import {CollectionFlag} from '@unique/playgrounds/types.js'; +import type {IEthCrossAccountId, TCollectionMode} from '@unique/playgrounds/types.js'; + +const DECIMALS = 18; +const CREATE_COLLECTION_DATA_DEFAULTS_ARRAY = [ + [], // properties + [], // tokenPropertyPermissions + [], // adminList + [false, false, []], // nestingSettings + [], // limits + emptyAddress, // pendingSponsor + [0], // flags +]; + +type ElementOf = A extends readonly (infer T)[] ? T : never; +function* cartesian>, R extends Array>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf}]> { + if(args.length === 0) { + yield internalRest as any; + return; + } + for(const value of args[0]) { + yield* cartesian([...internalRest, value], ...args.slice(1)) as any; + } +} + +describe('Create collection from EVM', () => { + let donor: IKeyringPair; + let nominal: bigint; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + nominal = helper.balance.getOneTokenNominal(); + }); + }); + + describe('Fungible collection', () => { + before(async function() { + await usingEthPlaygrounds((helper) => { + requirePalletsOrSkip(this, helper, [Pallets.Fungible]); + return Promise.resolve(); + }); + }); + + itEth('Collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; + const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); + + expect(await collectionHelpers + .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) + .to.be.false; + + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send(); + expect(await collectionHelpers + .methods.isCollectionExist(collectionAddress).call()) + .to.be.true; + + // check collectionOwner: + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); + const collectionOwner = await collectionEvm.methods.collectionOwner().call(); + expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true)); + }); + + itEth('destroyCollection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send(); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + + const result = await collectionHelper.methods + .destroyCollection(collectionAddress) + .send({from: owner}); + + const events = helper.eth.normalizeEvents(result.events); + + expect(events).to.be.deep.equal([ + { + address: collectionHelper.options.address, + event: 'CollectionDestroyed', + args: { + collectionId: collectionAddress, + }, + }, + ]); + + expect(await collectionHelper.methods + .isCollectionExist(collectionAddress) + .call()).to.be.false; + expect(await helper.collection.getData(collectionId)).to.be.null; + }); + + itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + { + const MAX_NAME_LENGTH = 64; + const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); + const description = 'A'; + const tokenPrefix = 'A'; + + await expect(collectionHelper.methods + .createCollection([ + collectionName, + description, + tokenPrefix, + CollectionMode.Fungible, + DECIMALS, + ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, + ]) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); + } + { + const MAX_DESCRIPTION_LENGTH = 256; + const collectionName = 'A'; + const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); + const tokenPrefix = 'A'; + await expect(collectionHelper.methods + .createCollection([ + collectionName, + description, + tokenPrefix, + CollectionMode.Fungible, + DECIMALS, + ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, + ]) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); + } + { + const MAX_TOKEN_PREFIX_LENGTH = 16; + const collectionName = 'A'; + const description = 'A'; + const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); + await expect(collectionHelper.methods + .createCollection([ + collectionName, + description, + tokenPrefix, + CollectionMode.Fungible, + DECIMALS, + ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, + ]) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); + } + }); + + itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + const expects = [0n, 1n, 30n].map(async value => { + await expect(collectionHelper.methods + .createCollection([ + 'Peasantry', + 'absolutely anything', + 'TWIW', + CollectionMode.Fungible, + DECIMALS, + ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, + ]) + .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); + }); + await Promise.all(expects); + }); + }); + + describe('Nonfungible collection', () => { + before(async function() { + await usingEthPlaygrounds((helper) => { + requirePalletsOrSkip(this, helper, [Pallets.NFT]); + return Promise.resolve(); + }); + }); + + itEth('Create collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const name = 'CollectionEVM'; + const description = 'Some description'; + const prefix = 'token prefix'; + + // todo:playgrounds this might fail when in async environment. + const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; + const {collectionId, collectionAddress, events} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'nft')).send(); + + expect(events).to.be.deep.equal([ + { + address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F', + event: 'CollectionCreated', + args: { + owner: owner, + collectionId: collectionAddress, + }, + }, + ]); + + const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; + + const collection = helper.nft.getCollectionObject(collectionId); + const data = (await collection.getData())!; + + // Parallel test safety + expect(collectionCountAfter - collectionCountBefore).to.be.gte(1); + expect(collectionId).to.be.eq(collectionCountAfter); + expect(data.name).to.be.eq(name); + expect(data.description).to.be.eq(description); + expect(data.raw.tokenPrefix).to.be.eq(prefix); + expect(data.raw.mode).to.be.eq('NFT'); + + const options = await collection.getOptions(); + + expect(options.tokenPropertyPermissions).to.be.empty; + }); + + // this test will occasionally fail when in async environment. + itEth('Check collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1; + const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId); + const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); + + expect(await collectionHelpers.methods + .isCollectionExist(expectedCollectionAddress) + .call()).to.be.false; + + await collectionHelpers.methods + .createCollection([ + 'A', + 'A', + 'A', + CollectionMode.Nonfungible, + 0, + ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, + ]) + .send({value: Number(2n * helper.balance.getOneTokenNominal())}); + + expect(await collectionHelpers.methods + .isCollectionExist(expectedCollectionAddress) + .call()).to.be.true; + }); + }); + + describe('Create RFT collection from EVM', () => { + before(async function() { + await usingEthPlaygrounds((helper) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + return Promise.resolve(); + }); + }); + + itEth('Create collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const name = 'CollectionEVM'; + const description = 'Some description'; + const prefix = 'token prefix'; + + const {collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'rft')).send(); + const data = (await helper.rft.getData(collectionId))!; + const collection = helper.rft.getCollectionObject(collectionId); + + expect(data.name).to.be.eq(name); + expect(data.description).to.be.eq(description); + expect(data.raw.tokenPrefix).to.be.eq(prefix); + expect(data.raw.mode).to.be.eq('ReFungible'); + + const options = await collection.getOptions(); + + expect(options.tokenPropertyPermissions).to.be.empty; + }); + + itEth('Create collection with properties & get description', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const name = 'CollectionEVM'; + const description = 'Some description'; + const prefix = 'token prefix'; + const baseUri = 'BaseURI'; + + const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri); + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft'); + + const collection = helper.rft.getCollectionObject(collectionId); + const data = (await collection.getData())!; + + expect(data.name).to.be.eq(name); + expect(data.description).to.be.eq(description); + expect(data.raw.tokenPrefix).to.be.eq(prefix); + expect(data.raw.mode).to.be.eq('ReFungible'); + + expect(await contract.methods.description().call()).to.deep.equal(description); + + const options = await collection.getOptions(); + expect(options.tokenPropertyPermissions).to.be.deep.equal([ + { + key: 'URI', + permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, + }, + { + key: 'URISuffix', + permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, + }, + ]); + }); + + itEth('Set sponsorship', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Sponsor', 'absolutely anything', 'ENVY', 'rft')).send(); + + const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + await collection.methods.setCollectionSponsorCross(sponsorCross).send(); + + let data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + + await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor); + await sponsorCollection.methods.confirmCollectionSponsorship().send(); + + data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + }); + + itEth('Collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; + const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); + + expect(await collectionHelpers + .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) + .to.be.false; + + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'rft')).send(); + expect(await collectionHelpers + .methods.isCollectionExist(collectionAddress).call()) + .to.be.true; + + // check collectionOwner: + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); + const collectionOwner = await collectionEvm.methods.collectionOwner().call(); + expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true)); + }); + + itEth('destroyCollection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'OLF', 'rft')).send(); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + + await expect(collectionHelper.methods + .destroyCollection(collectionAddress) + .send({from: owner})).to.be.fulfilled; + + expect(await collectionHelper.methods + .isCollectionExist(collectionAddress) + .call()).to.be.false; + expect(await helper.collection.getData(collectionId)).to.be.null; + }); + + itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + { + const MAX_NAME_LENGTH = 64; + const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); + const description = 'A'; + const tokenPrefix = 'A'; + + await expect(collectionHelper.methods + .createCollection([ + collectionName, + description, + tokenPrefix, + CollectionMode.Refungible, + DECIMALS, + ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, + ]) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); + } + { + const MAX_DESCRIPTION_LENGTH = 256; + const collectionName = 'A'; + const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); + const tokenPrefix = 'A'; + await expect(collectionHelper.methods + .createCollection([ + collectionName, + description, + tokenPrefix, + CollectionMode.Refungible, + DECIMALS, + ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, + ]) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); + } + { + const MAX_TOKEN_PREFIX_LENGTH = 16; + const collectionName = 'A'; + const description = 'A'; + const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); + await expect(collectionHelper.methods + .createCollection([ + collectionName, + description, + tokenPrefix, + CollectionMode.Refungible, + DECIMALS, + ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, + ]) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); + } + }); + + itEth('(!negative test!) Create collection (no funds)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + await expect(collectionHelper.methods + .createCollection([ + 'Peasantry', + 'absolutely anything', + 'TWIW', + CollectionMode.Refungible, + 0, + ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, + ]) + .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); + }); + }); + + describe('Sponsoring', () => { + itEth('Сan remove collection sponsor', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor); + + const {collectionAddress} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'Sponsor collection', + description: '1', + tokenPrefix: '1', + collectionMode: 'nft', + pendingSponsor: sponsorCross, + }, + ).send(); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true; + + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); + expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; + + await collectionEvm.methods.removeCollectionSponsor().send({from: owner}); + + sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); + expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000'); + }); + + itEth('Can sponsor from evm address via access list', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsorEth = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddr(sponsorEth); + + const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'Sponsor collection', + description: '1', + tokenPrefix: '1', + collectionMode: 'nft', + pendingSponsor: sponsorCross, + limits: [{field: CollectionLimitField.SponsoredDataRateLimit, value: 30n}], + tokenPropertyPermissions: [{key: 'key', permissions: [{code: TokenPermissionField.TokenOwner, value: true}]}], + }, + '', + ); + + const collectionSub = helper.nft.getCollectionObject(collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + let sponsorship = (await collectionSub.getData())!.raw.sponsorship; + expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); + // Account cannot confirm sponsorship if it is not set as a sponsor + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + // Sponsor can confirm sponsorship: + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); + sponsorship = (await collectionSub.getData())!.raw.sponsorship; + expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); + + // Create user with no balance: + const user = helper.ethCrossAccount.createAccount(); + const nextTokenId = await collectionEvm.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + + // Set collection permissions: + const oldPermissions = (await collectionSub.getData())!.raw.permissions; + expect(oldPermissions.mintMode).to.be.false; + expect(oldPermissions.access).to.be.equal('Normal'); + + await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); + await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner}); + await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); + + const newPermissions = (await collectionSub.getData())!.raw.permissions; + expect(newPermissions.mintMode).to.be.true; + expect(newPermissions.access).to.be.equal('AllowList'); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); + const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); + + // User can mint token without balance: + { + const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth}); + const event = helper.eth.normalizeEvents(result.events) + .find(event => event.event === 'Transfer'); + + expect(event).to.be.deep.equal({ + address: collectionAddress, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user.eth, + tokenId: '1', + }, + }); + + // await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value1')}]).send({from: user.eth}); + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); + const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); + + expect(await collectionEvm.methods.properties(nextTokenId, []).call()) + .to.be.like([ + [ + 'key', + '0x' + Buffer.from('Value').toString('hex'), + ], + ]); + expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); + expect(userBalanceAfter).to.be.eq(userBalanceBefore); + expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; + } + }); + + itEth('Check that transaction via EVM spend money from sponsor address', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor); + const user = helper.eth.createAccount(); + const userCross = helper.ethCrossAccount.fromAddress(user); + + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'Sponsor collection', + description: '1', + tokenPrefix: '1', + collectionMode: 'nft', + pendingSponsor: sponsorCross, + adminList: [userCross], + }, + '', + ); + + const collectionSub = helper.nft.getCollectionObject(collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + // Set collection sponsor: + let collectionData = (await collectionSub.getData())!; + expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + collectionData = (await collectionSub.getData())!; + expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + + const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); + const tokenId = mintingResult.events.Transfer.returnValues.tokenId; + + const event = helper.eth.normalizeEvents(mintingResult.events) + .find(event => event.event === 'Transfer'); + const address = helper.ethAddress.fromCollectionId(collectionId); + + expect(event).to.be.deep.equal({ + address, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user, + tokenId: '1', + }, + }); + expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI'); + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + }); + + itEth('Can reassign collection sponsor', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsorEth = await helper.eth.createAccountWithBalance(donor); + const sponsorCrossEth = helper.ethCrossAccount.fromAddr(sponsorEth); + const [sponsorSub] = await helper.arrange.createAccounts([100n], donor); + const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub); + + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'Sponsor collection', + description: '1', + tokenPrefix: '1', + collectionMode: 'nft', + pendingSponsor: sponsorCrossEth, + }, + '', + ); + const collectionSub = helper.nft.getCollectionObject(collectionId); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + // Set and confirm sponsor: + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); + + // Can reassign sponsor: + await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner}); + const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship; + expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address}); + }); + + [ + 'transfer', + 'transferCross', + 'transferFrom', + 'transferFromCross', + ].map(testCase => + itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor); + const user = await helper.eth.createAccountWithBalance(donor); + const userCross = helper.ethCrossAccount.fromAddress(user); + + const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'Sponsor collection', + description: '1', + tokenPrefix: '1', + collectionMode: 'rft', + pendingSponsor: sponsorCross, + adminList: [userCross], + }, + '', + ); + const receiver = await helper.eth.createAccountWithBalance(donor); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + + const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); + const tokenId = result.events.Transfer.returnValues.tokenId; + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + + switch (testCase) { + case 'transfer': + await collectionEvm.methods.transfer(receiver, tokenId).send({from: user}); + break; + case 'transferCross': + await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user}); + break; + case 'transferFrom': + await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user}); + break; + case 'transferFromCross': + await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user}); + break; + } + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + expect(userBalanceAfter).to.be.eq(userBalanceBefore); + })); + }); + + describe('Collection admins', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {mode: 'ft' as const, requiredPallets: []}, + ].map(testCase => { + itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => { + // arrange + const owner = await helper.eth.createAccountWithBalance(donor); + const adminSub = await privateKey('//admin2'); + const adminEth = helper.eth.createAccount().toLowerCase(); + + const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); + const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); + + const {collectionAddress, collectionId} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'Mint collection', + description: 'a', + tokenPrefix: 'b', + collectionMode: testCase.mode, + adminList: [adminCrossSub, adminCrossEth], + }, + ).send(); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true); + + // 1. Expect api.rpc.unique.adminlist returns admins: + const adminListRpc = await helper.collection.getAdmins(collectionId); + expect(adminListRpc).to.has.length(2); + expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}]); + + // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist + let adminListEth = await collectionEvm.methods.collectionAdmins().call(); + adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element)); + expect(adminListRpc).to.be.like(adminListEth); + + // 3. check isOwnerOrAdminCross returns true: + expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true; + expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true; + }); + }); + + itEth('cross account admin can mint', async ({helper}) => { + // arrange: create collection and accounts + const owner = await helper.eth.createAccountWithBalance(donor); + const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase(); + const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); + const [adminSub] = await helper.arrange.createAccounts([100n], donor); + const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'Mint collection', + description: 'a', + tokenPrefix: 'b', + collectionMode: 'nft', + adminList: [adminCrossSub, adminCrossEth], + }, + 'uri', + ); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + + // admin (sub and eth) can mint token: + await collectionEvm.methods.mint(owner).send({from: adminEth}); + await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}}); + + expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2); + }); + + itEth('cannot add invalid cross account admin', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const [admin] = await helper.arrange.createAccounts([100n, 100n], donor); + + const adminCross = { + eth: helper.address.substrateToEth(admin.address), + sub: admin.addressRaw, + }; + + await expect(helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: 'nft', + adminList: [adminCross], + }, + ).call()).to.be.rejected; + }); + + itEth('Remove [cross] admin by owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const [adminSub] = await helper.arrange.createAccounts([10n], donor); + const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase(); + const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); + const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); + + const {collectionAddress, collectionId} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: 'nft', + adminList: [adminCrossSub, adminCrossEth], + }, + ).send(); + + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + { + const adminList = await helper.collection.getAdmins(collectionId); + expect(adminList).to.deep.include({Substrate: adminSub.address}); + expect(adminList).to.deep.include({Ethereum: adminEth}); + } + + await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send(); + await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send(); + const adminList = await helper.collection.getAdmins(collectionId); + expect(adminList.length).to.be.eq(0); + + // Non admin cannot mint: + await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/); + await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected; + }); + }); + + describe('Collection limits', () => { + describe('Can set collection limits', () => { + [ + {case: 'nft' as const}, + {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {case: 'ft' as const}, + ].map(testCase => + itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const limits = { + accountTokenOwnershipLimit: 1000n, + sponsoredDataSize: 1024n, + sponsoredDataRateLimit: 30n, + tokenLimit: 1000000n, + sponsorTransferTimeout: 6n, + sponsorApproveTimeout: 6n, + ownerCanTransfer: 1n, + ownerCanDestroy: 0n, + transfersEnabled: 0n, + }; + + const {collectionId, collectionAddress} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'Limits', + description: 'absolutely anything', + tokenPrefix: 'FLO', + collectionMode: testCase.case, + limits: [ + {field: CollectionLimitField.AccountTokenOwnership, value: limits.accountTokenOwnershipLimit}, + {field: CollectionLimitField.SponsoredDataSize, value: limits.sponsoredDataSize}, + {field: CollectionLimitField.SponsoredDataRateLimit, value: limits.sponsoredDataRateLimit}, + {field: CollectionLimitField.TokenLimit, value: limits.tokenLimit}, + {field: CollectionLimitField.SponsorTransferTimeout, value: limits.sponsorTransferTimeout}, + {field: CollectionLimitField.SponsorApproveTimeout, value: limits.sponsorApproveTimeout}, + {field: CollectionLimitField.OwnerCanTransfer, value: limits.ownerCanTransfer}, + {field: CollectionLimitField.OwnerCanDestroy, value: limits.ownerCanDestroy}, + {field: CollectionLimitField.TransferEnabled, value: limits.transfersEnabled}, + ], + }, + ).send(); + + const expectedLimits = { + accountTokenOwnershipLimit: 1000, + sponsoredDataSize: 1024, + sponsoredDataRateLimit: {blocks: 30}, + tokenLimit: 1000000, + sponsorTransferTimeout: 6, + sponsorApproveTimeout: 6, + ownerCanTransfer: true, + ownerCanDestroy: false, + transfersEnabled: false, + }; + + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); + + // Check limits from sub: + const data = (await helper.rft.getData(collectionId))!; + expect(data.raw.limits).to.deep.eq(expectedLimits); + expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits); + // Check limits from eth: + const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner}); + expect(limitsEvm).to.have.length(9); + expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]); + expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]); + expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]); + expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]); + expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]); + expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]); + expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]); + expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]); + expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]); + })); + }); + + describe('(!negative test!) Cannot set invalid collection limits', () => { + [ + {case: 'nft' as const}, + {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {case: 'ft' as const}, + ].map(testCase => + itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { + const invalidLimits = { + accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER), + transfersEnabled: 3, + }; + + const createCollectionData = { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'Limits', + description: 'absolutely anything', + tokenPrefix: 'ISNI', + collectionMode: testCase.case, + }; + + const owner = await helper.eth.createAccountWithBalance(donor); + await expect(helper.eth.createCollection( + owner, + { + ...createCollectionData, + limits: [{field: 9 as CollectionLimitField, value: 1n}], + }, + ).call()).to.be.rejectedWith('value not convertible into enum "CollectionLimitField"'); + + await expect(helper.eth.createCollection( + owner, + { + ...createCollectionData, + limits: [{field: CollectionLimitField.AccountTokenOwnership, value: invalidLimits.accountTokenOwnershipLimit}], + }, + ).call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`); + + await expect(helper.eth.createCollection( + owner, + { + ...createCollectionData, + limits: [{field: CollectionLimitField.TransferEnabled, value: 3n}], + }, + ).call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`); + + await expect(helper.eth.createCollection( + owner, + { + ...createCollectionData, + limits: [{field: CollectionLimitField.SponsoredDataSize, value: -1n}], + }, + ).call()).to.be.rejectedWith('value out-of-bounds'); + })); + }); + }); + + describe('Collection properties', () => { + + [ + {mode: 'nft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, + {mode: 'rft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, + {mode: 'ft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, + ].map(testCase => + itEth.ifWithPallets(`Collection properties can be set for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const callerCross = helper.ethCrossAccount.fromAddress(caller); + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId, collectionAddress} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'name', + description: 'test', + tokenPrefix: 'test', + collectionMode: testCase.mode, + adminList: [callerCross], + properties: testCase.methodParams, + }, + ).send(); + + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller); + + const raw = (await helper[testCase.mode].getData(collectionId))?.raw; + expect(raw.properties).to.deep.equal(testCase.expectedProps); + + // collectionProperties returns properties: + expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value))); + })); + + [ + {mode: 'nft' as const}, + {mode: 'rft' as const}, + {mode: 'ft' as const}, + ].map(testCase => + itEth.ifWithPallets(`Collection properties can be deleted for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const callerCross = helper.ethCrossAccount.fromAddress(caller); + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId, collectionAddress} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'name', + description: 'test', + tokenPrefix: 'test', + collectionMode: testCase.mode, + adminList: [callerCross], + properties:[ + {key: 'testKey1', value: Buffer.from('testValue1')}, + {key: 'testKey2', value: Buffer.from('testValue2')}, + {key: 'testKey3', value: Buffer.from('testValue3')}], + }, + ).send(); + + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller); + + await collectionEvm.methods.deleteCollectionProperties(['testKey1', 'testKey2']).send({from: caller}); + + const raw = (await helper[testCase.mode].getData(collectionId))?.raw; + + expect(raw.properties.length).to.equal(1); + expect(raw.properties).to.deep.equal([{key: 'testKey3', value: 'testValue3'}]); + })); + + itEth('(!negative test!) Cannot set invalid properties', async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const callerCross = helper.ethCrossAccount.fromAddress(caller); + const owner = await helper.eth.createAccountWithBalance(donor); + const createCollectionData = { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'name', + description: 'test', + tokenPrefix: 'test', + collectionMode: 'nft' as TCollectionMode, + adminList: [callerCross], + }; + await expect(helper.eth.createCollection( + owner, + { + ...createCollectionData, + properties: [{key: '', value: Buffer.from('val1')}], + }, + ).call()).to.be.rejected; + + await expect(helper.eth.createCollection( + owner, + { + ...createCollectionData, + properties: [{key: 'déjà vu', value: Buffer.from('hmm...')}], + }, + ).call()).to.be.rejected; + + await expect(helper.eth.createCollection( + owner, + { + ...createCollectionData, + properties: [{key: 'a'.repeat(257), value: Buffer.from('val3')}], + }, + ).call()).to.be.rejected; + }); + + itEth('(!negative test!) cannot delete properties of non-owned collections', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'name', + description: 'test', + tokenPrefix: 'test', + collectionMode: 'nft', + properties:[ + {key: 'testKey1', value: Buffer.from('testValue1')}, + {key: 'testKey2', value: Buffer.from('testValue2')}], + }, + ).send(); + + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); + + await expect(collectionEvm.methods.deleteCollectionProperties(['testKey2']).send({from: caller})).to.be.rejected; + }); + }); + + describe('Token property permissions', () => { + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.ethCrossAccount.createAccountWithBalance(donor); + for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) { + const {collectionId, collectionAddress} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: testCase.mode, + adminList: [caller], + tokenPropertyPermissions: [ + { + key: 'testKey', + permissions: [ + {code: TokenPermissionField.Mutable, value: mutable}, + {code: TokenPermissionField.TokenOwner, value: tokenOwner}, + {code: TokenPermissionField.CollectionAdmin, value: collectionAdmin}, + ], + }, + ], + }, + ).send(); + const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + + expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{ + key: 'testKey', + permission: {mutable, collectionAdmin, tokenOwner}, + }]); + + expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([ + ['testKey', [ + [TokenPermissionField.Mutable.toString(), mutable], + [TokenPermissionField.TokenOwner.toString(), tokenOwner], + [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]], + ], + ]); + } + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions`, testCase.requiredPallets, async({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId, collectionAddress} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: testCase.mode, + tokenPropertyPermissions: [ + { + key: 'testKey_0', + permissions: [ + {code: TokenPermissionField.Mutable, value: true}, + {code: TokenPermissionField.TokenOwner, value: true}, + {code: TokenPermissionField.CollectionAdmin, value: true}], + }, + { + key: 'testKey_1', + permissions: [ + {code: TokenPermissionField.Mutable, value: true}, + {code: TokenPermissionField.TokenOwner, value: false}, + {code: TokenPermissionField.CollectionAdmin, value: true}], + }, + { + key: 'testKey_2', + permissions: [ + {code: TokenPermissionField.Mutable, value: false}, + {code: TokenPermissionField.TokenOwner, value: true}, + {code: TokenPermissionField.CollectionAdmin, value: false}], + }, + ], + }, + ).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + + expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([ + { + key: 'testKey_0', + permission: {mutable: true, tokenOwner: true, collectionAdmin: true}, + }, + { + key: 'testKey_1', + permission: {mutable: true, tokenOwner: false, collectionAdmin: true}, + }, + { + key: 'testKey_2', + permission: {mutable: false, tokenOwner: true, collectionAdmin: false}, + }, + ]); + + expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([ + ['testKey_0', [ + [TokenPermissionField.Mutable.toString(), true], + [TokenPermissionField.TokenOwner.toString(), true], + [TokenPermissionField.CollectionAdmin.toString(), true]], + ], + ['testKey_1', [ + [TokenPermissionField.Mutable.toString(), true], + [TokenPermissionField.TokenOwner.toString(), false], + [TokenPermissionField.CollectionAdmin.toString(), true]], + ], + ['testKey_2', [ + [TokenPermissionField.Mutable.toString(), false], + [TokenPermissionField.TokenOwner.toString(), true], + [TokenPermissionField.CollectionAdmin.toString(), false]], + ], + ]); + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`Can be deleted for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollection( + caller, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: testCase.mode, + adminList: [receiver], + tokenPropertyPermissions: [ + { + key: 'testKey', + permissions: [ + {code: TokenPermissionField.Mutable, value: true}, + {code: TokenPermissionField.CollectionAdmin, value: true}], + }, + { + key: 'testKey_1', + permissions: [ + {code: TokenPermissionField.Mutable, value: true}, + {code: TokenPermissionField.CollectionAdmin, value: true}], + }, + ], + }, + ).send(); + + const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller); + const tokenId = (await collection.methods.mintCross(receiver, [{key: 'testKey', value: Buffer.from('testValue')}, {key: 'testKey_1', value: Buffer.from('testValue_1')}]).send()).events.Transfer.returnValues.tokenId; + expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(2); + + await collection.methods.deleteProperties(tokenId, ['testKey', 'testKey_1']).send({from: caller}); + expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(0); + })); + }); + + describe('Nesting', () => { + itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: 'nft', + nestingSettings: {token_owner: true, collection_admin: false, restricted: []}, + }, + ).send(); + + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + // Create a token to be nested to + const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner}); + const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId; + const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId); + + // Create a nested token + const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner}); + const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId; + expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress); + + // Create a token to be nested and nest + const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner}); + const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId; + + await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner}); + expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress); + + // Unnest token back + await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner}); + expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner); + }); + + itEth('NFT: collectionNesting()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress: unnestedCollectionAddress} = await helper.eth.createCollection( + owner, + new CreateCollectionData('A', 'B', 'C', 'nft'), + ).send(); + + const unnestedContract = helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner); + expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]); + + const {collectionAddress} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: 'nft', + nestingSettings: {token_owner: true, collection_admin: false, restricted: [unnestedCollectionAddress.toString()]}, + }, + ).send(); + + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress.toString()]]); + await contract.methods.setCollectionNesting([false, false, []]).send({from: owner}); + expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]); + }); + + itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionId, collectionAddress} = await helper.eth.createCollection( + owner, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: 'nft', + nestingSettings: {token_owner: false, collection_admin: false, restricted: []}, + }, + ).send(); + + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + // Create a token to nest into + const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner}); + const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; + const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId); + + // Create a token to nest + const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner}); + const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId; + + // Try to nest + await expect(contract.methods + .transfer(targetNftTokenAddress, nftTokenId) + .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest'); + }); + }); + + describe('Flags', () => { + const createCollectionData = { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: 'nft' as TCollectionMode, + }; + + itEth('NFT: use numbers for flags', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + { + const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 0}).send(); + expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false}); + } + + { + const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 64}).send(); + expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true}); + } + }); + + itEth('NFT: can\'t set foreign flag number', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + { + await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 128}).call({from: owner})).to.be.rejectedWith(/internal flags were used/); + } + + { + await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 192}).call({from: owner})).to.be.rejectedWith(/internal flags were used/); + } + }); + + itEth('NFT: use enum for flags', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + { + const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata]}).send(); + expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true}); + } + }); + + itEth('NFT: foreign flag enum is ignored', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + { + await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/); + } + + { + await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata | CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/); + } + }); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/createFTCollection.seqtest.ts @@ -0,0 +1,76 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {Pallets, requirePalletsOrSkip} from '../util/index.js'; +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; + +const DECIMALS = 18; + +describe('Create FT collection from EVM', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.Fungible]); + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Create collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const name = 'CollectionEVM'; + const description = 'Some description'; + const prefix = 'token prefix'; + + // todo:playgrounds this might fail when in async environment. + const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; + + const {collectionId} = await helper.eth.createFungibleCollection(owner, name, DECIMALS, description, prefix); + + const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; + const data = (await helper.ft.getData(collectionId))!; + + expect(collectionCountAfter - collectionCountBefore).to.be.eq(1); + expect(collectionId).to.be.eq(collectionCountAfter); + expect(data.name).to.be.eq(name); + expect(data.description).to.be.eq(description); + expect(data.raw.tokenPrefix).to.be.eq(prefix); + expect(data.raw.mode).to.be.deep.eq({Fungible: DECIMALS.toString()}); + }); + + // todo:playgrounds this test will fail when in async environment. + itEth('Check collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1; + const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId); + const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); + + expect(await collectionHelpers.methods + .isCollectionExist(expectedCollectionAddress) + .call()).to.be.false; + + + await helper.eth.createFungibleCollection(owner, 'A', DECIMALS, 'A', 'A'); + + + expect(await collectionHelpers.methods + .isCollectionExist(expectedCollectionAddress) + .call()).to.be.true; + }); +}); --- /dev/null +++ b/js-packages/tests/eth/createFTCollection.test.ts @@ -0,0 +1,237 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {evmToAddress} from '@polkadot/util-crypto'; +import {Pallets, requirePalletsOrSkip} from '../util/index.js'; +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import {CollectionLimitField} from './util/playgrounds/types.js'; + +const DECIMALS = 18; + +describe('Create FT collection from EVM', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.Fungible]); + donor = await privateKey({url: import.meta.url}); + }); + }); + + // TODO move sponsorship tests to another file: + // Soft-deprecated + itEth('[eth] Set sponsorship', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const description = 'absolutely anything'; + + const {collectionId, collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', DECIMALS, description, 'ENVY'); + + const collection = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); + await collection.methods.setCollectionSponsor(sponsor).send(); + + let data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + + await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true); + await sponsorCollection.methods.confirmCollectionSponsorship().send(); + + data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + }); + + itEth('[cross] Set sponsorship', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const description = 'absolutely anything'; + const {collectionId, collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', DECIMALS, description, 'ENVY'); + + const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + await collection.methods.setCollectionSponsorCross(sponsorCross).send(); + + let data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + + await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor); + await sponsorCollection.methods.confirmCollectionSponsorship().send(); + + data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + expect(await collection.methods.description().call()).to.deep.equal(description); + }); + + itEth('Collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; + const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); + + expect(await collectionHelpers + .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) + .to.be.false; + + const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT'); + expect(await collectionHelpers + .methods.isCollectionExist(collectionAddress).call()) + .to.be.true; + + // check collectionOwner: + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); + const collectionOwner = await collectionEvm.methods.collectionOwner().call(); + expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true)); + }); + + itEth('destroyCollection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT'); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + + const result = await collectionHelper.methods + .destroyCollection(collectionAddress) + .send({from: owner}); + + const events = helper.eth.normalizeEvents(result.events); + + expect(events).to.be.deep.equal([ + { + address: collectionHelper.options.address, + event: 'CollectionDestroyed', + args: { + collectionId: collectionAddress, + }, + }, + ]); + + expect(await collectionHelper.methods + .isCollectionExist(collectionAddress) + .call()).to.be.false; + expect(await helper.collection.getData(collectionId)).to.be.null; + }); +}); + +describe('(!negative tests!) Create FT collection from EVM', () => { + let donor: IKeyringPair; + let nominal: bigint; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.Fungible]); + donor = await privateKey({url: import.meta.url}); + nominal = helper.balance.getOneTokenNominal(); + }); + }); + + itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + { + const MAX_NAME_LENGTH = 64; + const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); + const description = 'A'; + const tokenPrefix = 'A'; + + await expect(collectionHelper.methods + .createFTCollection(collectionName, DECIMALS, description, tokenPrefix) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); + } + { + const MAX_DESCRIPTION_LENGTH = 256; + const collectionName = 'A'; + const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); + const tokenPrefix = 'A'; + await expect(collectionHelper.methods + .createFTCollection(collectionName, DECIMALS, description, tokenPrefix) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); + } + { + const MAX_TOKEN_PREFIX_LENGTH = 16; + const collectionName = 'A'; + const description = 'A'; + const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); + await expect(collectionHelper.methods + .createFTCollection(collectionName, DECIMALS, description, tokenPrefix) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); + } + }); + + itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + const expects = [0n, 1n, 30n].map(async value => { + await expect(collectionHelper.methods + .createFTCollection('Peasantry', DECIMALS, 'absolutely anything', 'TWIW') + .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); + }); + await Promise.all(expects); + }); + + // Soft-deprecated + itEth('(!negative test!) [eth] Check owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const peasant = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE'); + const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant, true); + const EXPECTED_ERROR = 'NoPermission'; + { + const sponsor = await helper.eth.createAccountWithBalance(donor); + await expect(peasantCollection.methods + .setCollectionSponsor(sponsor) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor, true); + await expect(sponsorCollection.methods + .confirmCollectionSponsorship() + .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + } + { + await expect(peasantCollection.methods + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + } + }); + + itEth('(!negative test!) [cross] Check owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const peasant = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE'); + const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant); + const EXPECTED_ERROR = 'NoPermission'; + { + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + await expect(peasantCollection.methods + .setCollectionSponsorCross(sponsorCross) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor); + await expect(sponsorCollection.methods + .confirmCollectionSponsorship() + .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + } + { + await expect(peasantCollection.methods + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + } + }); +}); --- /dev/null +++ b/js-packages/tests/eth/createNFTCollection.seqtest.ts @@ -0,0 +1,89 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; + + +describe('Create NFT collection from EVM', () => { + let donor: IKeyringPair; + + before(async function () { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Create collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const name = 'CollectionEVM'; + const description = 'Some description'; + const prefix = 'token prefix'; + + // todo:playgrounds this might fail when in async environment. + const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; + const {collectionId, collectionAddress, events} = await helper.eth.createNFTCollection(owner, name, description, prefix); + + expect(events).to.be.deep.equal([ + { + address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F', + event: 'CollectionCreated', + args: { + owner: owner, + collectionId: collectionAddress, + }, + }, + ]); + + const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; + + const collection = helper.nft.getCollectionObject(collectionId); + const data = (await collection.getData())!; + + expect(collectionCountAfter - collectionCountBefore).to.be.eq(1); + expect(collectionId).to.be.eq(collectionCountAfter); + expect(data.name).to.be.eq(name); + expect(data.description).to.be.eq(description); + expect(data.raw.tokenPrefix).to.be.eq(prefix); + expect(data.raw.mode).to.be.eq('NFT'); + + const options = await collection.getOptions(); + + expect(options.tokenPropertyPermissions).to.be.empty; + }); + + // this test will occasionally fail when in async environment. + itEth('Check collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1; + const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId); + const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); + + expect(await collectionHelpers.methods + .isCollectionExist(expectedCollectionAddress) + .call()).to.be.false; + + await collectionHelpers.methods + .createNFTCollection('A', 'A', 'A') + .send({value: Number(2n * helper.balance.getOneTokenNominal())}); + + expect(await collectionHelpers.methods + .isCollectionExist(expectedCollectionAddress) + .call()).to.be.true; + }); +}); --- /dev/null +++ b/js-packages/tests/eth/createNFTCollection.test.ts @@ -0,0 +1,275 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {evmToAddress} from '@polkadot/util-crypto'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import {CollectionLimitField} from './util/playgrounds/types.js'; + + +describe('Create NFT collection from EVM', () => { + let donor: IKeyringPair; + + before(async function () { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Create collection with properties & get desctription', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const name = 'CollectionEVM'; + const description = 'Some description'; + const prefix = 'token prefix'; + const baseUri = 'BaseURI'; + + const {collectionId, collectionAddress, events} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, name, description, prefix, baseUri); + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft'); + + expect(events).to.be.deep.equal([ + { + address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F', + event: 'CollectionCreated', + args: { + owner: owner, + collectionId: collectionAddress, + }, + }, + ]); + + const collection = helper.nft.getCollectionObject(collectionId); + const data = (await collection.getData())!; + + expect(data.name).to.be.eq(name); + expect(data.description).to.be.eq(description); + expect(data.raw.tokenPrefix).to.be.eq(prefix); + expect(data.raw.mode).to.be.eq('NFT'); + + expect(await contract.methods.description().call()).to.deep.equal(description); + + const options = await collection.getOptions(); + expect(options.tokenPropertyPermissions).to.be.deep.equal([ + { + key: 'URI', + permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, + }, + { + key: 'URISuffix', + permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, + }, + ]); + }); + + // Soft-deprecated + itEth('[eth] Set sponsorship', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC'); + + const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + await collection.methods.setCollectionSponsor(sponsor).send(); + + let data = (await helper.nft.getData(collectionId))!; + expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + + await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true); + await sponsorCollection.methods.confirmCollectionSponsorship().send(); + + data = (await helper.nft.getData(collectionId))!; + expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + }); + + itEth('[cross] Set sponsorship & get description', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const description = 'absolutely anything'; + const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', description, 'ROC'); + + const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + await collection.methods.setCollectionSponsorCross(sponsorCross).send(); + + let data = (await helper.nft.getData(collectionId))!; + expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + + await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor); + await sponsorCollection.methods.confirmCollectionSponsorship().send(); + + data = (await helper.nft.getData(collectionId))!; + expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + + expect(await sponsorCollection.methods.description().call()).to.deep.equal(description); + }); + + itEth('Collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; + const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); + + expect(await collectionHelpers + .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) + .to.be.false; + + const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Exister', 'absolutely anything', 'EVC'); + expect(await collectionHelpers + .methods.isCollectionExist(collectionAddress).call()) + .to.be.true; + + // check collectionOwner: + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); + const collectionOwner = await collectionEvm.methods.collectionOwner().call(); + expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true)); + }); +}); + +describe('(!negative tests!) Create NFT collection from EVM', () => { + let donor: IKeyringPair; + let nominal: bigint; + + before(async function () { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + nominal = helper.balance.getOneTokenNominal(); + }); + }); + + itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + { + const MAX_NAME_LENGTH = 64; + const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); + const description = 'A'; + const tokenPrefix = 'A'; + + await expect(collectionHelper.methods + .createNFTCollection(collectionName, description, tokenPrefix) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); + + } + { + const MAX_DESCRIPTION_LENGTH = 256; + const collectionName = 'A'; + const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); + const tokenPrefix = 'A'; + await expect(collectionHelper.methods + .createNFTCollection(collectionName, description, tokenPrefix) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); + } + { + const MAX_TOKEN_PREFIX_LENGTH = 16; + const collectionName = 'A'; + const description = 'A'; + const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); + await expect(collectionHelper.methods + .createNFTCollection(collectionName, description, tokenPrefix) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); + } + }); + + itEth('(!negative test!) Create collection (no funds)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + await expect(collectionHelper.methods + .createNFTCollection('Peasantry', 'absolutely anything', 'CVE') + .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); + }); + + // Soft-deprecated + itEth('(!negative test!) [eth] Check owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const malfeasant = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR'); + const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant, true); + const EXPECTED_ERROR = 'NoPermission'; + { + const sponsor = await helper.eth.createAccountWithBalance(donor); + await expect(malfeasantCollection.methods + .setCollectionSponsor(sponsor) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true); + await expect(sponsorCollection.methods + .confirmCollectionSponsorship() + .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + } + { + await expect(malfeasantCollection.methods + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + } + }); + + itEth('(!negative test!) [cross] Check owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const malfeasant = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR'); + const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant); + const EXPECTED_ERROR = 'NoPermission'; + { + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + await expect(malfeasantCollection.methods + .setCollectionSponsorCross(sponsorCross) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor); + await expect(sponsorCollection.methods + .confirmCollectionSponsorship() + .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + } + { + await expect(malfeasantCollection.methods + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + } + }); + + itEth('destroyCollection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF'); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + + + const result = await collectionHelper.methods + .destroyCollection(collectionAddress) + .send({from: owner}); + + const events = helper.eth.normalizeEvents(result.events); + + expect(events).to.be.deep.equal([ + { + address: collectionHelper.options.address, + event: 'CollectionDestroyed', + args: { + collectionId: collectionAddress, + }, + }, + ]); + + expect(await collectionHelper.methods + .isCollectionExist(collectionAddress) + .call()).to.be.false; + expect(await helper.collection.getData(collectionId)).to.be.null; + }); +}); --- /dev/null +++ b/js-packages/tests/eth/createRFTCollection.test.ts @@ -0,0 +1,273 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {evmToAddress} from '@polkadot/util-crypto'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {Pallets, requirePalletsOrSkip} from '../util/index.js'; +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import {CollectionLimitField} from './util/playgrounds/types.js'; + + +describe('Create RFT collection from EVM', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Create collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const name = 'CollectionEVM'; + const description = 'Some description'; + const prefix = 'token prefix'; + + const {collectionId} = await helper.eth.createRFTCollection(owner, name, description, prefix); + const data = (await helper.rft.getData(collectionId))!; + const collection = helper.rft.getCollectionObject(collectionId); + + expect(data.name).to.be.eq(name); + expect(data.description).to.be.eq(description); + expect(data.raw.tokenPrefix).to.be.eq(prefix); + expect(data.raw.mode).to.be.eq('ReFungible'); + + const options = await collection.getOptions(); + + expect(options.tokenPropertyPermissions).to.be.empty; + }); + + + + itEth('Create collection with properties & get description', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const name = 'CollectionEVM'; + const description = 'Some description'; + const prefix = 'token prefix'; + const baseUri = 'BaseURI'; + + const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri); + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft'); + + const collection = helper.rft.getCollectionObject(collectionId); + const data = (await collection.getData())!; + + expect(data.name).to.be.eq(name); + expect(data.description).to.be.eq(description); + expect(data.raw.tokenPrefix).to.be.eq(prefix); + expect(data.raw.mode).to.be.eq('ReFungible'); + + expect(await contract.methods.description().call()).to.deep.equal(description); + + const options = await collection.getOptions(); + expect(options.tokenPropertyPermissions).to.be.deep.equal([ + { + key: 'URI', + permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, + }, + { + key: 'URISuffix', + permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, + }, + ]); + }); + + // Soft-deprecated + itEth('[eth] Set sponsorship', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY'); + + const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, true); + await collection.methods.setCollectionSponsor(sponsor).send(); + + let data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + + await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true); + await sponsorCollection.methods.confirmCollectionSponsorship().send(); + + data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + }); + + itEth('[cross] Set sponsorship', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY'); + + const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + await collection.methods.setCollectionSponsorCross(sponsorCross).send(); + + let data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + + await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor); + await sponsorCollection.methods.confirmCollectionSponsorship().send(); + + data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + }); + + itEth('Collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; + const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); + + expect(await collectionHelpers + .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) + .to.be.false; + + const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Exister', 'absolutely anything', 'WIWT'); + expect(await collectionHelpers + .methods.isCollectionExist(collectionAddress).call()) + .to.be.true; + + // check collectionOwner: + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); + const collectionOwner = await collectionEvm.methods.collectionOwner().call(); + expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true)); + }); +}); + +describe('(!negative tests!) Create RFT collection from EVM', () => { + let donor: IKeyringPair; + let nominal: bigint; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + donor = await privateKey({url: import.meta.url}); + nominal = helper.balance.getOneTokenNominal(); + }); + }); + + itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + { + const MAX_NAME_LENGTH = 64; + const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); + const description = 'A'; + const tokenPrefix = 'A'; + + await expect(collectionHelper.methods + .createRFTCollection(collectionName, description, tokenPrefix) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); + } + { + const MAX_DESCRIPTION_LENGTH = 256; + const collectionName = 'A'; + const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); + const tokenPrefix = 'A'; + await expect(collectionHelper.methods + .createRFTCollection(collectionName, description, tokenPrefix) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); + } + { + const MAX_TOKEN_PREFIX_LENGTH = 16; + const collectionName = 'A'; + const description = 'A'; + const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); + await expect(collectionHelper.methods + .createRFTCollection(collectionName, description, tokenPrefix) + .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); + } + }); + + itEth('(!negative test!) Create collection (no funds)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + await expect(collectionHelper.methods + .createRFTCollection('Peasantry', 'absolutely anything', 'TWIW') + .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); + }); + + // Soft-deprecated + itEth('(!negative test!) [eth] Check owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const peasant = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE'); + const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant, true); + const EXPECTED_ERROR = 'NoPermission'; + { + const sponsor = await helper.eth.createAccountWithBalance(donor); + await expect(peasantCollection.methods + .setCollectionSponsor(sponsor) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true); + await expect(sponsorCollection.methods + .confirmCollectionSponsorship() + .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + } + { + await expect(peasantCollection.methods + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + } + }); + + itEth('(!negative test!) [cross] Check owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const peasant = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE'); + const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant); + const EXPECTED_ERROR = 'NoPermission'; + { + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + await expect(peasantCollection.methods + .setCollectionSponsorCross(sponsorCross) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor); + await expect(sponsorCollection.methods + .confirmCollectionSponsorship() + .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); + } + { + await expect(peasantCollection.methods + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) + .call()).to.be.rejectedWith(EXPECTED_ERROR); + } + }); + + itEth('destroyCollection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress, collectionId} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'OLF'); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + + await expect(collectionHelper.methods + .destroyCollection(collectionAddress) + .send({from: owner})).to.be.fulfilled; + + expect(await collectionHelper.methods + .isCollectionExist(collectionAddress) + .call()).to.be.false; + expect(await helper.collection.getData(collectionId)).to.be.null; + }); +}); --- /dev/null +++ b/js-packages/tests/eth/crossTransfer.test.ts @@ -0,0 +1,105 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {itEth, usingEthPlaygrounds} from './util/index.js'; +import {CrossAccountId} from '@unique/playgrounds/unique.js'; +import type {IKeyringPair} from '@polkadot/types/types'; + +describe('Token transfer between substrate address and EVM address. Fungible', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + }); + }); + + itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => { + const bobCA = CrossAccountId.fromKeyring(bob).toICrossAccountId(); + const charlieCA = CrossAccountId.fromKeyring(charlie).toICrossAccountId(); + const charlieCAEth = CrossAccountId.fromKeyring(charlie).toEthereum().toICrossAccountId(); + + const collection = await helper.ft.mintCollection(alice); + await collection.setLimits(alice, {ownerCanTransfer: true}); + + await collection.mint(alice, 200n); + await collection.transfer(alice, charlieCAEth, 200n); + await collection.transferFrom(alice, charlieCAEth, charlieCA, 50n); + await collection.transfer(charlie, bobCA, 50n); + }); + + itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => { + const aliceProxy = await helper.eth.createAccountWithBalance(donor); + const bobProxy = await helper.eth.createAccountWithBalance(donor); + + const collection = await helper.ft.mintCollection(alice); + await collection.setLimits(alice, {ownerCanTransfer: true}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'ft', aliceProxy); + + await collection.mint(alice, 200n, {Ethereum: aliceProxy}); + await contract.methods.transfer(bobProxy, 50).send({from: aliceProxy}); + await collection.transferFrom(alice, {Ethereum: bobProxy}, CrossAccountId.fromKeyring(bob).toICrossAccountId(), 50n); + await collection.transfer(bob, CrossAccountId.fromKeyring(alice).toICrossAccountId(), 50n); + }); +}); + +describe('Token transfer between substrate address and EVM address. NFT', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + }); + }); + + itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => { + const charlieEth = CrossAccountId.fromKeyring(charlie, 'Ethereum').toICrossAccountId(); + + const collection = await helper.nft.mintCollection(alice); + await collection.setLimits(alice, {ownerCanTransfer: true}); + const token = await collection.mintToken(alice); + await token.transfer(alice, charlieEth); + await token.transferFrom(alice, charlieEth, CrossAccountId.fromKeyring(charlie).toICrossAccountId()); + await token.transfer(charlie, CrossAccountId.fromKeyring(bob).toICrossAccountId()); + }); + + itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => { + const aliceProxy = await helper.eth.createAccountWithBalance(donor); + const bobProxy = await helper.eth.createAccountWithBalance(donor); + + const collection = await helper.nft.mintCollection(alice); + await collection.setLimits(alice, {ownerCanTransfer: true}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft', aliceProxy); + + const token = await collection.mintToken(alice); + await token.transfer(alice, {Ethereum: aliceProxy}); + await contract.methods.transfer(bobProxy, 1).send({from: aliceProxy}); + await token.transferFrom(alice, {Ethereum: bobProxy}, {Substrate: bob.address}); + await token.transfer(bob, {Substrate: charlie.address}); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/destroyCollection.test.ts @@ -0,0 +1,63 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {Pallets} from '../util/index.js'; +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import type {TCollectionMode} from '@unique/playgrounds/types.js'; +import {CreateCollectionData} from './util/playgrounds/types.js'; + +describe('Destroy Collection from EVM', function() { + let donor: IKeyringPair; + const testCases = [ + {case: 'rft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'rft'], requiredPallets: [Pallets.ReFungible]}, + {case: 'nft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'nft'], requiredPallets: [Pallets.NFT]}, + {case: 'ft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'ft', 18], requiredPallets: [Pallets.Fungible]}, + ]; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + testCases.map((testCase) => + itEth.ifWithPallets(`Cannot burn non-owned or non-existing collection ${testCase.case}`, testCase.requiredPallets, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const signer = await helper.eth.createAccountWithBalance(donor); + + const unexistedCollection = helper.ethAddress.fromCollectionId(1000000); + + const collectionHelpers = await helper.ethNativeContract.collectionHelpers(signer); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData(...testCase.params as [string, string, string, TCollectionMode, number?])).send(); + // cannot burn collec + await expect(collectionHelpers.methods + .destroyCollection(collectionAddress) + .send({from: signer})).to.be.rejected; + + await expect(collectionHelpers.methods + .destroyCollection(unexistedCollection) + .send({from: signer})).to.be.rejected; + + expect(await collectionHelpers.methods + .isCollectionExist(unexistedCollection) + .call()).to.be.false; + + expect(await collectionHelpers.methods + .isCollectionExist(collectionAddress) + .call()).to.be.true; + })); +}); --- /dev/null +++ b/js-packages/tests/eth/ethFeesAreCorrect.test.ts @@ -0,0 +1,66 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; + +describe('Eth fees are correct', () => { + let donor: IKeyringPair; + let minter: IKeyringPair; + let alice: IKeyringPair; + + before(async () => { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [minter, alice] = await helper.arrange.createAccounts([100n, 200n], donor); + }); + }); + + + itEth('web3 fees are the same as evm.call fees', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {}); + + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const aliceEth = helper.address.substrateToEth(alice.address); + + const {tokenId: tokenA} = await collection.mintToken(minter, {Ethereum: owner}); + const {tokenId: tokenB} = await collection.mintToken(minter, {Ethereum: aliceEth}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + const balanceBeforeWeb3Transfer = await helper.balance.getEthereum(owner); + await contract.methods.transfer(receiver, tokenA).send({from: owner, maxFeePerGas: ((await helper.eth.getGasPrice())!).toString()}); + const balanceAfterWeb3Transfer = await helper.balance.getEthereum(owner); + const web3Diff = balanceBeforeWeb3Transfer - balanceAfterWeb3Transfer; + + const encodedCall = contract.methods.transfer(receiver, tokenB) + .encodeABI(); + + const balanceBeforeEvmCall = await helper.balance.getSubstrate(alice.address); + await helper.eth.sendEVM( + alice, + collectionAddress, + encodedCall, + '0', + ); + const balanceAfterEvmCall = await helper.balance.getSubstrate(alice.address); + const evmCallDiff = balanceBeforeEvmCall - balanceAfterEvmCall; + + expect(web3Diff).to.be.equal(evmCallDiff); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/events.test.ts @@ -0,0 +1,548 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {expect} from 'chai'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {itEth, usingEthPlaygrounds} from './util/index.js'; +import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; +import type {IEvent, TCollectionMode} from '@unique/playgrounds/types.js'; +import {Pallets, requirePalletsOrSkip} from '../util/index.js'; +import {CollectionLimitField, TokenPermissionField, CreateCollectionData} from './util/playgrounds/types.js'; +import type {NormalizedEvent} from './util/playgrounds/types.js'; + +let donor: IKeyringPair; + +before(async function () { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); +}); + +function clearEvents(ethEvents: NormalizedEvent[] | null, subEvents: IEvent[]) { + if(ethEvents !== null) { + ethEvents.splice(0); + } + subEvents.splice(0); +} + +async function testCollectionCreatedAndDestroy(helper: EthUniqueHelper, mode: TCollectionMode) { + const owner = await helper.eth.createAccountWithBalance(donor); + const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionCreated', 'CollectionDestroyed']}]); + const {collectionAddress, events: ethEvents} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); + + await helper.wait.newBlocks(1); + { + expect(ethEvents).to.containSubset([ + { + event: 'CollectionCreated', + args: { + owner: owner, + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'CollectionCreated'}]); + clearEvents(ethEvents, subEvents); + } + { + const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); + const result = await collectionHelper.methods.destroyCollection(collectionAddress).send({from:owner}); + await helper.wait.newBlocks(1); + expect(result.events).to.containSubset({ + CollectionDestroyed: { + returnValues: { + collectionId: collectionAddress, + }, + }, + }); + expect(subEvents).to.containSubset([{method: 'CollectionDestroyed'}]); + } + unsubscribe(); +} + +async function testCollectionPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); + const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); + + const ethEvents: any = []; + collectionHelper.events.allEvents((_: any, event: any) => { + ethEvents.push(event); + }); + const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionPropertySet', 'CollectionPropertyDeleted']}]); + { + await collection.methods.setCollectionProperties([{key: 'A', value: [0,1,2,3]}]).send({from:owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'CollectionPropertySet'}]); + clearEvents(ethEvents, subEvents); + } + { + await collection.methods.deleteCollectionProperties(['A']).send({from:owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'CollectionPropertyDeleted'}]); + } + unsubscribe(); +} + +async function testPropertyPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); + const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); + const ethEvents: any = []; + collectionHelper.events.allEvents((_: any, event: any) => { + ethEvents.push(event); + }); + const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['PropertyPermissionSet']}]); + await collection.methods.setTokenPropertyPermissions([ + ['A', [ + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], + ], + ]).send({from: owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'PropertyPermissionSet'}]); + unsubscribe(); +} + +async function testAllowListAddressAddedAndRemoved(helper: EthUniqueHelper, mode: TCollectionMode) { + const owner = await helper.eth.createAccountWithBalance(donor); + const user = helper.ethCrossAccount.createAccount(); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); + const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); + const ethEvents: any[] = []; + collectionHelper.events.allEvents((_: any, event: any) => { + ethEvents.push(event); + }); + + const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['AllowListAddressAdded', 'AllowListAddressRemoved']}]); + { + await collection.methods.addToCollectionAllowListCross(user).send({from: owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'AllowListAddressAdded'}]); + clearEvents(ethEvents, subEvents); + } + { + await collection.methods.removeFromCollectionAllowListCross(user).send({from: owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'AllowListAddressRemoved'}]); + } + unsubscribe(); +} + +async function testCollectionAdminAddedAndRemoved(helper: EthUniqueHelper, mode: TCollectionMode) { + const owner = await helper.eth.createAccountWithBalance(donor); + const user = helper.ethCrossAccount.createAccount(); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); + const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); + const ethEvents: any = []; + collectionHelper.events.allEvents((_: any, event: any) => { + ethEvents.push(event); + }); + const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionAdminAdded', 'CollectionAdminRemoved']}]); + { + await collection.methods.addCollectionAdminCross(user).send({from: owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'CollectionAdminAdded'}]); + clearEvents(ethEvents, subEvents); + } + { + await collection.methods.removeCollectionAdminCross(user).send({from: owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'CollectionAdminRemoved'}]); + } + unsubscribe(); +} + +async function testCollectionLimitSet(helper: EthUniqueHelper, mode: TCollectionMode) { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); + const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); + const ethEvents: any = []; + collectionHelper.events.allEvents((_: any, event: any) => { + ethEvents.push(event); + }); + const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionLimitSet']}]); + { + await collection.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanTransfer, value: {status: true, value: 0}}).send({from: owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'CollectionLimitSet'}]); + } + unsubscribe(); +} + +async function testCollectionOwnerChanged(helper: EthUniqueHelper, mode: TCollectionMode) { + const owner = await helper.eth.createAccountWithBalance(donor); + const newOwner = helper.ethCrossAccount.createAccount(); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); + const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); + const ethEvents: any = []; + collectionHelper.events.allEvents((_: any, event: any) => { + ethEvents.push(event); + }); + const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionOwnerChanged']}]); + { + await collection.methods.changeCollectionOwnerCross(newOwner).send({from: owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'CollectionOwnerChanged'}]); + } + unsubscribe(); +} + +async function testCollectionPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); + const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); + const ethEvents: any = []; + collectionHelper.events.allEvents((_: any, event: any) => { + ethEvents.push(event); + }); + const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionPermissionSet']}]); + { + await collection.methods.setCollectionMintMode(true).send({from: owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]); + clearEvents(ethEvents, subEvents); + } + { + await collection.methods.setCollectionAccess(1).send({from: owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]); + } + unsubscribe(); +} + +async function testCollectionSponsorSetAndConfirmedAndThenRemoved(helper: EthUniqueHelper, mode: TCollectionMode) { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.ethCrossAccount.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); + const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); + const ethEvents: any = []; + collectionHelper.events.allEvents((_: any, event: any) => { + ethEvents.push(event); + }); + const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{ + section: 'common', names: ['CollectionSponsorSet', 'SponsorshipConfirmed', 'CollectionSponsorRemoved', + ]}]); + { + await collection.methods.setCollectionSponsorCross(sponsor).send({from: owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([{ + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }]); + expect(subEvents).to.containSubset([{method: 'CollectionSponsorSet'}]); + clearEvents(ethEvents, subEvents); + } + { + await collection.methods.confirmCollectionSponsorship().send({from: sponsor.eth}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'SponsorshipConfirmed'}]); + clearEvents(ethEvents, subEvents); + } + { + await collection.methods.removeCollectionSponsor().send({from: owner}); + await helper.wait.newBlocks(1); + expect(ethEvents).to.containSubset([ + { + event: 'CollectionChanged', + returnValues: { + collectionId: collectionAddress, + }, + }, + ]); + expect(subEvents).to.containSubset([{method: 'CollectionSponsorRemoved'}]); + } + unsubscribe(); +} + +async function testTokenPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); + const result = await collection.methods.mint(owner).send({from: owner}); + const tokenId = result.events.Transfer.returnValues.tokenId; + await collection.methods.setTokenPropertyPermissions([ + ['A', [ + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], + ], + ]).send({from: owner}); + + const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['TokenPropertySet', 'TokenPropertyDeleted']}]); + { + const result = await collection.methods.setProperties(tokenId, [{key: 'A', value: [1,2,3]}]).send({from: owner}); + await helper.wait.newBlocks(1); + expect(result.events.TokenChanged).to.be.like({ + event: 'TokenChanged', + returnValues: { + tokenId: tokenId, + }, + }); + expect(subEvents).to.containSubset([{method: 'TokenPropertySet'}]); + clearEvents(null, subEvents); + } + { + const result = await collection.methods.deleteProperties(tokenId, ['A']).send({from: owner}); + await helper.wait.newBlocks(1); + expect(result.events.TokenChanged).to.be.like({ + event: 'TokenChanged', + returnValues: { + tokenId: tokenId, + }, + }); + expect(subEvents).to.containSubset([{method: 'TokenPropertyDeleted'}]); + } + unsubscribe(); +} + +describe('[FT] Sync sub & eth events', () => { + const mode: TCollectionMode = 'ft'; + + itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => { + await testCollectionCreatedAndDestroy(helper, mode); + }); + + itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => { + await testCollectionPropertySetAndDeleted(helper, mode); + }); + + itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => { + await testAllowListAddressAddedAndRemoved(helper, mode); + }); + + itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => { + await testCollectionAdminAddedAndRemoved(helper, mode); + }); + + itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => { + await testCollectionLimitSet(helper, mode); + }); + + itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => { + await testCollectionOwnerChanged(helper, mode); + }); + + itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => { + await testCollectionPermissionSet(helper, mode); + }); + + itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => { + await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode); + }); +}); + +describe('[NFT] Sync sub & eth events', () => { + const mode: TCollectionMode = 'nft'; + + itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => { + await testCollectionCreatedAndDestroy(helper, mode); + }); + + itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => { + await testCollectionPropertySetAndDeleted(helper, mode); + }); + + itEth('CollectionChanged event for PropertyPermissionSet', async ({helper}) => { + await testPropertyPermissionSet(helper, mode); + }); + + itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => { + await testAllowListAddressAddedAndRemoved(helper, mode); + }); + + itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => { + await testCollectionAdminAddedAndRemoved(helper, mode); + }); + + itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => { + await testCollectionLimitSet(helper, mode); + }); + + itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => { + await testCollectionOwnerChanged(helper, mode); + }); + + itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => { + await testCollectionPermissionSet(helper, mode); + }); + + itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => { + await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode); + }); + + itEth('CollectionChanged event for TokenPropertySet, TokenPropertyDeleted', async ({helper}) => { + await testTokenPropertySetAndDeleted(helper, mode); + }); +}); + +describe('[RFT] Sync sub & eth events', () => { + const mode: TCollectionMode = 'rft'; + + before(async function() { + await usingEthPlaygrounds((helper) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + return Promise.resolve(); + }); + }); + + itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => { + await testCollectionCreatedAndDestroy(helper, mode); + }); + + itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => { + await testCollectionPropertySetAndDeleted(helper, mode); + }); + + itEth('CollectionChanged event for PropertyPermissionSet', async ({helper}) => { + await testPropertyPermissionSet(helper, mode); + }); + + itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => { + await testAllowListAddressAddedAndRemoved(helper, mode); + }); + + itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => { + await testCollectionAdminAddedAndRemoved(helper, mode); + }); + + itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => { + await testCollectionLimitSet(helper, mode); + }); + + itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => { + await testCollectionOwnerChanged(helper, mode); + }); + + itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => { + await testCollectionPermissionSet(helper, mode); + }); + + itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => { + await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode); + }); + + itEth('CollectionChanged event for TokenPropertySet, TokenPropertyDeleted', async ({helper}) => { + await testTokenPropertySetAndDeleted(helper, mode); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/evmCoder.test.ts @@ -0,0 +1,88 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itEth, expect, usingEthPlaygrounds} from './util/index.js'; + +const getContractSource = (collectionAddress: string, contractAddress: string): string => ` + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.0; + interface ITest { + function ztestzzzzzzz() external returns (uint256 n); + } + contract Test { + event Result(bool, uint256); + function test1() public { + try + ITest(${collectionAddress}).ztestzzzzzzz() + returns (uint256 n) { + // enters + emit Result(true, n); // => [true, BigNumber { value: "43648854190028290368124427828690944273759144372138548774646036134290060795932" }] + } catch { + emit Result(false, 0); + } + } + function test2() public { + try + ITest(${contractAddress}).ztestzzzzzzz() + returns (uint256 n) { + emit Result(true, n); + } catch { + // enters + emit Result(false, 0); // => [ false, BigNumber { value: "0" } ] + } + } + function test3() public { + ITest(${collectionAddress}).ztestzzzzzzz(); + } + } + `; + + +describe('Evm Coder tests', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Call non-existing function', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.eth.createNFTCollection(owner, 'EVMCODER', '', 'TEST'); + const contract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, '0x1bfed5D614b886b9Ab2eA4CBAc22A96B7EC29c9c')); + const testContract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, contract.options.address)); + { + const result = await testContract.methods.test1().send(); + expect(result.events.Result.returnValues).to.deep.equal({ + '0': false, + '1': '0', + }); + } + { + const result = await testContract.methods.test2().send(); + expect(result.events.Result.returnValues).to.deep.equal({ + '0': false, + '1': '0', + }); + } + { + await expect(testContract.methods.test3().call()) + .to.be.rejectedWith(/unrecognized selector: 0xd9f02b36$/g); + } + }); +}); --- /dev/null +++ b/js-packages/tests/eth/fractionalizer/Fractionalizer.sol @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: Apache License +pragma solidity >=0.8.0; +import {CollectionHelpers} from "../api/CollectionHelpers.sol"; +import {ContractHelpers} from "../api/ContractHelpers.sol"; +import {UniqueRefungibleToken} from "../api/UniqueRefungibleToken.sol"; +import {UniqueRefungible, CrossAddress} from "../api/UniqueRefungible.sol"; +import {UniqueNFT} from "../api/UniqueNFT.sol"; + +/// @dev Fractionalization contract. It stores mappings between NFT and RFT tokens, +/// stores allowlist of NFT tokens available for fractionalization, has methods +/// for fractionalization and defractionalization of NFT tokens. +contract Fractionalizer { + struct Token { + address _collection; + uint256 _tokenId; + } + address rftCollection; + mapping(address => bool) nftCollectionAllowList; + mapping(address => mapping(uint256 => uint256)) public nft2rftMapping; + mapping(address => Token) public rft2nftMapping; + //use constant to reduce gas cost + bytes32 constant refungibleCollectionType = keccak256(bytes("ReFungible")); + + receive() external payable onlyOwner {} + + /// @dev Method modifier to only allow contract owner to call it. + modifier onlyOwner() { + address contracthelpersAddress = 0x842899ECF380553E8a4de75bF534cdf6fBF64049; + ContractHelpers contractHelpers = ContractHelpers(contracthelpersAddress); + address contractOwner = contractHelpers.contractOwner(address(this)); + require(msg.sender == contractOwner, "Only owner can"); + _; + } + + /// @dev This emits when RFT collection setting is changed. + event RFTCollectionSet(address _collection); + + /// @dev This emits when NFT collection is allowed or disallowed. + event AllowListSet(address _collection, bool _status); + + /// @dev This emits when NFT token is fractionalized by contract. + event Fractionalized(address _collection, uint256 _tokenId, address _rftToken, uint128 _amount); + + /// @dev This emits when NFT token is defractionalized by contract. + event Defractionalized(address _rftToken, address _nftCollection, uint256 _nftTokenId); + + /// Set RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens + /// would be created in this collection. + /// @dev Throws if RFT collection is already configured for this contract. + /// Throws if collection of wrong type (NFT, Fungible) is provided instead + /// of RFT collection. + /// Throws if `msg.sender` is not owner or admin of provided RFT collection. + /// Can only be called by contract owner. + /// @param _collection address of RFT collection. + function setRFTCollection(address _collection) external onlyOwner { + require(rftCollection == address(0), "RFT collection is already set"); + UniqueRefungible refungibleContract = UniqueRefungible(_collection); + string memory collectionType = refungibleContract.uniqueCollectionType(); + + // compare hashed to reduce gas cost + require( + keccak256(bytes(collectionType)) == refungibleCollectionType, + "Wrong collection type. Collection is not refungible." + ); + require( + refungibleContract.isOwnerOrAdminCross(CrossAddress({eth: address(this), sub: uint256(0)})), + "Fractionalizer contract should be an admin of the collection" + ); + rftCollection = _collection; + emit RFTCollectionSet(rftCollection); + } + + /// Creates and sets RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens + /// would be created in this collection. + /// @dev Throws if RFT collection is already configured for this contract. + /// Can only be called by contract owner. + /// @param _name name for created RFT collection. + /// @param _description description for created RFT collection. + /// @param _tokenPrefix token prefix for created RFT collection. + function createAndSetRFTCollection( + string calldata _name, + string calldata _description, + string calldata _tokenPrefix + ) external payable onlyOwner { + require(rftCollection == address(0), "RFT collection is already set"); + address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F; + rftCollection = CollectionHelpers(collectionHelpers).createRFTCollection{value: msg.value}( + _name, + _description, + _tokenPrefix + ); + emit RFTCollectionSet(rftCollection); + } + + /// Allow or disallow NFT collection tokens from being fractionalized by this contract. + /// @dev Can only be called by contract owner. + /// @param collection NFT token address. + /// @param status `true` to allow and `false` to disallow NFT token. + function setNftCollectionIsAllowed(address collection, bool status) external onlyOwner { + nftCollectionAllowList[collection] = status; + emit AllowListSet(collection, status); + } + + /// Fractionilize NFT token. + /// @dev Takes NFT token from `msg.sender` and transfers RFT token to `msg.sender` + /// instead. Creates new RFT token if provided NFT token never was fractionalized + /// by this contract or existing RFT token if it was. + /// Throws if RFT collection isn't configured for this contract. + /// Throws if fractionalization of provided NFT token is not allowed + /// Throws if `msg.sender` is not owner of provided NFT token + /// @param _collection NFT collection address + /// @param _token id of NFT token to be fractionalized + /// @param _pieces number of pieces new RFT token would have + function nft2rft( + address _collection, + uint256 _token, + uint128 _pieces + ) external { + require(rftCollection != address(0), "RFT collection is not set"); + UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection); + require( + nftCollectionAllowList[_collection] == true, + "Fractionalization of this collection is not allowed by admin" + ); + require(UniqueNFT(_collection).ownerOf(_token) == msg.sender, "Only token owner could fractionalize it"); + UniqueNFT(_collection).transferFrom(msg.sender, address(this), _token); + uint256 rftTokenId; + address rftTokenAddress; + UniqueRefungibleToken rftTokenContract; + if (nft2rftMapping[_collection][_token] == 0) { + rftTokenId = rftCollectionContract.mint(address(this)); + rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId); + nft2rftMapping[_collection][_token] = rftTokenId; + rft2nftMapping[rftTokenAddress] = Token(_collection, _token); + + rftTokenContract = UniqueRefungibleToken(rftTokenAddress); + } else { + rftTokenId = nft2rftMapping[_collection][_token]; + rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId); + rftTokenContract = UniqueRefungibleToken(rftTokenAddress); + } + rftTokenContract.repartition(_pieces); + rftTokenContract.transfer(msg.sender, _pieces); + emit Fractionalized(_collection, _token, rftTokenAddress, _pieces); + } + + /// Defrationalize NFT token. + /// @dev Takes RFT token from `msg.sender` and transfers corresponding NFT token + /// to `msg.sender` instead. + /// Throws if RFT collection isn't configured for this contract. + /// Throws if provided RFT token is no from configured RFT collection. + /// Throws if RFT token was not created by this contract. + /// Throws if `msg.sender` isn't owner of all RFT token pieces. + /// @param _collection RFT collection address + /// @param _token id of RFT token + function rft2nft(address _collection, uint256 _token) external { + require(rftCollection != address(0), "RFT collection is not set"); + require(rftCollection == _collection, "Wrong RFT collection"); + UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection); + address rftTokenAddress = rftCollectionContract.tokenContractAddress(_token); + Token memory nftToken = rft2nftMapping[rftTokenAddress]; + require(nftToken._collection != address(0), "No corresponding NFT token found"); + UniqueRefungibleToken rftTokenContract = UniqueRefungibleToken(rftTokenAddress); + require( + rftTokenContract.balanceOf(msg.sender) == rftTokenContract.totalSupply(), + "Not all pieces are owned by the caller" + ); + rftCollectionContract.transferFrom(msg.sender, address(this), _token); + UniqueNFT(nftToken._collection).transferFrom(address(this), msg.sender, nftToken._tokenId); + emit Defractionalized(rftTokenAddress, nftToken._collection, nftToken._tokenId); + } +} --- /dev/null +++ b/js-packages/tests/eth/fractionalizer/fractionalizer.test.ts @@ -0,0 +1,453 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + + +import {readFile} from 'fs/promises'; + +import type {IKeyringPair} from '@polkadot/types/types'; +import {evmToAddress} from '@polkadot/util-crypto'; + +import {Contract} from 'web3-eth-contract'; + +import {usingEthPlaygrounds, expect, itEth} from '../util/index.js'; +import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js'; +import type {CompiledContract} from '../util/playgrounds/types.js'; +import {requirePalletsOrSkip, Pallets, makeNames} from '../../util/index.js'; + +const {dirname} = makeNames(import.meta.url); + +let compiledFractionalizer: CompiledContract; + +const compileContract = async (helper: EthUniqueHelper): Promise => { + if(!compiledFractionalizer) { + compiledFractionalizer = await helper.ethContract.compile('Fractionalizer', (await readFile(`${dirname}/Fractionalizer.sol`)).toString(), [ + {solPath: 'api/CollectionHelpers.sol', fsPath: `${dirname}/../api/CollectionHelpers.sol`}, + {solPath: 'api/ContractHelpers.sol', fsPath: `${dirname}/../api/ContractHelpers.sol`}, + {solPath: 'api/UniqueRefungibleToken.sol', fsPath: `${dirname}/../api/UniqueRefungibleToken.sol`}, + {solPath: 'api/UniqueRefungible.sol', fsPath: `${dirname}/../api/UniqueRefungible.sol`}, + {solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}, + ]); + } + return compiledFractionalizer; +}; + + +const deployContract = async (helper: EthUniqueHelper, owner: string): Promise => { + const compiled = await compileContract(helper); + return await helper.ethContract.deployByAbi(owner, compiled.abi, compiled.object); +}; + + +const initContract = async (helper: EthUniqueHelper, owner: string): Promise<{contract: Contract, rftCollectionAddress: string}> => { + const fractionalizer = await deployContract(helper, owner); + const amount = 10n * helper.balance.getOneTokenNominal(); + const web3 = helper.getWeb3(); + await web3.eth.sendTransaction({from: owner, to: fractionalizer.options.address, value: `${amount}`, gas: helper.eth.DEFAULT_GAS}); + const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({value: Number(2n * helper.balance.getOneTokenNominal())}); + const rftCollectionAddress = result.events.RFTCollectionSet.returnValues._collection; + return {contract: fractionalizer, rftCollectionAddress}; +}; + +const mintRFTToken = async (helper: EthUniqueHelper, owner: string, fractionalizer: Contract, amount: bigint): Promise<{ + nftCollectionAddress: string, nftTokenId: number, rftTokenAddress: string +}> => { + const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); + const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); + const mintResult = await nftContract.methods.mint(owner).send({from: owner}); + const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; + + await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); + await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner}); + const result = await fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, amount).send({from: owner}); + const {_collection, _tokenId, _rftToken} = result.events.Fractionalized.returnValues; + return { + nftCollectionAddress: _collection, + nftTokenId: _tokenId, + rftTokenAddress: _rftToken, + }; +}; + + +describe('Fractionalizer contract usage', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Set RFT collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const fractionalizer = await deployContract(helper, owner); + const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); + const rftContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner); + + const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address); + await rftContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner}); + const result = await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner}); + expect(result.events).to.be.like({ + RFTCollectionSet: { + returnValues: { + _collection: rftCollection.collectionAddress, + }, + }, + }); + }); + + itEth('Mint RFT collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const fractionalizer = await deployContract(helper, owner); + await helper.balance.transferToSubstrate(donor, evmToAddress(fractionalizer.options.address), 10n * helper.balance.getOneTokenNominal()); + + const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({from: owner, value: Number(2n * helper.balance.getOneTokenNominal())}); + expect(result.events).to.be.like({ + RFTCollectionSet: {}, + }); + expect(result.events.RFTCollectionSet.returnValues._collection).to.be.ok; + }); + + itEth('Set Allowlist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {contract: fractionalizer} = await initContract(helper, owner); + const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); + + const result1 = await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); + expect(result1.events).to.be.like({ + AllowListSet: { + returnValues: { + _collection: nftCollection.collectionAddress, + _status: true, + }, + }, + }); + const result2 = await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, false).send({from: owner}); + expect(result2.events).to.be.like({ + AllowListSet: { + returnValues: { + _collection: nftCollection.collectionAddress, + _status: false, + }, + }, + }); + }); + + itEth('NFT to RFT', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); + const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); + const mintResult = await nftContract.methods.mint(owner).send({from: owner}); + const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; + + const {contract: fractionalizer} = await initContract(helper, owner); + + await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); + await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner}); + const result = await fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).send({from: owner}); + expect(result.events).to.be.like({ + Fractionalized: { + returnValues: { + _collection: nftCollection.collectionAddress, + _tokenId: nftTokenId, + _amount: '100', + }, + }, + }); + const rftTokenAddress = result.events.Fractionalized.returnValues._rftToken; + + // FIXME: should work without the caller + const rftTokenContract = helper.ethNativeContract.rftToken(rftTokenAddress); + expect(await rftTokenContract.methods.balanceOf(owner).call()).to.equal('100'); + }); + + itEth('RFT to NFT', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner); + const {rftTokenAddress, nftCollectionAddress, nftTokenId} = await mintRFTToken(helper, owner, fractionalizer, 100n); + + const {collectionId, tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress); + const refungibleAddress = helper.ethAddress.fromCollectionId(collectionId); + expect(rftCollectionAddress).to.be.equal(refungibleAddress); + const refungibleTokenContract = await helper.ethNativeContract.rftToken(rftTokenAddress, owner); + await refungibleTokenContract.methods.approve(fractionalizer.options.address, 100).send({from: owner}); + const result = await fractionalizer.methods.rft2nft(refungibleAddress, tokenId).send({from: owner}); + expect(result.events).to.be.like({ + Defractionalized: { + returnValues: { + _rftToken: rftTokenAddress, + _nftCollection: nftCollectionAddress, + _nftTokenId: nftTokenId, + }, + }, + }); + }); + + itEth('Test fractionalizer NFT <-> RFT mapping ', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner); + const {rftTokenAddress, nftCollectionAddress, nftTokenId} = await mintRFTToken(helper, owner, fractionalizer, 100n); + + const {collectionId, tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress); + const refungibleAddress = helper.ethAddress.fromCollectionId(collectionId); + expect(rftCollectionAddress).to.be.equal(refungibleAddress); + const refungibleTokenContract = await helper.ethNativeContract.rftToken(rftTokenAddress, owner); + await refungibleTokenContract.methods.approve(fractionalizer.options.address, 100).send({from: owner}); + + const rft2nft = await fractionalizer.methods.rft2nftMapping(rftTokenAddress).call(); + expect(rft2nft).to.be.like({ + _collection: nftCollectionAddress, + _tokenId: nftTokenId, + }); + + const nft2rft = await fractionalizer.methods.nft2rftMapping(nftCollectionAddress, nftTokenId).call(); + expect(nft2rft).to.be.eq(tokenId.toString()); + }); +}); + + + +describe('Negative Integration Tests for fractionalizer', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('call setRFTCollection twice', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); + const refungibleContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner); + + const fractionalizer = await deployContract(helper, owner); + const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address); + await refungibleContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner}); + await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner}); + + await expect(fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).call()) + .to.be.rejectedWith(/RFT collection is already set$/g); + }); + + itEth('call setRFTCollection with NFT collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); + const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); + + const fractionalizer = await deployContract(helper, owner); + const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address); + await nftContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner}); + + await expect(fractionalizer.methods.setRFTCollection(nftCollection.collectionAddress).call()) + .to.be.rejectedWith(/Wrong collection type. Collection is not refungible.$/g); + }); + + itEth('call setRFTCollection while not collection admin', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const fractionalizer = await deployContract(helper, owner); + const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); + + await expect(fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).call()) + .to.be.rejectedWith(/Fractionalizer contract should be an admin of the collection$/g); + }); + + itEth('call setRFTCollection after createAndSetRFTCollection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const fractionalizer = await deployContract(helper, owner); + await helper.balance.transferToSubstrate(donor, evmToAddress(fractionalizer.options.address), 10n * helper.balance.getOneTokenNominal()); + + const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({from: owner, value: Number(2n * helper.balance.getOneTokenNominal())}); + const collectionIdAddress = result.events.RFTCollectionSet.returnValues._collection; + + await expect(fractionalizer.methods.setRFTCollection(collectionIdAddress).call()) + .to.be.rejectedWith(/RFT collection is already set$/g); + }); + + itEth('call nft2rft without setting RFT collection for contract', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); + const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); + const mintResult = await nftContract.methods.mint(owner).send({from: owner}); + const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; + + const fractionalizer = await deployContract(helper, owner); + + await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call()) + .to.be.rejectedWith(/RFT collection is not set$/g); + }); + + itEth('call nft2rft while not owner of NFT token', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const nftOwner = await helper.eth.createAccountWithBalance(donor); + + const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); + const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); + const mintResult = await nftContract.methods.mint(owner).send({from: owner}); + const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; + await nftContract.methods.transfer(nftOwner, 1).send({from: owner}); + + + const {contract: fractionalizer} = await initContract(helper, owner); + await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); + + await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call({from: owner})) + .to.be.rejectedWith(/Only token owner could fractionalize it$/g); + }); + + itEth('call nft2rft while not in list of allowed accounts', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); + const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); + const mintResult = await nftContract.methods.mint(owner).send({from: owner}); + const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; + + const {contract: fractionalizer} = await initContract(helper, owner); + + await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner}); + await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call()) + .to.be.rejectedWith(/Fractionalization of this collection is not allowed by admin$/g); + }); + + itEth('call nft2rft while fractionalizer doesnt have approval for nft token', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); + const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); + const mintResult = await nftContract.methods.mint(owner).send({from: owner}); + const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; + + const {contract: fractionalizer} = await initContract(helper, owner); + + await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); + await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call()) + .to.be.rejectedWith(/ApprovedValueTooLow$/g); + }); + + itEth('call rft2nft without setting RFT collection for contract', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const fractionalizer = await deployContract(helper, owner); + const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); + const refungibleContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner); + const mintResult = await refungibleContract.methods.mint(owner).send({from: owner}); + const rftTokenId = mintResult.events.Transfer.returnValues.tokenId; + + await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call({from: owner})) + .to.be.rejectedWith(/RFT collection is not set$/g); + }); + + itEth('call rft2nft for RFT token that is not from configured RFT collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {contract: fractionalizer} = await initContract(helper, owner); + const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); + const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner); + const mintResult = await refungibleContract.methods.mint(owner).send({from: owner}); + const rftTokenId = mintResult.events.Transfer.returnValues.tokenId; + + await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call()) + .to.be.rejectedWith(/Wrong RFT collection$/g); + }); + + itEth('call rft2nft for RFT token that was not minted by fractionalizer contract', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); + const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner); + + const fractionalizer = await deployContract(helper, owner); + + const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address); + await refungibleContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner}); + await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner}); + + const mintResult = await refungibleContract.methods.mint(owner).send({from: owner}); + const rftTokenId = mintResult.events.Transfer.returnValues.tokenId; + + await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call()) + .to.be.rejectedWith(/No corresponding NFT token found$/g); + }); + + itEth('call rft2nft without owning all RFT pieces', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.eth.createAccountWithBalance(donor); + + const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner); + const {rftTokenAddress} = await mintRFTToken(helper, owner, fractionalizer, 100n); + + const {tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress); + const refungibleTokenContract = helper.ethNativeContract.rftToken(rftTokenAddress, owner); + await refungibleTokenContract.methods.transfer(receiver, 50).send({from: owner}); + await refungibleTokenContract.methods.approve(fractionalizer.options.address, 50).send({from: receiver}); + await expect(fractionalizer.methods.rft2nft(rftCollectionAddress, tokenId).call({from: receiver})) + .to.be.rejectedWith(/Not all pieces are owned by the caller$/g); + }); + + itEth('send QTZ/UNQ to contract from non owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const payer = await helper.eth.createAccountWithBalance(donor); + + const fractionalizer = await deployContract(helper, owner); + const amount = 10n * helper.balance.getOneTokenNominal(); + const web3 = helper.getWeb3(); + await expect(web3.eth.sendTransaction({from: payer, to: fractionalizer.options.address, value: `${amount}`, gas: helper.eth.DEFAULT_GAS})).to.be.rejected; + }); + + itEth('fractionalize NFT with NFT transfers disallowed', async ({helper}) => { + const nftCollection = await helper.nft.mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const owner = await helper.eth.createAccountWithBalance(donor); + const nftToken = await nftCollection.mintToken(donor, {Ethereum: owner}); + await helper.executeExtrinsic(donor, 'api.tx.unique.setTransfersEnabledFlag', [nftCollection.collectionId, false], true); + const nftCollectionAddress = helper.ethAddress.fromCollectionId(nftCollection.collectionId); + const {contract: fractionalizer} = await initContract(helper, owner); + await fractionalizer.methods.setNftCollectionIsAllowed(nftCollectionAddress, true).send({from: owner}); + + const nftContract = await helper.ethNativeContract.collection(nftCollectionAddress, 'nft', owner); + await nftContract.methods.approve(fractionalizer.options.address, nftToken.tokenId).send({from: owner}); + await expect(fractionalizer.methods.nft2rft(nftCollectionAddress, nftToken.tokenId, 100).call()) + .to.be.rejectedWith(/TransferNotAllowed$/g); + }); + + itEth('fractionalize NFT with RFT transfers disallowed', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const rftCollection = await helper.rft.mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const rftCollectionAddress = helper.ethAddress.fromCollectionId(rftCollection.collectionId); + const fractionalizer = await deployContract(helper, owner); + await rftCollection.addAdmin(donor, {Ethereum: fractionalizer.options.address}); + + await fractionalizer.methods.setRFTCollection(rftCollectionAddress).send({from: owner}); + await helper.executeExtrinsic(donor, 'api.tx.unique.setTransfersEnabledFlag', [rftCollection.collectionId, false], true); + + const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); + const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); + const mintResult = await nftContract.methods.mint(owner).send({from: owner}); + const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; + + await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); + await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner}); + + await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100n).call()) + .to.be.rejectedWith(/TransferNotAllowed$/g); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/fungible.test.ts @@ -0,0 +1,643 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import type {IKeyringPair} from '@polkadot/types/types'; + +describe('Fungible: Plain calls', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let owner: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, owner] = await helper.arrange.createAccounts([30n, 20n], donor); + }); + }); + + [ + 'substrate' as const, + 'ethereum' as const, + ].map(testCase => { + itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => { + // 1. Create receiver depending on the test case: + const receiverEth = helper.eth.createAccount(); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const receiverSub = owner; + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(owner); + + const ethOwner = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.ft.mintCollection(alice); + await collection.addAdmin(alice, {Ethereum: ethOwner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', ethOwner); + + // 2. Mint tokens: + const result = await collectionEvm.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, 100).send(); + + const event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(receiverSub.address)); + expect(event.returnValues.value).to.equal('100'); + + // 3. Get balance depending on the test case: + let balance; + if(testCase === 'ethereum') balance = await collection.getBalance({Ethereum: receiverEth}); + else if(testCase === 'substrate') balance = await collection.getBalance({Substrate: receiverSub.address}); + // 3.1 Check balance: + expect(balance).to.eq(100n); + }); + }); + + itEth('Can perform mintBulk()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const bulkSize = 3; + const receivers = [...new Array(bulkSize)].map(() => helper.eth.createAccount()); + const collection = await helper.ft.mintCollection(alice); + await collection.addAdmin(alice, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + const result = await contract.methods.mintBulk(Array.from({length: bulkSize}, (_, i) => ( + [receivers[i], (i + 1) * 10] + ))).send(); + const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.value - b.returnValues.value); + for(let i = 0; i < bulkSize; i++) { + const event = events[i]; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal(receivers[i]); + expect(event.returnValues.value).to.equal(String(10 * (i + 1))); + } + }); + + // Soft-deprecated + itEth('Can perform burn()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.ft.mintCollection(alice); + await collection.addAdmin(alice, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); + await contract.methods.mint(receiver, 100).send(); + + const result = await contract.methods.burnFrom(receiver, 49).send({from: receiver}); + + const event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal(receiver); + expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.value).to.equal('49'); + + const balance = await contract.methods.balanceOf(receiver).call(); + expect(balance).to.equal('51'); + }); + + itEth('Can perform approve()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + { + const result = await contract.methods.approve(spender, 100).send({from: owner}); + + const event = result.events.Approval; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.owner).to.be.equal(owner); + expect(event.returnValues.spender).to.be.equal(spender); + expect(event.returnValues.value).to.be.equal('100'); + } + + { + const allowance = await contract.methods.allowance(owner, spender).call(); + expect(+allowance).to.equal(100); + } + { + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const spenderCross = helper.ethCrossAccount.fromAddress(spender); + const allowance = await contract.methods.allowanceCross(ownerCross, spenderCross).call(); + expect(+allowance).to.equal(100); + } + }); + + itEth('Can perform approveCross()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + const spenderSub = (await helper.arrange.createAccounts([1n], donor))[0]; + const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender); + const spenderCrossSub = helper.ethCrossAccount.fromKeyringPair(spenderSub); + + + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + { + const result = await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner}); + const event = result.events.Approval; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.owner).to.be.equal(owner); + expect(event.returnValues.spender).to.be.equal(spender); + expect(event.returnValues.value).to.be.equal('100'); + } + + { + const allowance = await contract.methods.allowance(owner, spender).call(); + expect(+allowance).to.equal(100); + } + + + { + const result = await contract.methods.approveCross(spenderCrossSub, 100).send({from: owner}); + const event = result.events.Approval; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.owner).to.be.equal(owner); + expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(spenderSub.address)); + expect(event.returnValues.value).to.be.equal('100'); + } + + { + const allowance = await collection.getApprovedTokens({Ethereum: owner}, {Substrate: spenderSub.address}); + expect(allowance).to.equal(100n); + } + + { + //TO-DO expect with future allowanceCross(owner, spenderCrossEth).call() + } + }); + + itEth('Non-owner and non admin cannot approveCross', async ({helper}) => { + const nonOwner = await helper.eth.createAccountWithBalance(donor); + const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner); + const owner = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.ft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}); + await collection.mint(alice, 100n, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + await expect(collectionEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned'); + }); + + + itEth('Can perform burnFromCross()', async ({helper}) => { + const sender = await helper.eth.createAccountWithBalance(donor); + + const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0); + + await collection.mint(owner, 200n, {Substrate: owner.address}); + await collection.approveTokens(owner, {Ethereum: sender}, 100n); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'ft'); + + const fromBalanceBefore = await collection.getBalance({Substrate: owner.address}); + + const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner); + const result = await contract.methods.burnFromCross(ownerCross, 49).send({from: sender}); + const events = result.events; + + expect(events).to.be.like({ + Transfer: { + address: helper.ethAddress.fromCollectionId(collection.collectionId), + event: 'Transfer', + returnValues: { + from: helper.address.substrateToEth(owner.address), + to: '0x0000000000000000000000000000000000000000', + value: '49', + }, + }, + Approval: { + address: helper.ethAddress.fromCollectionId(collection.collectionId), + returnValues: { + owner: helper.address.substrateToEth(owner.address), + spender: sender, + value: '51', + }, + event: 'Approval', + }, + }); + + const fromBalanceAfter = await collection.getBalance({Substrate: owner.address}); + expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(49n); + }); + + itEth('Can perform transferFrom()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + await contract.methods.approve(spender, 100).send(); + + { + const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender}); + + let event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(owner); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.value).to.be.equal('49'); + + event = result.events.Approval; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.owner).to.be.equal(owner); + expect(event.returnValues.spender).to.be.equal(spender); + expect(event.returnValues.value).to.be.equal('51'); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(49); + } + + { + const balance = await contract.methods.balanceOf(owner).call(); + expect(+balance).to.equal(151); + } + }); + + itEth('Can perform transferCross()', async ({helper}) => { + const sender = await helper.eth.createAccountWithBalance(donor); + const receiverEth = await helper.eth.createAccountWithBalance(donor); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(donor); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n, {Ethereum: sender}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', sender); + + { + // Can transferCross to ethereum address: + const result = await collectionEvm.methods.transferCross(receiverCrossEth, 50).send({from: sender}); + // Check events: + const event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(sender); + expect(event.returnValues.to).to.be.equal(receiverEth); + expect(event.returnValues.value).to.be.equal('50'); + // Sender's balance decreased: + const ownerBalance = await collectionEvm.methods.balanceOf(sender).call(); + expect(+ownerBalance).to.equal(150); + // Receiver's balance increased: + const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); + expect(+receiverBalance).to.equal(50); + } + + { + // Can transferCross to substrate address: + const result = await collectionEvm.methods.transferCross(receiverCrossSub, 50).send({from: sender}); + // Check events: + const event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(sender); + expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(donor.address)); + expect(event.returnValues.value).to.be.equal('50'); + // Sender's balance decreased: + const senderBalance = await collection.getBalance({Ethereum: sender}); + expect(senderBalance).to.equal(100n); + // Receiver's balance increased: + const balance = await collection.getBalance({Substrate: donor.address}); + expect(balance).to.equal(50n); + } + }); + + ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} incorrect amount`, async ({helper}) => { + const sender = await helper.eth.createAccountWithBalance(donor); + const receiverEth = await helper.eth.createAccountWithBalance(donor); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const BALANCE = 200n; + const BALANCE_TO_TRANSFER = BALANCE + 100n; + + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, BALANCE, {Ethereum: sender}); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', sender); + + // 1. Cannot transfer more than have + const receiver = testCase === 'transfer' ? receiverEth : receiverCrossEth; + await expect(collectionEvm.methods[testCase](receiver, BALANCE_TO_TRANSFER).send({from: sender})).to.be.rejected; + // 2. Zero transfer allowed (EIP-20): + await collectionEvm.methods[testCase](receiver, 0n).send({from: sender}); + })); + + + itEth('Can perform transfer()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + { + const result = await contract.methods.transfer(receiver, 50).send({from: owner}); + + const event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(owner); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.value).to.be.equal('50'); + } + + { + const balance = await contract.methods.balanceOf(owner).call(); + expect(+balance).to.equal(150); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(50); + } + }); + + itEth('Can perform transferFromCross()', async ({helper}) => { + const sender = await helper.eth.createAccountWithBalance(donor); + + const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0); + + const receiver = helper.eth.createAccount(); + + await collection.mint(owner, 200n, {Substrate: owner.address}); + await collection.approveTokens(owner, {Ethereum: sender}, 100n); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'ft'); + + const from = helper.ethCrossAccount.fromKeyringPair(owner); + const to = helper.ethCrossAccount.fromAddress(receiver); + + const fromBalanceBefore = await collection.getBalance({Substrate: owner.address}); + const toBalanceBefore = await collection.getBalance({Ethereum: receiver}); + + const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender}); + + expect(result.events).to.be.like({ + Transfer: { + address, + event: 'Transfer', + returnValues: { + from: helper.address.substrateToEth(owner.address), + to: receiver, + value: '51', + }, + }, + Approval: { + address, + event: 'Approval', + returnValues: { + owner: helper.address.substrateToEth(owner.address), + spender: sender, + value: '49', + }, + }}); + + const fromBalanceAfter = await collection.getBalance({Substrate: owner.address}); + expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(51n); + const toBalanceAfter = await collection.getBalance({Ethereum: receiver}); + expect(toBalanceAfter - toBalanceBefore).to.be.eq(51n); + }); + + itEth('Check balanceOfCross()', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}); + const owner = await helper.ethCrossAccount.createAccountWithBalance(donor); + const other = await helper.ethCrossAccount.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth); + + expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0'); + expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0'); + + await collection.mint(alice, 100n, {Ethereum: owner.eth}); + expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100'); + expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0'); + + await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth}); + expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50'); + expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50'); + + await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth}); + expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0'); + expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100'); + + await collectionEvm.methods.transferCross(owner, 100n).send({from: other.eth}); + expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100'); + expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0'); + }); +}); + +describe('Fungible: Fees', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([20n], donor); + }); + }); + + itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner})); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + }); + + itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + await contract.methods.approve(spender, 100).send({from: owner}); + + const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender})); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + }); + + itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner})); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + }); +}); + +describe('Fungible: Substrate calls', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let owner: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, owner] = await helper.arrange.createAccounts([20n, 20n], donor); + }); + }); + + itEth('Events emitted for approve()', async ({helper}) => { + const receiver = helper.eth.createAccount(); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft'); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + await collection.approveTokens(alice, {Ethereum: receiver}, 100n); + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.event).to.be.equal('Approval'); + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.spender).to.be.equal(receiver); + expect(event.returnValues.value).to.be.equal('100'); + }); + + itEth('Events emitted for transferFrom()', async ({helper}) => { + const [bob] = await helper.arrange.createAccounts([10n], donor); + const receiver = helper.eth.createAccount(); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n); + await collection.approveTokens(alice, {Substrate: bob.address}, 100n); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft'); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + await collection.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n); + if(events.length == 0) await helper.wait.newBlocks(1); + let event = events[0]; + + expect(event.event).to.be.equal('Transfer'); + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.value).to.be.equal('51'); + + event = events[1]; + expect(event.event).to.be.equal('Approval'); + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address)); + expect(event.returnValues.value).to.be.equal('49'); + }); + + itEth('Events emitted for transfer()', async ({helper}) => { + const receiver = helper.eth.createAccount(); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft'); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + await collection.transfer(alice, {Ethereum:receiver}, 51n); + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.event).to.be.equal('Transfer'); + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.value).to.be.equal('51'); + }); + + itEth('Events emitted for transferFromCross()', async ({helper}) => { + const sender = await helper.eth.createAccountWithBalance(donor); + + const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0); + + const receiver = helper.eth.createAccount(); + + await collection.mint(owner, 200n, {Substrate: owner.address}); + await collection.approveTokens(owner, {Ethereum: sender}, 100n); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'ft'); + + const from = helper.ethCrossAccount.fromKeyringPair(owner); + const to = helper.ethCrossAccount.fromAddress(receiver); + + const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender}); + + expect(result.events).to.be.like({ + Transfer: { + address, + event: 'Transfer', + returnValues: { + from: helper.address.substrateToEth(owner.address), + to: receiver, + value: '51', + }, + }, + Approval: { + address, + event: 'Approval', + returnValues: { + owner: helper.address.substrateToEth(owner.address), + spender: sender, + value: '49', + }, + }}); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/getCode.test.ts @@ -0,0 +1,53 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {COLLECTION_HELPER, CONTRACT_HELPER} from '../util/index.js'; + +describe('RPC eth_getCode', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + [ + {address: COLLECTION_HELPER}, + {address: CONTRACT_HELPER}, + ].map(testCase => { + itEth(`returns value for native contract: ${testCase.address}`, async ({helper}) => { + const contractCodeSub = (await helper.callRpc('api.rpc.eth.getCode', [testCase.address])).toJSON(); + const contractCodeEth = (await helper.getWeb3().eth.getCode(testCase.address)); + + expect(contractCodeSub).to.has.length.greaterThan(4); + expect(contractCodeEth).to.has.length.greaterThan(4); + }); + }); + + itEth('returns value for custom contract', async ({helper}) => { + const signer = await helper.eth.createAccountWithBalance(donor); + const flipper = await helper.eth.deployFlipper(signer); + + const contractCodeSub = (await helper.callRpc('api.rpc.eth.getCode', [flipper.options.address])).toJSON(); + const contractCodeEth = (await helper.getWeb3().eth.getCode(flipper.options.address)); + + expect(contractCodeSub).to.has.length.greaterThan(4); + expect(contractCodeEth).to.has.length.greaterThan(4); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/helpersSmoke.test.ts @@ -0,0 +1,46 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import type {IKeyringPair} from '@polkadot/types/types'; + +describe('Helpers sanity check', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Contract owner is recorded', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const flipper = await helper.eth.deployFlipper(owner); + + expect(await (await helper.ethNativeContract.contractHelpers(owner)).methods.contractOwner(flipper.options.address).call()).to.be.equal(owner); + }); + + itEth('Flipper is working', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const flipper = await helper.eth.deployFlipper(owner); + + expect(await flipper.methods.getValue().call()).to.be.false; + await flipper.methods.flip().send({from: owner}); + expect(await flipper.methods.getValue().call()).to.be.true; + }); +}); --- /dev/null +++ b/js-packages/tests/eth/marketplace-v2/Market.sol @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import { UniqueNFT, CrossAddress } from "@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol"; +import { UniqueFungible, CrossAddress as CrossAddressF } from "@unique-nft/solidity-interfaces/contracts/UniqueFungible.sol"; +import { CollectionHelpers } from "@unique-nft/solidity-interfaces/contracts/CollectionHelpers.sol"; +import "./royalty/UniqueRoyaltyHelper.sol"; + +contract Market is Ownable, ReentrancyGuard { + using ERC165Checker for address; + + struct Order { + uint32 id; + uint32 collectionId; + uint32 tokenId; + uint32 amount; + uint256 price; + CrossAddress seller; + } + + uint32 public constant version = 0; + uint32 public constant buildVersion = 3; + bytes4 private constant InterfaceId_ERC721 = 0x80ac58cd; + bytes4 private constant InterfaceId_ERC165 = 0x5755c3f2; + CollectionHelpers private constant collectionHelpers = + CollectionHelpers(0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F); + + mapping(uint32 => mapping(uint32 => Order)) orders; + uint32 private idCount = 1; + uint32 public marketFee; + uint64 public ctime; + address public ownerAddress; + mapping(address => bool) public admins; + + event TokenIsUpForSale(uint32 version, Order item); + event TokenRevoke(uint32 version, Order item, uint32 amount); + event TokenIsApproved(uint32 version, Order item); + event TokenIsPurchased( + uint32 version, + Order item, + uint32 salesAmount, + CrossAddress buyer, + RoyaltyAmount[] royalties + ); + event Log(string message); + + error InvalidArgument(string info); + error InvalidMarketFee(); + error SellerIsNotOwner(); + error TokenIsAlreadyOnSale(); + error TokenIsNotApproved(); + error CollectionNotFound(); + error CollectionNotSupportedERC721(); + error OrderNotFound(); + error TooManyAmountRequested(); + error NotEnoughMoneyError(); + error InvalidRoyaltiesError(uint256 totalRoyalty); + error FailTransferToken(string reason); + + modifier onlyAdmin() { + require(msg.sender == this.owner() || admins[msg.sender], "Only admin can"); + _; + } + + modifier validCrossAddress(address eth, uint256 sub) { + if (eth == address(0) && sub == 0) { + revert InvalidArgument("Ethereum and Substrate addresses cannot be null at the same time"); + } + + if (eth != address(0) && sub != 0) { + revert InvalidArgument("Ethereum and Substrate addresses cannot be not null at the same time"); + } + + _; + } + + constructor(uint32 fee, uint64 timestamp) { + marketFee = fee; + ctime = timestamp; + + if (marketFee >= 100) { + revert InvalidMarketFee(); + } + } + + /** + * Fallback that allows this contract to receive native token. + * We need this for self-sponsoring + */ + fallback() external payable {} + + /** + * Receive also allows this contract to receive native token. + * We need this for self-sponsoring + */ + receive() external payable {} + + function getErc721(uint32 collectionId) private view returns (IERC721) { + address collectionAddress = collectionHelpers.collectionAddress( + collectionId + ); + + uint size; + assembly { + size := extcodesize(collectionAddress) + } + + if (size == 0) { + revert CollectionNotFound(); + } + + if (!collectionAddress.supportsInterface(InterfaceId_ERC721)) { + revert CollectionNotSupportedERC721(); + } + + return IERC721(collectionAddress); + } + + /** + * Add new admin. Only owner or an existing admin can add admins. + * + * @param admin: Address of a new admin to add + */ + function addAdmin(address admin) public onlyAdmin { + admins[admin] = true; + } + + /** + * Remove an admin. Only owner or an existing admin can remove admins. + * + * @param admin: Address of a new admin to add + */ + function removeAdmin(address admin) public onlyAdmin { + delete admins[admin]; + } + + /** + * Place an NFT or RFT token for sale. It must be pre-approved for transfers by this contract address. + * + * @param collectionId: ID of the token collection + * @param tokenId: ID of the token + * @param price: Price (with proper network currency decimals) + * @param amount: Number of token fractions to list (must always be 1 for NFT) + * @param seller: The seller cross-address (the beneficiary account to receive payment, may be different from transaction sender) + */ + function put( + uint32 collectionId, + uint32 tokenId, + uint256 price, + uint32 amount, + CrossAddress memory seller + ) public validCrossAddress(seller.eth, seller.sub) { + if (price == 0) { + revert InvalidArgument("price must not be zero"); + } + if (amount == 0) { + revert InvalidArgument("amount must not be zero"); + } + + if (orders[collectionId][tokenId].price > 0) { + revert TokenIsAlreadyOnSale(); + } + + IERC721 erc721 = getErc721(collectionId); + + if (erc721.ownerOf(tokenId) != msg.sender) { + revert SellerIsNotOwner(); + } + + if (erc721.getApproved(tokenId) != address(this)) { + revert TokenIsNotApproved(); + } + + Order memory order = Order( + 0, + collectionId, + tokenId, + amount, + price, + seller + ); + + order.id = idCount++; + orders[collectionId][tokenId] = order; + + emit TokenIsUpForSale(version, order); + } + + /** + * Get information about the listed token order + * + * @param collectionId: ID of the token collection + * @param tokenId: ID of the token + * @return The order information + */ + function getOrder( + uint32 collectionId, + uint32 tokenId + ) external view returns (Order memory) { + return orders[collectionId][tokenId]; + } + + /** + * Revoke the token from the sale. Only the original lister can use this method. + * + * @param collectionId: ID of the token collection + * @param tokenId: ID of the token + * @param amount: Number of token fractions to de-list (must always be 1 for NFT) + */ + function revoke( + uint32 collectionId, + uint32 tokenId, + uint32 amount + ) external { + if (amount == 0) { + revert InvalidArgument("amount must not be zero"); + } + + Order memory order = orders[collectionId][tokenId]; + + if (order.price == 0) { + revert OrderNotFound(); + } + + if (amount > order.amount) { + revert TooManyAmountRequested(); + } + + IERC721 erc721 = getErc721(collectionId); + + address ethAddress; + if (order.seller.eth != address(0)) { + ethAddress = order.seller.eth; + } else { + ethAddress = payable(address(uint160(order.seller.sub >> 96))); + } + if (erc721.ownerOf(tokenId) != ethAddress) { + revert SellerIsNotOwner(); + } + + order.amount -= amount; + if (order.amount == 0) { + delete orders[collectionId][tokenId]; + } else { + orders[collectionId][tokenId] = order; + } + + emit TokenRevoke(version, order, amount); + } + + /** + * Test if the token is still approved to be transferred by this contract and delete the order if not. + * + * @param collectionId: ID of the token collection + * @param tokenId: ID of the token + */ + function checkApproved(uint32 collectionId, uint32 tokenId) public onlyAdmin { + Order memory order = orders[collectionId][tokenId]; + if (order.price == 0) { + revert OrderNotFound(); + } + + IERC721 erc721 = getErc721(collectionId); + + if (erc721.getApproved(tokenId) != address(this) || erc721.ownerOf(tokenId) != getAddressFromCrossAccount(order.seller)) { + uint32 amount = order.amount; + order.amount = 0; + emit TokenRevoke(version, order, amount); + + delete orders[collectionId][tokenId]; + } else { + emit TokenIsApproved(version, order); + } + } + + function getAddressFromCrossAccount(CrossAddress memory account) private pure returns (address) { + if (account.eth != address(0)) { + return account.eth; + } else { + return address(uint160(account.sub >> 96)); + } + } + + /** + * Revoke the token from the sale. Only the contract admin can use this method. + * + * @param collectionId: ID of the token collection + * @param tokenId: ID of the token + */ + function revokeAdmin(uint32 collectionId, uint32 tokenId) public onlyAdmin { + Order memory order = orders[collectionId][tokenId]; + if (order.price == 0) { + revert OrderNotFound(); + } + + uint32 amount = order.amount; + order.amount = 0; + emit TokenRevoke(version, order, amount); + + delete orders[collectionId][tokenId]; + } + + /** + * Buy a token (partially for an RFT). + * + * @param collectionId: ID of the token collection + * @param tokenId: ID of the token + * @param amount: Number of token fractions to buy (must always be 1 for NFT) + * @param buyer: Cross-address of the buyer, eth part must be equal to the transaction signer address + */ + function buy( + uint32 collectionId, + uint32 tokenId, + uint32 amount, + CrossAddress memory buyer + ) public payable validCrossAddress(buyer.eth, buyer.sub) nonReentrant { + if (msg.value == 0) { + revert InvalidArgument("msg.value must not be zero"); + } + if (amount == 0) { + revert InvalidArgument("amount must not be zero"); + } + + Order memory order = orders[collectionId][tokenId]; + if (order.price == 0) { + revert OrderNotFound(); + } + + if (amount > order.amount) { + revert TooManyAmountRequested(); + } + + uint256 totalValue = order.price * amount; + uint256 feeValue = (totalValue * marketFee) / 100; + + if (msg.value < totalValue) { + revert NotEnoughMoneyError(); + } + + IERC721 erc721 = getErc721(order.collectionId); + if (erc721.getApproved(tokenId) != address(this)) { + revert TokenIsNotApproved(); + } + + order.amount -= amount; + if (order.amount == 0) { + delete orders[collectionId][tokenId]; + } else { + orders[collectionId][tokenId] = order; + } + + address collectionAddress = collectionHelpers.collectionAddress(collectionId); + UniqueNFT nft = UniqueNFT(collectionAddress); + + nft.transferFromCross( + order.seller, + buyer, + order.tokenId + ); + + (uint256 totalRoyalty, RoyaltyAmount[] memory royalties) = sendRoyalties(collectionAddress, tokenId, totalValue - feeValue); + + if (totalRoyalty >= totalValue - feeValue) { + revert InvalidRoyaltiesError(totalRoyalty); + } + + sendMoney(order.seller, totalValue - feeValue - totalRoyalty); + + if (msg.value > totalValue) { + sendMoney(buyer, msg.value - totalValue); + } + + emit TokenIsPurchased(version, order, amount, buyer, royalties); + } + + function sendMoney(CrossAddress memory to, uint256 money) private { + address collectionAddress = collectionHelpers.collectionAddress(0); + + UniqueFungible fungible = UniqueFungible(collectionAddress); + + CrossAddressF memory fromF = CrossAddressF(address(this), 0); + CrossAddressF memory toF = CrossAddressF(to.eth, to.sub); + + fungible.transferFromCross(fromF, toF, money); + } + + function sendRoyalties(address collection, uint tokenId, uint sellPrice) private returns (uint256, RoyaltyAmount[] memory) { + RoyaltyAmount[] memory royalties = UniqueRoyaltyHelper.calculate(collection, tokenId, sellPrice); + + uint256 totalRoyalty = 0; + + for (uint256 i=0; i 0) { + payable(transferTo).transfer(balance); + } + } +} --- /dev/null +++ b/js-packages/tests/eth/marketplace-v2/marketplace.test.ts @@ -0,0 +1,240 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import * as web3 from 'web3'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {readFile} from 'fs/promises'; +import {SponsoringMode, itEth, usingEthPlaygrounds} from '../util/index.js'; +import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js'; +import {makeNames} from '../../util/index.js'; +import {expect} from 'chai'; + +const {dirname} = makeNames(import.meta.url); + +const MARKET_FEE = 1; + +describe('Market V2 Contract', () => { + let donor: IKeyringPair; + + before(async () => { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + + const marketOwner = await helper.eth.createAccountWithBalance(donor, 600n); + + await deployMarket(helper, marketOwner); + }); + }); + + async function deployMarket(helper: EthUniqueHelper, marketOwner: string) { + return await helper.ethContract.deployByCode( + marketOwner, + 'Market', + (await readFile(`${dirname}/Market.sol`)).toString(), + [ + { + solPath: '@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol', + fsPath: `${dirname}/../api/UniqueNFT.sol`, + }, + { + solPath: '@unique-nft/solidity-interfaces/contracts/UniqueFungible.sol', + fsPath: `${dirname}/../api/UniqueFungible.sol`, + }, + { + solPath: '@openzeppelin/contracts/utils/introspection/IERC165.sol', + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol`, + }, + { + solPath: '@openzeppelin/contracts/access/Ownable.sol', + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/access/Ownable.sol`, + }, + { + solPath: '@openzeppelin/contracts/utils/Context.sol', + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Context.sol`, + }, + { + solPath: '@openzeppelin/contracts/security/ReentrancyGuard.sol', + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol`, + }, + { + solPath: '@openzeppelin/contracts/utils/introspection/ERC165Checker.sol', + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/ERC165Checker.sol`, + }, + { + solPath: '@openzeppelin/contracts/token/ERC721/IERC721.sol', + fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol`, + }, + { + solPath: '@unique-nft/solidity-interfaces/contracts/CollectionHelpers.sol', + fsPath: `${dirname}/../api/CollectionHelpers.sol`, + }, + { + solPath: 'royalty/UniqueRoyaltyHelper.sol', + fsPath: `${dirname}/royalty/UniqueRoyaltyHelper.sol`, + }, + { + solPath: 'royalty/UniqueRoyalty.sol', + fsPath: `${dirname}/royalty/UniqueRoyalty.sol`, + }, + { + solPath: 'royalty/LibPart.sol', + fsPath: `${dirname}/royalty/LibPart.sol`, + }, + ], + 15000000, + [MARKET_FEE, 0], + ); + } + + function substrateAddressToHex(sub: Uint8Array| string, web3: web3.default) { + if(typeof sub === 'string') + return web3.utils.padLeft(web3.utils.toHex(web3.utils.toBN(sub)), 64); + else if(sub instanceof Uint8Array) + return web3.utils.padLeft(web3.utils.bytesToHex(Array.from(sub)), 64); + throw Error('Infallible'); + } + + itEth('Put + Buy [eth]', async ({helper}) => { + const ONE_TOKEN = helper.balance.getOneTokenNominal(); + const PRICE = 2n * ONE_TOKEN; // 2 UNQ + const marketOwner = await helper.eth.createAccountWithBalance(donor, 60000n); + const market = await deployMarket(helper, marketOwner); + const contractHelpers = helper.ethNativeContract.contractHelpers(marketOwner); + + // Set external sponsoring + await contractHelpers.methods.setSponsor(market.options.address, marketOwner).send({from: marketOwner}); + await contractHelpers.methods.confirmSponsorship(market.options.address).send({from: marketOwner}); + + // Configure sponsoring + await contractHelpers.methods.setSponsoringMode(market.options.address, SponsoringMode.Generous).send({from: marketOwner}); + await contractHelpers.methods.setSponsoringRateLimit(market.options.address, 0).send({from: marketOwner}); + + const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(marketOwner, 'Sponsor', 'absolutely anything', 'ROC'); + const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', marketOwner, true); + + // Set collection sponsoring + await collection.methods.setCollectionSponsor(marketOwner).send({from: marketOwner}); + await collection.methods.confirmCollectionSponsorship().send({from: marketOwner}); + + const sellerCross = helper.ethCrossAccount.createAccount(); + const result = await collection.methods.mintCross(sellerCross, []).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + await collection.methods.approve(market.options.address, tokenId).send({from: sellerCross.eth}); + + // Seller has no funds at all, his transactions are sponsored + const sellerBalance = await helper.balance.getEthereum(sellerCross.eth); + expect(sellerBalance).to.be.eq(0n); + + const putResult = await market.methods.put(collectionId, tokenId, PRICE.toString(), 1, sellerCross).send({ + from: sellerCross.eth, gasLimit: 1_000_000, + }); + expect(putResult.events.TokenIsUpForSale).is.not.undefined; + + // Seller balance are still 0 + const sellerBalanceAfter = await helper.balance.getEthereum(sellerCross.eth); + expect(sellerBalanceAfter).to.be.eq(0n); + + let ownerCross = await collection.methods.ownerOfCross(tokenId).call(); + expect(ownerCross.eth).to.be.eq(sellerCross.eth); + expect(ownerCross.sub).to.be.eq(sellerCross.sub); + + const buyerCross = await helper.ethCrossAccount.createAccountWithBalance(donor, 10n); + + // Buyer has only 10 UNQ + const buyerBalance = await helper.balance.getEthereum(buyerCross.eth); + expect(buyerBalance).to.be.eq(10n * ONE_TOKEN); + + const buyResult = await market.methods.buy(collectionId, tokenId, 1, buyerCross).send({from: buyerCross.eth, value: PRICE.toString(), gasLimit: 1_000_000}); + expect(buyResult.events.TokenIsPurchased).is.not.undefined; + + // Buyer pays only value, transaction use sponsoring + const buyerBalanceAfter = await helper.balance.getEthereum(buyerCross.eth); + expect(buyerBalanceAfter).to.be.eq(10n * ONE_TOKEN - PRICE); + + ownerCross = await collection.methods.ownerOfCross(tokenId).call(); + expect(ownerCross.eth).to.be.eq(buyerCross.eth); + expect(ownerCross.sub).to.be.eq(buyerCross.sub); + }); + + itEth('Put + Buy [sub]', async ({helper}) => { + const ONE_TOKEN = helper.balance.getOneTokenNominal(); + const PRICE = 2n * ONE_TOKEN; // 2 UNQ + const web3 = helper.getWeb3(); + const marketOwner = await helper.eth.createAccountWithBalance(donor, 600n); + const market = await deployMarket(helper, marketOwner); + const contractHelpers = helper.ethNativeContract.contractHelpers(marketOwner); + + // Set self sponsoring from contract balance + await contractHelpers.methods.selfSponsoredEnable(market.options.address).send({from: marketOwner}); + await helper.eth.transferBalanceFromSubstrate(donor, market.options.address, 10n); + + // Configure sponsoring + await contractHelpers.methods.setSponsoringMode(market.options.address, SponsoringMode.Generous).send({from: marketOwner}); + await contractHelpers.methods.setSponsoringRateLimit(market.options.address, 0).send({from: marketOwner}); + + const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(marketOwner, 'Sponsor', 'absolutely anything', 'ROC'); + const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', marketOwner, true); + + // Set collection sponsoring + await collection.methods.setCollectionSponsor(marketOwner).send({from: marketOwner}); + await collection.methods.confirmCollectionSponsorship().send({from: marketOwner}); + + const seller = helper.util.fromSeed(`//Market-seller-${(new Date()).getTime()}`); + const sellerCross = helper.ethCrossAccount.fromKeyringPair(seller); + + // Seller has no funds at all, his transactions are sponsored + { + const sellerBalance = await helper.balance.getSubstrate(seller.address); + expect(sellerBalance).to.be.eq(0n); + } + + const result = await collection.methods.mintCross(sellerCross, []).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + await helper.nft.approveToken(seller, collectionId, tokenId, {Ethereum: market.options.address}); + + await helper.eth.sendEVM(seller, market.options.address, market.methods.put(collectionId, tokenId, PRICE, 1, sellerCross).encodeABI(), '0'); + // Seller balance is still zero + { + const sellerBalance = await helper.balance.getSubstrate(seller.address); + expect(sellerBalance).to.be.eq(0n); + } + let ownerCross = await collection.methods.ownerOfCross(tokenId).call(); + expect(ownerCross.eth).to.be.eq(sellerCross.eth); + expect(substrateAddressToHex(ownerCross.sub, web3)).to.be.eq(substrateAddressToHex(sellerCross.sub, web3)); + + const [buyer] = await helper.arrange.createAccounts([600n], donor); + // Buyer has only expected balance + { + const buyerBalance = await helper.balance.getSubstrate(buyer.address); + expect(buyerBalance).to.be.eq(600n * ONE_TOKEN); + } + const buyerCross = helper.ethCrossAccount.fromKeyringPair(buyer); + + const buyerBalanceBefore = await helper.balance.getSubstrate(buyer.address); + await helper.eth.sendEVM(buyer, market.options.address, market.methods.buy(collectionId, tokenId, 1, buyerCross).encodeABI(), PRICE.toString()); + const buyerBalanceAfter = await helper.balance.getSubstrate(buyer.address); + // Buyer balance not changed: transaction is sponsored + expect(buyerBalanceBefore).to.be.eq(buyerBalanceAfter + PRICE); + + const sellerBalanceAfterBuy = BigInt(await helper.balance.getSubstrate(seller.address)); + ownerCross = await collection.methods.ownerOfCross(tokenId).call(); + expect(ownerCross.eth).to.be.eq(buyerCross.eth); + expect(substrateAddressToHex(ownerCross.sub, web3)).to.be.eq(substrateAddressToHex(buyerCross.sub, web3)); + + // Seller got only PRICE - MARKET_FEE + expect(sellerBalanceAfterBuy).to.be.eq(PRICE * BigInt(100 - MARKET_FEE) / 100n); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/marketplace-v2/royalty/LibPart.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./UniqueRoyalty.sol"; + +library LibPart { + bytes32 public constant TYPE_HASH = keccak256("Part(address account,uint96 value)"); + + struct Part { + address payable account; + uint96 value; + } + + function hash(Part memory part) internal pure returns (bytes32) { + return keccak256(abi.encode(TYPE_HASH, part.account, part.value)); + } +} + +library LibPartAdapter { + function encode(LibPart.Part[] memory parts) internal pure returns (bytes memory) { + if (parts.length == 0) return ""; + + uint256[] memory encoded = new uint256[](parts.length * 2); + + for (uint i = 0; i < parts.length; i++) { + encoded[i * 2] = 0x0100000000000000000000000000000000000000000000040000000000000000 | uint256(parts[i].value); + encoded[i * 2 + 1] = uint256(uint160(address(parts[i].account))); + } + + return abi.encodePacked(encoded); + } + + function decode(bytes memory b) internal pure returns (LibPart.Part[] memory) { + if (b.length == 0) return new LibPart.Part[](0); + + require((b.length % (32 * 2)) == 0, "Invalid bytes length, expected (32 * 2) * UniqueRoyaltyParts count"); + uint partsCount = b.length / (32 * 2); + uint numbersCount = partsCount * 2; + + LibPart.Part[] memory parts = new LibPart.Part[](partsCount); + + // need this because numbers encoded via abi.encodePacked + bytes memory prefix = new bytes(64); + + assembly { + mstore(add(prefix, 32), 32) + mstore(add(prefix, 64), numbersCount) + } + + uint256[] memory encoded = abi.decode(bytes.concat(prefix, b), (uint256[])); + + for (uint i = 0; i < partsCount; i++) { + uint96 value = uint96(encoded[i * 2] & 0xFFFFFFFFFFFFFFFF); + address account = address(uint160(encoded[i * 2 + 1])); + + parts[i] = LibPart.Part({ + account: payable(account), + value: value + }); + } + + return parts; + } +} + +library LibPartAdapterComplex { + function decodeSafe(bytes memory data) internal pure returns (LibPart.Part[] memory) { + return fromUniqueRoyalties(UniqueRoyalty.decode(data)); + } + + function encodeSafe(LibPart.Part[] memory parts) internal pure returns (bytes memory) { + return UniqueRoyalty.encode(toUniqueRoyalties(parts)); + } + + function fromUniqueRoyalties(UniqueRoyaltyPart[] memory royalties) internal pure returns (LibPart.Part[] memory) { + LibPart.Part[] memory parts = new LibPart.Part[](royalties.length); + + for (uint i = 0; i < royalties.length; i++) { + uint96 value = royalties[i].decimals >= 4 + ? uint96(royalties[i].value * (10 ** (royalties[i].decimals - 4))) + : uint96(royalties[i].value / (10 ** (4 - royalties[i].decimals))); + + parts[i] = LibPart.Part({ + account: payable(CrossAddressLib.toAddress(royalties[i].crossAddress)), + value: value + }); + } + + return parts; + } + + function toUniqueRoyalties(LibPart.Part[] memory parts) internal pure returns (UniqueRoyaltyPart[] memory) { + UniqueRoyaltyPart[] memory royalties = new UniqueRoyaltyPart[](parts.length); + + for (uint i = 0; i < parts.length; i++) { + royalties[i] = UniqueRoyaltyPart({ + version: 1, + value: uint64(parts[i].value), + decimals: 4, + crossAddress: CrossAddress({ + sub: 0, + eth: parts[i].account + }), + isPrimarySaleOnly: false + }); + } + + return royalties; + } +} \ No newline at end of file --- /dev/null +++ b/js-packages/tests/eth/marketplace-v2/royalty/UniqueRoyalty.sol @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.17; + +import { CrossAddress } from "@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol"; + +struct UniqueRoyaltyPart { + uint8 version; + uint8 decimals; + uint64 value; + bool isPrimarySaleOnly; + CrossAddress crossAddress; +} + +library CrossAddressLib { + function toAddress(CrossAddress memory crossAddress) internal pure returns (address) { + return crossAddress.eth != address(0) ? crossAddress.eth : address(uint160(crossAddress.sub >> 96)); + } +} + +library UniqueRoyalty { + uint private constant DECIMALS_OFFSET = 4 * 16; + uint private constant ADDRESS_TYPE_OFFSET = 4 * (16 + 2); // 0 - eth, 1 - sub + uint private constant ROYALTY_TYPE_OFFSET = 4 * (16 + 2 + 1); // 0 - default, 1 - primary sale only + uint private constant VERSION_OFFSET = 4 * (16 + 2 + 1 + 1 + 42); + + uint private constant PART_LENGTH = 32 * 2; + + function decode(bytes memory b) internal pure returns (UniqueRoyaltyPart[] memory) { + if (b.length == 0) return new UniqueRoyaltyPart[](0); + + require((b.length % PART_LENGTH) == 0, "Invalid bytes length, expected (32 * 2) * UniqueRoyaltyParts count"); + uint partsCount = b.length / PART_LENGTH; + uint numbersCount = partsCount * 2; + + UniqueRoyaltyPart[] memory parts = new UniqueRoyaltyPart[](partsCount); + + // need this because numbers encoded via abi.encodePacked + bytes memory prefix = new bytes(64); + + assembly { + mstore(add(prefix, 32), 32) + mstore(add(prefix, 64), numbersCount) + } + + uint256[] memory encoded = abi.decode(bytes.concat(prefix, b), (uint256[])); + + for (uint i = 0; i < partsCount; i++) { + parts[i] = decodePart(encoded[i * 2], encoded[i * 2 + 1]); + } + + return parts; + } + + function encode(UniqueRoyaltyPart[] memory parts) internal pure returns (bytes memory) { + if (parts.length == 0) return ""; + + uint256[] memory encoded = new uint256[](parts.length * 2); + + for (uint i = 0; i < parts.length; i++) { + (uint256 encodedMeta, uint256 encodedAddress) = encodePart(parts[i]); + + encoded[i * 2] = encodedMeta; + encoded[i * 2 + 1] = encodedAddress; + } + + return abi.encodePacked(encoded); + } + + function decodePart(bytes memory b) internal pure returns (UniqueRoyaltyPart memory) { + require(b.length == PART_LENGTH, "Invalid bytes length, expected 32 * 2"); + + uint256[2] memory encoded = abi.decode(b, (uint256[2])); + + return decodePart(encoded[0], encoded[1]); + } + + function decodePart( + uint256 _meta, + uint256 _address + ) internal pure returns (UniqueRoyaltyPart memory) { + uint256 version = _meta >> VERSION_OFFSET; + bool isPrimarySaleOnly = (_meta & (1 << ROYALTY_TYPE_OFFSET)) > 0; + bool isEthereumAddress = (_meta & (1 << ADDRESS_TYPE_OFFSET)) == 0; + uint256 decimals = (_meta >> 4 * 16) & 0xFF; + uint256 value = _meta & 0xFFFFFFFFFFFFFFFF; + + CrossAddress memory crossAddress = isEthereumAddress + ? CrossAddress({ sub: 0, eth: address(uint160(_address)) }) + : CrossAddress({ sub: _address, eth: address(0) }); + + UniqueRoyaltyPart memory royaltyPart = UniqueRoyaltyPart({ + version: uint8(version), + decimals: uint8(decimals), + value: uint64(value), + isPrimarySaleOnly: isPrimarySaleOnly, + crossAddress: crossAddress + }); + + return royaltyPart; + } + + function encodePart(UniqueRoyaltyPart memory royaltyPart) internal pure returns (uint256, uint256) { + uint256 encodedMeta = 0; + uint256 encodedAddress = 0; + + encodedMeta |= uint256(royaltyPart.version) << VERSION_OFFSET; + if (royaltyPart.isPrimarySaleOnly) encodedMeta |= 1 << ROYALTY_TYPE_OFFSET; + + if (royaltyPart.crossAddress.eth == address(0x0)) { + encodedMeta |= 1 << ADDRESS_TYPE_OFFSET; + encodedAddress = royaltyPart.crossAddress.sub; + } else { + encodedAddress = uint256(uint160(royaltyPart.crossAddress.eth)); + } + + encodedMeta |= uint256(royaltyPart.decimals) << DECIMALS_OFFSET; + encodedMeta |= uint256(royaltyPart.value); + + return (encodedMeta, encodedAddress); + } +} \ No newline at end of file --- /dev/null +++ b/js-packages/tests/eth/marketplace-v2/royalty/UniqueRoyaltyHelper.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.17; + +import "./UniqueRoyalty.sol"; +import "./LibPart.sol"; + +string constant ROYALTIES_PROPERTY = "royalties"; + +interface ICollection { + function collectionProperty(string memory key) external view returns (bytes memory); + function property(uint256 tokenId, string memory key) external view returns (bytes memory); +} + +struct RoyaltyAmount { + CrossAddress crossAddress; + uint amount; +} + +library UniqueRoyaltyHelper { + function encodePart(UniqueRoyaltyPart memory part) internal pure returns (bytes memory) { + (uint256 encodedMeta, uint256 encodedAddress) = UniqueRoyalty.encodePart(part); + + return abi.encodePacked(encodedMeta, encodedAddress); + } + + function decodePart(bytes memory data) internal pure returns (UniqueRoyaltyPart memory) { + return UniqueRoyalty.decodePart(data); + } + + // todo - implement smth better - check royalties sum is lte 100% + function validatePart(bytes memory b) internal pure returns (bool isValid) { + isValid = b.length == 64; + } + + function encode(UniqueRoyaltyPart[] memory royalties) internal pure returns (bytes memory) { + return UniqueRoyalty.encode(royalties); + } + + function decode(bytes memory data) internal pure returns (UniqueRoyaltyPart[] memory) { + return UniqueRoyalty.decode(data); + } + + // todo - implement smth better - check royalties sum is lte 100% + function validate(bytes memory b) internal pure returns (bool) { + return b.length % 64 == 0; + } + + function getTokenRoyalty(address collection, uint tokenId) internal view returns (UniqueRoyaltyPart[] memory) { + try ICollection(collection).property(tokenId, ROYALTIES_PROPERTY) returns (bytes memory encoded) { + return UniqueRoyalty.decode(encoded); + } catch { + return new UniqueRoyaltyPart[](0); + } + } + + function getCollectionRoyalty(address collection) internal view returns (UniqueRoyaltyPart[] memory) { + try ICollection(collection).collectionProperty(ROYALTIES_PROPERTY) returns (bytes memory encoded) { + return UniqueRoyalty.decode(encoded); + } catch { + return new UniqueRoyaltyPart[](0); + } + } + + function getRoyalty(address collection, uint tokenId) internal view returns (UniqueRoyaltyPart[] memory royalty) { + royalty = getTokenRoyalty(collection, tokenId); + + if (royalty.length == 0) { + royalty = getCollectionRoyalty(collection); + } + } + + function calculateRoyalties(UniqueRoyaltyPart[] memory royalties, bool isPrimarySale, uint sellPrice) internal pure returns (RoyaltyAmount[] memory) { + RoyaltyAmount[] memory royaltyAmounts = new RoyaltyAmount[](royalties.length); + uint amountsCount = 0; + + for (uint i = 0; i < royalties.length; i++) { + if (isPrimarySale == royalties[i].isPrimarySaleOnly) { + uint amount = (sellPrice * royalties[i].value) / (10 ** (royalties[i].decimals)); + + royaltyAmounts[amountsCount] = RoyaltyAmount({ + crossAddress: royalties[i].crossAddress, + amount: amount + }); + + amountsCount += 1; + } + } + + // shrink royaltyAmounts to amountsCount length + assembly { mstore(royaltyAmounts, amountsCount) } + + return royaltyAmounts; + } + + function calculateForPrimarySale(address collection, uint tokenId, uint sellPrice) internal view returns (RoyaltyAmount[] memory) { + UniqueRoyaltyPart[] memory royalties = getRoyalty(collection, tokenId); + + return calculateRoyalties(royalties, true, sellPrice); + } + + function calculate(address collection, uint tokenId, uint sellPrice) internal view returns (RoyaltyAmount[] memory) { + UniqueRoyaltyPart[] memory royalties = getRoyalty(collection, tokenId); + + return calculateRoyalties(royalties, false, sellPrice); + } +} \ No newline at end of file --- /dev/null +++ b/js-packages/tests/eth/marketplace-v2/utils.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +contract Utils { + function toString(address account) public pure returns (string memory) { + return toString(abi.encodePacked(account)); + } + + function toString(bool value) public pure returns (string memory) { + return value ? "true" : "false"; + } + + function toString(uint256 value) public pure returns (string memory) { + return toString(abi.encodePacked(value)); + } + + function toString(bytes32 value) public pure returns (string memory) { + return toString(abi.encodePacked(value)); + } + + function toString(bytes memory data) public pure returns (string memory) { + bytes memory alphabet = "0123456789abcdef"; + + bytes memory str = new bytes(2 + data.length * 2); + str[0] = "0"; + str[1] = "x"; + for (uint i = 0; i < data.length; i++) { + str[2 + i * 2] = alphabet[uint(uint8(data[i] >> 4))]; + str[3 + i * 2] = alphabet[uint(uint8(data[i] & 0x0f))]; + } + return string(str); + } +} \ No newline at end of file --- /dev/null +++ b/js-packages/tests/eth/marketplace/MarketPlace.sol @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: Apache License +pragma solidity >=0.8.0; +import {UniqueNFT, Dummy, ERC165} from "../api/UniqueNFT.sol"; + +// Inline +interface ERC20Events { + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); +} + +interface ERC20 is Dummy, ERC165, ERC20Events { + // Selector: name() 06fdde03 + function name() external view returns (string memory); + + // Selector: symbol() 95d89b41 + function symbol() external view returns (string memory); + + // Selector: totalSupply() 18160ddd + function totalSupply() external view returns (uint256); + + // Selector: decimals() 313ce567 + function decimals() external view returns (uint8); + + // Selector: balanceOf(address) 70a08231 + function balanceOf(address owner) external view returns (uint256); + + // Selector: transfer(address,uint256) a9059cbb + function transfer(address to, uint256 amount) external returns (bool); + + // Selector: transferFrom(address,address,uint256) 23b872dd + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); + + // Selector: approve(address,uint256) 095ea7b3 + function approve(address spender, uint256 amount) external returns (bool); + + // Selector: allowance(address,address) dd62ed3e + function allowance(address owner, address spender) + external + view + returns (uint256); +} + +interface UniqueFungible is Dummy, ERC165, ERC20 {} + +contract MarketPlace { + struct Order { + + uint256 idNFT; + address currencyCode; // UNIQ tokens as address address (1); wKSM + uint256 price; + uint256 time; + address idCollection; + address ownerAddr; + uint8 flagActive; + string name; + string symbol; + string tokenURI; + } + Order[] public orders; + uint test; + mapping (address => uint256) public balanceKSM; // [ownerAddr][currency] => [KSMs] + mapping (address => mapping (uint256 => uint256)) public asks ; // [buyer][idCollection][idNFT] => idorder + mapping (address => mapping (uint => uint[])) public ordersbyNFT; // [addressCollection] =>idNFT =>idorder + + mapping (address => uint[]) public asksbySeller; // [addressSeller] =>idorder + + mapping (address =>bool) internal isEscrow; + + //address escrow; + address owner; + address nativecoin; + + // from abstract contract ReentrancyGuard + // Booleans are more expensive than uint256 or any type that takes up a full + // word because each write operation emits an extra SLOAD to first read the + // slot's contents, replace the bits taken up by the boolean, and then write + // back. This is the compiler's defense against contract upgrades and + // pointer aliasing, and it cannot be disabled. + + // The values being non-zero value makes deployment a bit more expensive, + // but in exchange the refund on every call to nonReentrant will be lower in + // amount. Since refunds are capped to a percentage of the total + // transaction's gas, it is best to keep them low in cases like this one, to + // increase the likelihood of the full refund coming into effect. + uint8 private constant _NOT_ENTERED = 1; + uint8 private constant _ENTERED = 2; + + uint8 private _status; + + struct NFT { + address collection; + uint256 id; + } + + //function initialize() public initializer { + constructor () { // call setEscrow directly + owner = msg.sender; + + orders.push(Order( + 0, + address(0), + 0, + 0, + address(0), + address(0), + 0, "","","")); + _status = _NOT_ENTERED; + + } + + modifier nonReentrant() { // from abstract contract ReentrancyGuard + // On the first call to nonReentrant, _notEntered will be true + require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _status = _ENTERED; + + _; + + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _status = _NOT_ENTERED; + } + + modifier onlyEscrow () { + require(isEscrow [msg.sender] , "Only escrow can"); + _; + } + + modifier onlyOwner () { + require(msg.sender == owner, "Only owner can"); + _; + } + + /** + * Make bids (orders) to sell NFTs + */ + + + receive () external payable { + // revert ("Can't accept payment without collection and IDs, use dApp to send"); + } + fallback () external payable { + revert ("No such function"); + } + + event AddedAsk (uint256 _price, + address _currencyCode, + address _idCollection, + uint256 _idNFT, + uint256 orderId + ); + event EditedAsk (uint256 _price, + address _currencyCode, + address _idCollection, + uint256 _idNFT, + uint8 _active, + uint orderId); + + event CanceledAsk (address _idCollection, + uint256 _idNFT, + uint orderId + ); + + event DepositedKSM (uint256 _amount, address _sender); + + event BoughtNFT4KSM (address _idCollection, uint256 _idNFT, uint orderID, uint orderPrice ); + + event BoughtNFT (address _idCollection, uint256 _idNFT, uint orderID, uint orderPrice ); + + event WithdrawnAllKSM (address _sender, uint256 balance); + + event WithdrawnKSM (address _sender, uint256 balance); + + event Withdrawn (uint256 _amount, address _currencyCode, address _sender); + + + function setOwner (address _newOwner) public onlyOwner { + owner = _newOwner; + } + + function setEscrow (address _escrow, bool _state) public onlyOwner returns (bool) { + if (isEscrow[_escrow] != _state) { + isEscrow[_escrow] = _state; + return true; + } + return false; + } + + function setNativeCoin (address _coin) public onlyOwner { + nativecoin = _coin; + } + + + function addAsk (uint256 _price, + address _currencyCode, + address _idCollection, + uint256 _idNFT + ) public { // + address ownerNFT = UniqueNFT(_idCollection).ownerOf(_idNFT); + require (ownerNFT == msg.sender, "Only token owner can make ask"); + string memory nameNFT; + string memory symbolNFT; + string memory uriNFT; + try UniqueNFT(_idCollection).name() returns (string memory name_) { + nameNFT = name_; + } + catch { + nameNFT=""; + } + try UniqueNFT(_idCollection).symbol() returns (string memory symbol_) { + symbolNFT = symbol_; + } + catch { + symbolNFT=""; + } + try UniqueNFT(_idCollection).tokenURI(_idNFT) returns (string memory uri_) { + uriNFT = uri_; + } + catch { + uriNFT=""; + } + orders.push(Order( + _idNFT, + _currencyCode, + _price, + block.timestamp, + _idCollection, + msg.sender, + 1, // 1 = is active + nameNFT, + symbolNFT, + uriNFT + )); + + uint orderId = orders.length-1; + asks[_idCollection][_idNFT] = orderId; + asksbySeller[msg.sender].push(orderId); + UniqueNFT(_idCollection).transferFrom(msg.sender, address(this), _idNFT); + emit AddedAsk(_price, _currencyCode, _idCollection, _idNFT, orderId); + } + + function editAsk (uint256 _price, + address _currencyCode, + address _idCollection, + uint256 _idNFT, + uint8 _active) public { + + + uint orderID = asks[_idCollection][_idNFT]; + + require (orders[orderID].ownerAddr == msg.sender, "Only token owner can edit ask"); + require (orders[orderID].flagActive != 0, "This ask is closed"); + if (_price> 0 ) { + orders[orderID].price = _price ; + } + + if (_currencyCode != address(0) ) { + orders[orderID].currencyCode = _currencyCode ; + } + + orders[orderID].time = block.timestamp; + orders[orderID].flagActive = _active; + + emit EditedAsk(_price, _currencyCode, _idCollection, _idNFT, _active, orderID); + } + + + function cancelAsk (address _idCollection, + uint256 _idNFT + ) public { + + uint orderID = asks[_idCollection][_idNFT]; + + require (orders[orderID].ownerAddr == msg.sender, "Only token owner can edit ask"); + require (orders[orderID].flagActive != 0, "This ask is closed"); + + orders[orderID].time = block.timestamp; + orders[orderID].flagActive = 0; + UniqueNFT(_idCollection).transferFrom(address(this),orders[orderID].ownerAddr, _idNFT); + emit CanceledAsk(_idCollection, _idNFT, orderID); + } + + + function depositKSM (uint256 _amount, address _sender) public onlyEscrow { + balanceKSM[_sender] = balanceKSM[_sender] + _amount; + emit DepositedKSM(_amount, _sender); + } + + function buyKSM (address _idCollection, uint256 _idNFT, address _buyer, address _receiver ) public { + + Order memory order = orders[ asks[_idCollection][_idNFT]]; + require(isEscrow[msg.sender] || msg.sender == _buyer, "Only escrow or buyer can call buyKSM" ); + //1. reduce balance + + balanceKSM[_buyer] = balanceKSM[_buyer] - order.price; + balanceKSM[order.ownerAddr] = balanceKSM[order.ownerAddr] + order.price; + // 2. close order + orders[ asks[_idCollection][_idNFT]].flagActive = 0; + // 3. transfer NFT to buyer + UniqueNFT(_idCollection).transferFrom(address(this), _receiver, _idNFT); + emit BoughtNFT4KSM(_idCollection, _idNFT, asks[_idCollection][_idNFT], order.price); + + } + function buy (address _idCollection, uint256 _idNFT ) public payable returns (bool result) { //buing for UNQ like as ethers + + Order memory order = orders[asks[_idCollection][_idNFT]]; + //1. check sent amount and send to seller + require (msg.value == order.price, "Not right amount sent, have to be equal price" ); + // 2. close order + orders[ asks[_idCollection][_idNFT]].flagActive = 0; + + // 3. transfer NFT to buyer + UniqueNFT(_idCollection).transferFrom(address(this), msg.sender, _idNFT); + //uint balance = address(this).balance; + result = payable(order.ownerAddr).send (order.price); + emit BoughtNFT(_idCollection, _idNFT, asks[_idCollection][_idNFT], order.price); + + } + +/* + function buyOther (address _idCollection, uint256 _idNFT, address _currencyCode, uint _amount ) public { //buy for sny token if seller wants + + Order memory order = orders[ asks[_idCollection][_idNFT]]; + //1. check sent amount and transfer from buyer to seller + require (order.price == _amount && order.currencyCode == _currencyCode, "Not right amount or currency sent, have to be equal currency and price" ); + // !!! transfer have to be approved to marketplace! + UniqueFungible(order.currencyCode).transferFrom(msg.sender, address(this), order.price); //to not disclojure buyer's address + UniqueFungible(order.currencyCode).transfer(order.ownerAddr, order.price); + // 2. close order + orders[ asks[_idCollection][_idNFT]].flagActive = 0; + // 3. transfer NFT to buyer + UniqueNFT(_idCollection).transferFrom(address(this), msg.sender, _idNFT); + + + } + */ + + function withdrawAllKSM (address _sender) public nonReentrant returns (uint lastBalance ){ + require(isEscrow[msg.sender] || msg.sender == _sender, "Only escrow or balance owner can withdraw all KSM" ); + + lastBalance = balanceKSM[_sender]; + balanceKSM[_sender] =0; + emit WithdrawnAllKSM(_sender, lastBalance); + } + + function withdrawKSM (uint _amount, address _sender) onlyEscrow public { + balanceKSM[_sender] = balanceKSM[_sender] - _amount; + emit WithdrawnKSM(_sender, balanceKSM[_sender]); + + } + + function withdraw (uint256 _amount, address _currencyCode) public nonReentrant returns (bool result ){ //onlyOwner + address payable _sender = payable( msg.sender); + if (_currencyCode != nativecoin ) { //erc20 compat. tokens on UNIQUE chain + // uint balance = UniqueFungible(_currencyCode).balanceOf(address(this)); + UniqueFungible(_currencyCode).transfer(_sender, _amount); + } else { + // uint balance = address(this).balance; + + result = (_sender).send(_amount); // for UNQ like as ethers + } + emit Withdrawn(_amount, _currencyCode, _sender); + return result; + + } + + + // event GettedOrder(uint , Order); + function getOrder (address _idCollection, uint256 _idNFT) public view returns (Order memory) { + uint orderId = asks[_idCollection][_idNFT]; + Order memory order = orders[orderId]; + // emit GettedOrder (orderId, order); + return order; + } + + function getOrdersLen () public view returns (uint) { + return orders.length; + } + + function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) public pure returns(bytes4) { + return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); + } + + //TODO make destructing function to return all + function terminate(address[] calldata tokens, NFT[] calldata nfts) public onlyOwner { + // Transfer tokens to owner (TODO: error handling) + for (uint i = 0; i < tokens.length; i++) { + address addr = tokens[i]; + UniqueFungible token = UniqueFungible(addr); + uint256 balance = token.balanceOf(address(this)); + token.transfer(owner, balance); + } + for (uint i = 0; i < nfts.length; i++) { + address addr = nfts[i].collection; + UniqueNFT token = UniqueNFT(addr); + token.transferFrom(address(this), owner, nfts[i].id); + } + // Transfer Eth to owner and terminate contract + address payable owner1 = payable (owner); + uint balance = address(this).balance; + owner1.transfer(balance); + + } + +} --- /dev/null +++ b/js-packages/tests/eth/marketplace/marketplace.test.ts @@ -0,0 +1,208 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {readFile} from 'fs/promises'; +import {itEth, usingEthPlaygrounds, expect, SponsoringMode} from '../util/index.js'; +import {makeNames} from '../../util/index.js'; + +const {dirname} = makeNames(import.meta.url); + +describe('Matcher contract usage', () => { + const PRICE = 2000n; + let donor: IKeyringPair; + let alice: IKeyringPair; + let aliceMirror: string; + let aliceDoubleMirror: string; + let seller: IKeyringPair; + let sellerMirror: string; + + before(async () => { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + beforeEach(async () => { + await usingEthPlaygrounds(async (helper, privateKey) => { + [alice] = await helper.arrange.createAccounts([1000n], donor); + aliceMirror = helper.address.substrateToEth(alice.address).toLowerCase(); + aliceDoubleMirror = helper.address.ethToSubstrate(aliceMirror); + seller = await privateKey(`//Seller/${Date.now()}`); + sellerMirror = helper.address.substrateToEth(seller.address).toLowerCase(); + + await helper.balance.transferToSubstrate(donor, aliceDoubleMirror, 10_000_000_000_000_000_000n); + }); + }); + + itEth('With UNQ', async ({helper}) => { + const matcherOwner = await helper.eth.createAccountWithBalance(donor); + const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2); + + const sponsor = await helper.eth.createAccountWithBalance(donor); + const helpers = await helper.ethNativeContract.contractHelpers(matcherOwner); + await helpers.methods.setSponsoringMode(matcher.options.address, SponsoringMode.Allowlisted).send({from: matcherOwner}); + await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner}); + + await helpers.methods.setSponsor(matcher.options.address, sponsor).send({from: matcherOwner}); + await helpers.methods.confirmSponsorship(matcher.options.address).send({from: sponsor}); + + const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}, pendingSponsor: {Substrate: alice.address}}); + await collection.confirmSponsorship(alice); + await collection.addToAllowList(alice, {Substrate: aliceDoubleMirror}); + const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); + await helper.eth.transferBalanceFromSubstrate(donor, aliceMirror); + + await helpers.methods.toggleAllowed(matcher.options.address, aliceMirror, true).send({from: matcherOwner}); + await helpers.methods.toggleAllowed(matcher.options.address, sellerMirror, true).send({from: matcherOwner}); + + const token = await collection.mintToken(alice, {Ethereum: sellerMirror}); + + // Token is owned by seller initially + expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror}); + + // Ask + { + await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0'); + await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0'); + } + + // Token is transferred to matcher + expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()}); + + // Buy + { + const sellerBalanceBeforePurchase = await helper.balance.getSubstrate(seller.address); + await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buy(evmCollection.options.address, token.tokenId).encodeABI(), PRICE.toString()); + expect(await helper.balance.getSubstrate(seller.address) - sellerBalanceBeforePurchase === PRICE); + } + + // Token is transferred to evm account of alice + expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror}); + + // Transfer token to substrate side of alice + await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address}); + + // Token is transferred to substrate account of alice, seller received funds + expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address}); + }); + + itEth('With escrow', async ({helper}) => { + const matcherOwner = await helper.eth.createAccountWithBalance(donor); + const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2); + + const sponsor = await helper.eth.createAccountWithBalance(donor); + const escrow = await helper.eth.createAccountWithBalance(donor); + await matcher.methods.setEscrow(escrow, true).send({from: matcherOwner}); + const helpers = await helper.ethNativeContract.contractHelpers(matcherOwner); + await helpers.methods.setSponsoringMode(matcher.options.address, SponsoringMode.Allowlisted).send({from: matcherOwner}); + await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner}); + + await helpers.methods.setSponsor(matcher.options.address, sponsor).send({from: matcherOwner}); + await helpers.methods.confirmSponsorship(matcher.options.address).send({from: sponsor}); + + const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}, pendingSponsor: {Substrate: alice.address}}); + await collection.confirmSponsorship(alice); + await collection.addToAllowList(alice, {Substrate: aliceDoubleMirror}); + const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); + await helper.eth.transferBalanceFromSubstrate(donor, aliceMirror); + + + await helpers.methods.toggleAllowed(matcher.options.address, aliceMirror, true).send({from: matcherOwner}); + + await helpers.methods.toggleAllowed(matcher.options.address, sellerMirror, true).send({from: matcherOwner}); + + const token = await collection.mintToken(alice, {Ethereum: sellerMirror}); + + // Token is owned by seller initially + expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror}); + + // Ask + { + await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0'); + await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0'); + } + + // Token is transferred to matcher + expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()}); + + // Give buyer KSM + await matcher.methods.depositKSM(PRICE, aliceMirror).send({from: escrow}); + + // Buy + { + expect(await matcher.methods.balanceKSM(sellerMirror).call()).to.be.equal('0'); + expect(await matcher.methods.balanceKSM(aliceMirror).call()).to.be.equal(PRICE.toString()); + + await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buyKSM(evmCollection.options.address, token.tokenId, aliceMirror, aliceMirror).encodeABI(), '0'); + + // Price is removed from buyer balance, and added to seller + expect(await matcher.methods.balanceKSM(aliceMirror).call()).to.be.equal('0'); + expect(await matcher.methods.balanceKSM(sellerMirror).call()).to.be.equal(PRICE.toString()); + } + + // Token is transferred to evm account of alice + expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror}); + + // Transfer token to substrate side of alice + await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address}); + + // Token is transferred to substrate account of alice, seller received funds + expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address}); + }); + + itEth('Sell tokens from substrate user via EVM contract', async ({helper}) => { + const matcherOwner = await helper.eth.createAccountWithBalance(donor); + const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2); + + await helper.eth.transferBalanceFromSubstrate(donor, matcher.options.address); + + const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}}); + const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); + + await helper.balance.transferToSubstrate(donor, seller.address, 100_000_000_000_000_000_000n); + + const token = await collection.mintToken(alice, {Ethereum: sellerMirror}); + + // Token is owned by seller initially + expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror}); + + // Ask + { + await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0'); + await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0'); + } + + // Token is transferred to matcher + expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()}); + + // Buy + { + const sellerBalanceBeforePurchase = await helper.balance.getSubstrate(seller.address); + await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buy(evmCollection.options.address, token.tokenId).encodeABI(), PRICE.toString()); + expect(await helper.balance.getSubstrate(seller.address) - sellerBalanceBeforePurchase === PRICE); + } + + // Token is transferred to evm account of alice + expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror}); + + // Transfer token to substrate side of alice + await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address}); + + // Token is transferred to substrate account of alice, seller received funds + expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address}); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/migration.seqtest.ts @@ -0,0 +1,209 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {Struct} from '@polkadot/types'; + +import type {IEvent} from '@unique/playgrounds/types.js'; +import type {InterfaceTypes} from '@polkadot/types/types/registry'; +import {ApiPromise} from '@polkadot/api'; + +const encodeEvent = (api: ApiPromise, pallet: string, palletEvents: string, event: string, fields: any) => { + const palletIndex = api.runtimeMetadata.asV14.pallets.find(p => p.name.toString() == pallet)!.index.toNumber(); + const eventMeta = api.events[palletEvents][event].meta; + const eventIndex = eventMeta.index.toNumber(); + const data = [ + palletIndex, eventIndex, + ]; + const metaEvent = api.registry.findMetaEvent(new Uint8Array(data)); + data.push(...new Struct(api.registry, {data: metaEvent}, {data: fields}).toU8a()); + + const typeName = api.registry.lookup.names.find(n => n.endsWith('RuntimeEvent'))!; + const obj = api.registry.createType(typeName, new Uint8Array(data)) as InterfaceTypes['RuntimeEvent']; + return obj.toHex(); +}; + +describe('EVM Migrations', () => { + let superuser: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + superuser = await privateKey('//Alice'); + charlie = await privateKey('//Charlie'); + }); + }); + + // todo:playgrounds requires sudo, look into later + itEth('Deploy contract saved state', async ({helper}) => { + /* + contract StatefulContract { + uint counter; + mapping (uint => uint) kv; + + function inc() public { + counter = counter + 1; + } + function counterValue() public view returns (uint) { + return counter; + } + + function set(uint key, uint value) public { + kv[key] = value; + } + + function get(uint key) public view returns (uint) { + return kv[key]; + } + } + */ + const ADDRESS = '0x4956bf52ef9ed8789f21bc600e915e0d961079f6'; + const CODE = '0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80631ab06ee514610051578063371303c01461006d5780637bfdec3b146100775780639507d39a14610095575b600080fd5b61006b60048036038101906100669190610160565b6100c5565b005b6100756100e1565b005b61007f6100f8565b60405161008c91906101af565b60405180910390f35b6100af60048036038101906100aa9190610133565b610101565b6040516100bc91906101af565b60405180910390f35b8060016000848152602001908152602001600020819055505050565b60016000546100f091906101ca565b600081905550565b60008054905090565b600060016000838152602001908152602001600020549050919050565b60008135905061012d8161025e565b92915050565b60006020828403121561014957610148610259565b5b60006101578482850161011e565b91505092915050565b6000806040838503121561017757610176610259565b5b60006101858582860161011e565b92505060206101968582860161011e565b9150509250929050565b6101a981610220565b82525050565b60006020820190506101c460008301846101a0565b92915050565b60006101d582610220565b91506101e083610220565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156102155761021461022a565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b61026781610220565b811461027257600080fd5b5056fea26469706673582212206a02d2fb5c244105ab884961479c1aee3b4c1011e4b5530ab483eb22344a865664736f6c63430008060033'; + const DATA = [ + // counter = 10 + ['0x0000000000000000000000000000000000000000000000000000000000000000', '0x000000000000000000000000000000000000000000000000000000000000000a'], + // kv = {1: 1, 2: 2, 3: 3, 4: 4}, + ['0xcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f', '0x0000000000000000000000000000000000000000000000000000000000000001'], + ['0xd9d16d34ffb15ba3a3d852f0d403e2ce1d691fb54de27ac87cd2f993f3ec330f', '0x0000000000000000000000000000000000000000000000000000000000000002'], + ['0x7dfe757ecd65cbd7922a9c0161e935dd7fdbcc0e999689c7d31633896b1fc60b', '0x0000000000000000000000000000000000000000000000000000000000000003'], + ['0xedc95719e9a3b28dd8e80877cb5880a9be7de1a13fc8b05e7999683b6b567643', '0x0000000000000000000000000000000000000000000000000000000000000004'], + ]; + + const caller = await helper.eth.createAccountWithBalance(superuser); + + const txBegin = helper.constructApiCall('api.tx.evmMigration.begin', [ADDRESS]); + const txSetData = helper.constructApiCall('api.tx.evmMigration.setData', [ADDRESS, DATA]); + const txFinish = helper.constructApiCall('api.tx.evmMigration.finish', [ADDRESS, CODE]); + await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txBegin])).to.be.fulfilled; + await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txSetData])).to.be.fulfilled; + await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txFinish])).to.be.fulfilled; + + const web3 = helper.getWeb3(); + const contract = new web3.eth.Contract([ + { + inputs: [], + name: 'counterValue', + outputs: [{ + internalType: 'uint256', + name: '', + type: 'uint256', + }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ + internalType: 'uint256', + name: 'key', + type: 'uint256', + }], + name: 'get', + outputs: [{ + internalType: 'uint256', + name: '', + type: 'uint256', + }], + stateMutability: 'view', + type: 'function', + }, + ], ADDRESS, {from: caller, gas: helper.eth.DEFAULT_GAS}); + + expect(await contract.methods.counterValue().call()).to.be.equal('10'); + for(let i = 1; i <= 4; i++) { + expect(await contract.methods.get(i).call()).to.be.equal(i.toString()); + } + }); + itEth('Fake collection creation on substrate side', async ({helper}) => { + const txInsertEvents = helper.constructApiCall('api.tx.evmMigration.insertEvents', [[ + encodeEvent(helper.getApi(), 'Common', 'common', 'CollectionCreated', [ + // Collection Id + 9999, + // Collection mode: NFT + 1, + // Owner + charlie.address, + ]), + ]]); + await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEvents]); + const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; + const eventStrings = event.map(e => `${e.section}.${e.method}`); + + expect(eventStrings).to.contain('common.CollectionCreated'); + }); + itEth('Fake token creation on substrate side', async ({helper}) => { + const txInsertEvents = helper.constructApiCall('api.tx.evmMigration.insertEvents', [[ + encodeEvent(helper.getApi(), 'Common', 'common', 'ItemCreated', [ + // Collection Id + 9999, + // TokenId + 9999, + // Owner + {Substrate: charlie.address}, + // Amount + 1, + ]), + ]]); + await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEvents]); + const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; + const eventStrings = event.map(e => `${e.section}.${e.method}`); + + expect(eventStrings).to.contain('common.ItemCreated'); + }); + itEth('Fake token creation on ethereum side', async ({helper}) => { + const collection = await helper.nft.mintCollection(superuser); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const caller = await helper.eth.createAccountWithBalance(superuser); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + { + const txInsertEthLogs = helper.constructApiCall('api.tx.evmMigration.insertEthLogs', [[ + { + // Contract, which has emitted this log + address: collectionAddress, + + topics: [ + // First topic - event signature + helper.getWeb3().eth.abi.encodeEventSignature('Transfer(address,address,uint256)'), + // Rest of topics - indexed event fields in definition order + helper.getWeb3().eth.abi.encodeParameter('address', '0x' + '00'.repeat(20)), + helper.getWeb3().eth.abi.encodeParameter('address', caller), + helper.getWeb3().eth.abi.encodeParameter('uint256', 9999), + ], + + // Every field coming from event, which is not marked as indexed, should be encoded here + // NFT transfer has no such fields, but here is an example for some other possible event: + // data: helper.getWeb3().eth.abi.encodeParameters(['uint256', 'address'], [22, collectionAddress]) + data: [], + }, + ]]); + await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEthLogs]); + } + + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x' + '00'.repeat(20)); + expect(event.returnValues.to).to.be.equal(caller); + expect(event.returnValues.tokenId).to.be.equal('9999'); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/nativeFungible.test.ts @@ -0,0 +1,173 @@ +// Copyright 2019-2023 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import {UniqueHelper} from '@unique/playgrounds/unique.js'; + +describe('NativeFungible: ERC20 calls', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('approve()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + await expect(contract.methods.approve(spender, 100).call({from: owner})).to.be.rejectedWith('approve not supported'); + }); + + itEth('balanceOf()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor, 123n); + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + const balance = await contract.methods.balanceOf(owner).call({from: owner}); + expect(balance).to.be.eq('123000000000000000000'); + }); + + itEth('decimals()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + const realDecimals = (await helper.chain.getChainProperties().tokenDecimals)[0].toString(); + const decimals = await contract.methods.decimals().call({from: owner}); + expect(decimals).to.be.eq(realDecimals); + }); + + itEth('name()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + const realName = await UniqueHelper.detectNetwork(helper.getApi()); + const name = await contract.methods.name().call({from: owner}); + expect(name).to.be.eq(realName); + }); + + itEth('symbol()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + const realName = (await helper.chain.getChainProperties().tokenSymbol)[0]; + const name = await contract.methods.symbol().call({from: owner}); + expect(name).to.be.eq(realName); + }); + + itEth('totalSupply()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + const totalSupplyEth = BigInt(await contract.methods.totalSupply().call({from: owner})); + const totalSupplySub = await helper.balance.getTotalIssuance(); + expect(totalSupplyEth).to.be.eq(totalSupplySub); + }); + + itEth('transfer()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + const balanceOwnerBefore = await helper.balance.getEthereum(owner); + const balanceReceiverBefore = await helper.balance.getEthereum(receiver); + + await contract.methods.transfer(receiver, 50).send({from: owner}); + + const balanceOwnerAfter = await helper.balance.getEthereum(owner); + const balanceReceiverAfter = await helper.balance.getEthereum(receiver); + + expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true; + expect(balanceReceiverBefore + 50n).to.be.equal(balanceReceiverAfter); + }); + + itEth('transferFrom()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + const balanceOwnerBefore = await helper.balance.getEthereum(owner); + const balanceReceiverBefore = await helper.balance.getEthereum(receiver); + + await contract.methods.transferFrom(owner, receiver, 50).send({from: owner}); + + const balanceOwnerAfter = await helper.balance.getEthereum(owner); + const balanceReceiverAfter = await helper.balance.getEthereum(receiver); + + expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true; + expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true; + + await expect(contract.methods.transferFrom(receiver, receiver, 50).call({from: owner})).to.be.rejectedWith('ApprovedValueTooLow'); + }); +}); + +describe('NativeFungible: ERC20UniqueExtensions calls', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('transferCross()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + const balanceOwnerBefore = await helper.balance.getEthereum(owner); + const balanceReceiverBefore = await helper.balance.getEthereum(receiver.eth); + + await contract.methods.transferCross(receiver, 50).send({from: owner}); + + const balanceOwnerAfter = await helper.balance.getEthereum(owner); + const balanceReceiverAfter = await helper.balance.getEthereum(receiver.eth); + + expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true; + expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true; + }); + + itEth('transferFromCross()', async ({helper}) => { + const owner = await helper.ethCrossAccount.createAccountWithBalance(donor); + const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth); + + const balanceOwnerBefore = await helper.balance.getEthereum(owner.eth); + const balanceReceiverBefore = await helper.balance.getEthereum(receiver.eth); + + await contract.methods.transferFromCross(owner, receiver, 50).send({from: owner.eth}); + + const balanceOwnerAfter = await helper.balance.getEthereum(owner.eth); + const balanceReceiverAfter = await helper.balance.getEthereum(receiver.eth); + + expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true; + expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true; + + await expect(contract.methods.transferFromCross(receiver, receiver, 50).call({from: owner.eth})).to.be.rejectedWith('no permission'); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/nativeRpc/estimateGas.test.ts @@ -0,0 +1,62 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {expect, itEth, usingEthPlaygrounds} from '../util/index.js'; +import type {IKeyringPair} from '@polkadot/types/types'; + + +describe('Ethereum native RPC calls', () => { + let donor: IKeyringPair; + const NATIVE_TOKEN_ADDRESS = '0x17c4e6453cc49aaaaeaca894e6d9683e00000000'; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('estimate gas', async ({helper}) => { + const BALANCE = 100n; + const BALANCE_TO_TRANSFER = 90n; + + const owner = await helper.eth.createAccountWithBalance(donor, BALANCE); + const recepient = helper.eth.createAccount(); + + const web3 = helper.getWeb3(); + // data: transfer(recepient, 90); + const data = web3.eth.abi.encodeFunctionCall({ + name: 'transfer', + type: 'function', + inputs: [{ + type: 'address', + name: 'to', + },{ + type: 'uint256', + name: 'amount', + }], + }, [recepient, (BALANCE_TO_TRANSFER * (10n ** 18n)).toString()]); + + const estimateGas = await web3.eth.estimateGas({ + to: NATIVE_TOKEN_ADDRESS, + value: '0x0', + data, + from: owner, + maxFeePerGas: '0x14c9338c61d', + }); + + expect(estimateGas).to.be.greaterThan(35000).and.to.be.lessThan(50000); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/nesting/nest.test.ts @@ -0,0 +1,289 @@ +import type {IKeyringPair} from '@polkadot/types/types'; +import {Contract} from 'web3-eth-contract'; + +import {itEth, usingEthPlaygrounds, expect} from '../util/index.js'; +import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js'; + +const createNestingCollection = async ( + helper: EthUniqueHelper, + owner: string, + mergeDeprecated = false, +): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => { + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, mergeDeprecated); + await contract.methods.setCollectionNesting([true, false, []]).send({from: owner}); + + return {collectionId, collectionAddress, contract}; +}; + + +describe('EVM nesting tests group', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + describe('Integration Test: EVM Nesting', () => { + itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId, contract} = await createNestingCollection(helper, owner); + + // Create a token to be nested to + const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner}); + const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId; + const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId); + + // Create a nested token + const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner}); + const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId; + expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress); + + // Create a token to be nested and nest + const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner}); + const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId; + + await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner}); + expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress); + + // Unnest token back + await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner}); + expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner); + }); + + itEth('NFT: collectionNesting()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress: unnestedCollectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + const unnestedContract = await helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner); + expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]); + + const {contract} = await createNestingCollection(helper, owner); + expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, []]); + await contract.methods.setCollectionNesting([true, false, [unnestedCollectionAddress]]).send({from: owner}); + expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress]]); + await contract.methods.setCollectionNesting([false, true, [unnestedCollectionAddress]]).send({from: owner}); + expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, true, [unnestedCollectionAddress]]); + await contract.methods.setCollectionNesting([false, false, []]).send({from: owner}); + expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]); + }); + + // Sof-deprecated + itEth('NFT: collectionNestingRestrictedCollectionIds() & collectionNestingPermissions', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId: unnestedCollsectionId, collectionAddress: unnsetedCollectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + const unnestedContract = await helper.ethNativeContract.collection(unnsetedCollectionAddress, 'nft', owner, true); + expect(await unnestedContract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([false, []]); + + const {contract} = await createNestingCollection(helper, owner, true); + expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, []]); + await contract.methods['setCollectionNesting(bool,address[])'](true, [unnsetedCollectionAddress]).send({from: owner}); + expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, [unnestedCollsectionId.toString()]]); + expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['1', false], ['0', true]]); + await contract.methods['setCollectionNesting(bool)'](false).send({from: owner}); + expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['1', false], ['0', false]]); + }); + + itEth('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner); + const {contract: contractB} = await createNestingCollection(helper, owner); + await contractA.methods.setCollectionNesting([true, false, [contractA.options.address, contractB.options.address]]).send({from: owner}); + + // Create a token to nest into + const mintingtargetNftTokenIdResult = await contractA.methods.mint(owner).send({from: owner}); + const targetNftTokenId = mintingtargetNftTokenIdResult.events.Transfer.returnValues.tokenId; + const nftTokenAddressA1 = helper.ethAddress.fromTokenId(collectionIdA, targetNftTokenId); + + // Create a token for nesting in the same collection as the target + const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner}); + const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId; + + // Create a token for nesting in a different collection + const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner}); + const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId; + + // Nest + await contractA.methods.transfer(nftTokenAddressA1, nftTokenIdA).send({from: owner}); + expect(await contractA.methods.ownerOf(nftTokenIdA).call()).to.be.equal(nftTokenAddressA1); + + await contractB.methods.transfer(nftTokenAddressA1, nftTokenIdB).send({from: owner}); + expect(await contractB.methods.ownerOf(nftTokenIdB).call()).to.be.equal(nftTokenAddressA1); + }); + }); + + describe('Negative Test: EVM Nesting', () => { + itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionId, contract} = await createNestingCollection(helper, owner); + await contract.methods.setCollectionNesting([false, false, []]).send({from: owner}); + + // Create a token to nest into + const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner}); + const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; + const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId); + + // Create a token to nest + const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner}); + const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId; + + // Try to nest + await expect(contract.methods + .transfer(targetNftTokenAddress, nftTokenId) + .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest'); + }); + + itEth('NFT: disallows a non-Owner to nest someone else\'s token', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const malignant = await helper.eth.createAccountWithBalance(donor); + + const {collectionId, contract} = await createNestingCollection(helper, owner); + + // Mint a token + const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner}); + const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; + const targetTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId); + + // Mint a token belonging to a different account + const mintingTokenIdResult = await contract.methods.mint(malignant).send({from: owner}); + const tokenId = mintingTokenIdResult.events.Transfer.returnValues.tokenId; + + // Try to nest one token in another as a non-owner account + await expect(contract.methods + .transfer(targetTokenAddress, tokenId) + .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest'); + }); + + itEth('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const malignant = await helper.eth.createAccountWithBalance(donor); + + const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner); + const {contract: contractB} = await createNestingCollection(helper, owner); + + await contractA.methods.setCollectionNesting([true, false, [contractA.options.address, contractB.options.address]]).send({from: owner}); + + // Create a token in one collection + const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner}); + const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId; + const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA); + + // Create a token in another collection + const mintingTokenIdBResult = await contractB.methods.mint(malignant).send({from: owner}); + const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId; + + // Try to drag someone else's token into the other collection and nest + await expect(contractB.methods + .transfer(nftTokenAddressA, nftTokenIdB) + .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest'); + }); + + itEth('NFT: disallows to nest token in an unlisted collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner); + const {contract: contractB} = await createNestingCollection(helper, owner); + + await contractA.methods.setCollectionNesting([true, false, [contractA.options.address]]).send({from: owner}); + + // Create a token in one collection + const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner}); + const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId; + const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA); + + // Create a token in another collection + const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner}); + const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId; + + + // Try to nest into a token in the other collection, disallowed in the first + await expect(contractB.methods + .transfer(nftTokenAddressA, nftTokenIdB) + .call()).to.be.rejectedWith('SourceCollectionIsNotAllowedToNest'); + }); + }); + + describe('Fungible', () => { + async function createFungibleCollection(helper: EthUniqueHelper, owner: string, mode: 'ft' | 'native ft') { + if(mode === 'ft') { + const {collectionAddress} = await helper.eth.createFungibleCollection(owner, '', 18, '', ''); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + await contract.methods.mint(owner, 100n).send({from: owner}); + return {collectionAddress, contract}; + } + + // native ft + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + return {collectionAddress, contract}; + } + + [ + {mode: 'ft' as const}, + {mode: 'native ft' as const}, + ].map(testCase => { + itEth(`Allow nest [${testCase.mode}]`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner); + const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode); + + const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner}); + const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; + const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId); + + await ftContract.methods.transfer(targetTokenAddress, 10n).send({from: owner}); + expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('10'); + }); + }); + + [ + {mode: 'ft' as const}, + {mode: 'native ft' as const}, + ].map(testCase => { + itEth(`Allow partial/full unnest [${testCase.mode}]`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner); + const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode); + + const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner}); + const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; + const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId); + + await ftContract.methods.transfer(targetTokenAddress, 10n).send({from: owner}); + + await ftContract.methods.transferFrom(targetTokenAddress, owner, 5n).send({from: owner}); + expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('5'); + + await ftContract.methods.transferFrom(targetTokenAddress, owner, 5n).send({from: owner}); + expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('0'); + }); + }); + + [ + {mode: 'ft' as const}, + {mode: 'native ft' as const}, + ].map(testCase => { + itEth(`Disallow nest into collection without nesting permission [${testCase.mode}] (except for native fungible collection)`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner); + await targetContract.methods.setCollectionNesting([false, false, []]).send({from: owner}); + + const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode); + + const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner}); + const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; + const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId); + + if(testCase.mode === 'ft') { + await expect(ftContract.methods.transfer(targetTokenAddress, 10n).call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest'); + } else { + await expect(ftContract.methods.transfer(targetTokenAddress, 10n).call({from: owner})).to.be.not.rejected; + } + }); + }); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/nonFungible.test.ts @@ -0,0 +1,1127 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; +import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {Contract} from 'web3-eth-contract'; +import type {ITokenPropertyPermission} from '@unique/playgrounds/types.js'; +import {CREATE_COLLECTION_DATA_DEFAULTS, TokenPermissionField} from './util/playgrounds/types.js'; + +describe('Check ERC721 token URI for NFT', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', baseUri); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + const result = await contract.methods.mint(receiver).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + expect(tokenId).to.be.equal('1'); + + if(propertyKey && propertyValue) { + // Set URL or suffix + await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send(); + } + + const event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.tokenId).to.be.equal(tokenId); + + return {contract, nextTokenId: tokenId}; + } + + itEth('Empty tokenURI', async ({helper}) => { + const {contract, nextTokenId} = await setup(helper, ''); + expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal(''); + }); + + itEth('TokenURI from url', async ({helper}) => { + const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI'); + expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI'); + }); + + itEth('TokenURI from baseURI', async ({helper}) => { + const {contract, nextTokenId} = await setup(helper, 'BaseURI_'); + expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_'); + }); + + itEth('TokenURI from baseURI + suffix', async ({helper}) => { + const suffix = '/some/suffix'; + const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix); + expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix); + }); +}); + +describe('NFT: Plain calls', () => { + let donor: IKeyringPair; + let minter: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + // TODO combine all minting tests in one place + [ + 'substrate' as const, + 'ethereum' as const, + ].map(testCase => { + itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => { + const collectionAdmin = await helper.eth.createAccountWithBalance(donor); + + const receiverEth = helper.eth.createAccount(); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const receiverSub = bob; + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub); + + // const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob); + const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)})); + const permissions: ITokenPropertyPermission[] = properties + .map(p => ({ + key: p.key, permission: { + tokenOwner: false, + collectionAdmin: true, + mutable: false, + }, + })); + + const collection = await helper.nft.mintCollection(minter, { + tokenPrefix: 'ethp', + tokenPropertyPermissions: permissions, + }); + await collection.addAdmin(minter, {Ethereum: collectionAdmin}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', collectionAdmin, true); + let expectedTokenId = await contract.methods.nextTokenId().call(); + let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send(); + let tokenId = result.events.Transfer.returnValues.tokenId; + expect(tokenId).to.be.equal(expectedTokenId); + + let event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address)); + expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); + + expectedTokenId = await contract.methods.nextTokenId().call(); + result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send(); + event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address)); + expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); + + tokenId = result.events.Transfer.returnValues.tokenId; + + expect(tokenId).to.be.equal(expectedTokenId); + + expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties + .map(p => helper.ethProperty.property(p.key, p.value.toString()))); + + expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)) + .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address}); + }); + }); + + itEth('Non-owner and non admin cannot mintCross', async ({helper}) => { + const nonOwner = await helper.eth.createAccountWithBalance(donor); + const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner); + + const collection = await helper.nft.mintCollection(minter); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft'); + + await expect(collectionEvm.methods.mintCross(nonOwnerCross, []).call({from: nonOwner})) + .to.be.rejectedWith('PublicMintingNotAllowed'); + }); + + //TODO: CORE-302 add eth methods + itEth.skip('Can perform mintBulk()', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const collection = await helper.nft.mintCollection(minter); + await collection.addAdmin(minter, {Ethereum: caller}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); + { + const bulkSize = 3; + const nextTokenId = await contract.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + const result = await contract.methods.mintBulkWithTokenURI( + receiver, + Array.from({length: bulkSize}, (_, i) => ( + [+nextTokenId + i, `Test URI ${i}`] + )), + ).send({from: caller}); + + const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId); + for(let i = 0; i < bulkSize; i++) { + const event = events[i]; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal(receiver); + expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`); + + expect(await contract.methods.tokenURI(+nextTokenId + i).call()).to.be.equal(`Test URI ${i}`); + } + } + }); + + itEth('Can perform mintBulkCross()', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const callerCross = helper.ethCrossAccount.fromAddress(caller); + const receiver = helper.eth.createAccount(); + const receiverCross = helper.ethCrossAccount.fromAddress(receiver); + + const permissions = [ + {code: TokenPermissionField.Mutable, value: true}, + {code: TokenPermissionField.TokenOwner, value: true}, + {code: TokenPermissionField.CollectionAdmin, value: true}, + ]; + const {collectionAddress} = await helper.eth.createCollection( + caller, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: 'nft', + adminList: [callerCross], + tokenPropertyPermissions: [ + {key: 'key_0_0', permissions}, + {key: 'key_1_0', permissions}, + {key: 'key_1_1', permissions}, + {key: 'key_2_0', permissions}, + {key: 'key_2_1', permissions}, + {key: 'key_2_2', permissions}, + ], + }, + ).send(); + + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); + { + const nextTokenId = await contract.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + const result = await contract.methods.mintBulkCross([ + { + owner: receiverCross, + properties: [ + {key: 'key_0_0', value: Buffer.from('value_0_0')}, + ], + }, + { + owner: receiverCross, + properties: [ + {key: 'key_1_0', value: Buffer.from('value_1_0')}, + {key: 'key_1_1', value: Buffer.from('value_1_1')}, + ], + }, + { + owner: receiverCross, + properties: [ + {key: 'key_2_0', value: Buffer.from('value_2_0')}, + {key: 'key_2_1', value: Buffer.from('value_2_1')}, + {key: 'key_2_2', value: Buffer.from('value_2_2')}, + ], + }, + ]).send({from: caller}); + const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId); + const bulkSize = 3; + for(let i = 0; i < bulkSize; i++) { + const event = events[i]; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal(receiver); + expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`); + } + + const properties = [ + await contract.methods.properties(+nextTokenId, []).call(), + await contract.methods.properties(+nextTokenId + 1, []).call(), + await contract.methods.properties(+nextTokenId + 2, []).call(), + ]; + expect(properties).to.be.deep.equal([ + [ + ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')], + ], + [ + ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')], + ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')], + ], + [ + ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')], + ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')], + ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')], + ], + ]); + } + }); + + itEth('Can perform burn()', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + + const collection = await helper.nft.mintCollection(minter, {}); + const {tokenId} = await collection.mintToken(minter, {Ethereum: caller}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); + + { + const result = await contract.methods.burn(tokenId).send({from: caller}); + + const event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(caller); + expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); + } + }); + + itEth('Can perform approve()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + + const collection = await helper.nft.mintCollection(minter, {}); + const {tokenId} = await collection.mintToken(minter, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + { + const badTokenId = await contract.methods.nextTokenId().call() + 1; + await expect(contract.methods.getApproved(badTokenId).call()).to.be.rejectedWith('revert TokenNotFound'); + } + { + const approved = await contract.methods.getApproved(tokenId).call(); + expect(approved).to.be.equal('0x0000000000000000000000000000000000000000'); + } + { + const result = await contract.methods.approve(spender, tokenId).send({from: owner}); + + const event = result.events.Approval; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.owner).to.be.equal(owner); + expect(event.returnValues.approved).to.be.equal(spender); + expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); + } + { + const approved = await contract.methods.getApproved(tokenId).call(); + expect(approved).to.be.equal(spender); + } + }); + + itEth('Can perform setApprovalForAll()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const operator = helper.eth.createAccount(); + + const collection = await helper.nft.mintCollection(minter, {}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call(); + expect(approvedBefore).to.be.equal(false); + + { + const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner}); + + expect(result.events.ApprovalForAll).to.be.like({ + address: collectionAddress, + event: 'ApprovalForAll', + returnValues: { + owner, + operator, + approved: true, + }, + }); + + const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call(); + expect(approvedAfter).to.be.equal(true); + } + + { + const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner}); + + expect(result.events.ApprovalForAll).to.be.like({ + address: collectionAddress, + event: 'ApprovalForAll', + returnValues: { + owner, + operator, + approved: false, + }, + }); + + const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call(); + expect(approvedAfter).to.be.equal(false); + } + }); + + itEth('Can perform burn with ApprovalForAll', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const owner = await helper.eth.createAccountWithBalance(donor); + const operator = await helper.eth.createAccountWithBalance(donor); + + const token = await collection.mintToken(minter, {Ethereum: owner}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft'); + + { + await contract.methods.setApprovalForAll(operator, true).send({from: owner}); + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator}); + const events = result.events.Transfer; + + expect(events).to.be.like({ + address, + event: 'Transfer', + returnValues: { + from: owner, + to: '0x0000000000000000000000000000000000000000', + tokenId: token.tokenId.toString(), + }, + }); + } + + expect(await helper.nft.doesTokenExist(collection.collectionId, token.tokenId)).to.be.false; + }); + + itEth('Can perform transfer with ApprovalForAll', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const owner = await helper.eth.createAccountWithBalance(donor); + const operator = await helper.eth.createAccountWithBalance(donor); + const receiver = charlie; + + const token = await collection.mintToken(minter, {Ethereum: owner}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft'); + + { + await contract.methods.setApprovalForAll(operator, true).send({from: owner}); + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); + const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator}); + const event = result.events.Transfer; + expect(event).to.be.like({ + address: helper.ethAddress.fromCollectionId(collection.collectionId), + event: 'Transfer', + returnValues: { + from: owner, + to: helper.address.substrateToEth(receiver.address), + tokenId: token.tokenId.toString(), + }, + }); + } + + expect(await token.getOwner()).to.be.like({Substrate: receiver.address}); + }); + + itEth('Can perform burnFromCross()', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const ownerSub = bob; + const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub); + const ownerEth = await helper.eth.createAccountWithBalance(donor); + const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth); + + const burnerEth = await helper.eth.createAccountWithBalance(donor); + const burnerCrossEth = helper.ethCrossAccount.fromAddress(burnerEth); + + const token1 = await collection.mintToken(minter, {Substrate: ownerSub.address}); + const token2 = await collection.mintToken(minter, {Ethereum: ownerEth}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft'); + + // Approve tokens from substrate and ethereum: + await token1.approve(ownerSub, {Ethereum: burnerEth}); + await collectionEvm.methods.approveCross(burnerCrossEth, token2.tokenId).send({from: ownerEth}); + + // can burnFromCross: + const result1 = await collectionEvm.methods.burnFromCross(ownerCrossSub, token1.tokenId).send({from: burnerEth}); + const result2 = await collectionEvm.methods.burnFromCross(ownerCrossEth, token2.tokenId).send({from: burnerEth}); + const events1 = result1.events.Transfer; + const events2 = result2.events.Transfer; + + // Check events for burnFromCross (substrate and ethereum): + [ + [events1, token1, helper.address.substrateToEth(ownerSub.address)], + [events2, token2, ownerEth], + ].map(burnData => { + expect(burnData[0]).to.be.like({ + address: collectionAddress, + event: 'Transfer', + returnValues: { + from: burnData[2], + to: '0x0000000000000000000000000000000000000000', + tokenId: burnData[1].tokenId.toString(), + }, + }); + }); + + expect(await token1.doesExist()).to.be.false; + expect(await token2.doesExist()).to.be.false; + }); + + // TODO combine all approve tests in one place + itEth('Can perform approveCross()', async ({helper}) => { + // arrange: create accounts + const owner = await helper.eth.createAccountWithBalance(donor); + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const receiverSub = charlie; + const recieverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub); + const receiverEth = await helper.eth.createAccountWithBalance(donor); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + + // arrange: create collection and tokens: + const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const token1 = await collection.mintToken(minter, {Ethereum: owner}); + const token2 = await collection.mintToken(minter, {Ethereum: owner}); + + const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); + + // Can approveCross substrate and ethereum address: + const resultSub = await collectionEvm.methods.approveCross(recieverCrossSub, token1.tokenId).send({from: owner}); + const resultEth = await collectionEvm.methods.approveCross(receiverCrossEth, token2.tokenId).send({from: owner}); + const eventSub = resultSub.events.Approval; + const eventEth = resultEth.events.Approval; + expect(eventSub).to.be.like({ + address: helper.ethAddress.fromCollectionId(collection.collectionId), + event: 'Approval', + returnValues: { + owner, + approved: helper.address.substrateToEth(receiverSub.address), + tokenId: token1.tokenId.toString(), + }, + }); + expect(eventEth).to.be.like({ + address: helper.ethAddress.fromCollectionId(collection.collectionId), + event: 'Approval', + returnValues: { + owner, + approved: receiverEth, + tokenId: token2.tokenId.toString(), + }, + }); + + // Substrate address can transferFrom approved tokens: + await helper.nft.transferTokenFrom(receiverSub, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiverSub.address}); + expect(await helper.nft.getTokenOwner(collection.collectionId, token1.tokenId)).to.deep.eq({Substrate: receiverSub.address}); + // Ethereum address can transferFromCross approved tokens: + await collectionEvm.methods.transferFromCross(ownerCross, receiverCrossEth, token2.tokenId).send({from: receiverEth}); + expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()}); + }); + + itEth('Non-owner and non admin cannot approveCross', async ({helper}) => { + const nonOwner = await helper.eth.createAccountWithBalance(donor); + const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner); + const owner = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); + const token = await collection.mintToken(minter, {Ethereum: owner}); + + await expect(collectionEvm.methods.approveCross(nonOwnerCross, token.tokenId).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned'); + }); + + itEth('Can reaffirm approved address', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner); + const [receiver1, receiver2] = await helper.arrange.createAccounts([100n, 100n], donor); + const receiver1Cross = helper.ethCrossAccount.fromKeyringPair(receiver1); + const receiver2Cross = helper.ethCrossAccount.fromKeyringPair(receiver2); + const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const token1 = await collection.mintToken(minter, {Ethereum: owner}); + const token2 = await collection.mintToken(minter, {Ethereum: owner}); + const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); + + // Can approve and reaffirm approved address: + await collectionEvm.methods.approveCross(receiver1Cross, token1.tokenId).send({from: owner}); + await collectionEvm.methods.approveCross(receiver2Cross, token1.tokenId).send({from: owner}); + + // receiver1 cannot transferFrom: + await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected; + // receiver2 can transferFrom: + await helper.nft.transferTokenFrom(receiver2, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver2.address}); + + // can set approved address to self address to remove approval: + await collectionEvm.methods.approveCross(receiver1Cross, token2.tokenId).send({from: owner}); + await collectionEvm.methods.approveCross(ownerCrossEth, token2.tokenId).send({from: owner}); + + // receiver1 cannot transfer token anymore: + await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token2.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected; + }); + + itEth('Can perform transferFrom()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const collection = await helper.nft.mintCollection(minter, {}); + const {tokenId} = await collection.mintToken(minter, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + await contract.methods.approve(spender, tokenId).send({from: owner}); + + { + const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: spender}); + + const event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(owner); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(1); + } + + { + const balance = await contract.methods.balanceOf(owner).call(); + expect(+balance).to.equal(0); + } + }); + + itEth('Can perform transferFromCross()', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const [owner, receiver] = await helper.arrange.createAccounts([100n, 100n], donor); + const spender = await helper.eth.createAccountWithBalance(donor); + + const token = await collection.mintToken(minter, {Substrate: owner.address}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft'); + + await token.approve(owner, {Ethereum: spender}); + + { + const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner); + const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); + const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender}); + const event = result.events.Transfer; + expect(event).to.be.like({ + address: helper.ethAddress.fromCollectionId(collection.collectionId), + event: 'Transfer', + returnValues: { + from: helper.address.substrateToEth(owner.address), + to: helper.address.substrateToEth(receiver.address), + tokenId: token.tokenId.toString(), + }, + }); + } + + expect(await token.getOwner()).to.be.like({Substrate: receiver.address}); + }); + + itEth('Can perform transfer()', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {}); + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const {tokenId} = await collection.mintToken(minter, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + { + const result = await contract.methods.transfer(receiver, tokenId).send({from: owner}); + + const event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(owner); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); + } + + { + const balance = await contract.methods.balanceOf(owner).call(); + expect(+balance).to.equal(0); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(1); + } + }); + + itEth('Can perform transferCross()', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {}); + const owner = await helper.eth.createAccountWithBalance(donor); + const receiverEth = await helper.eth.createAccountWithBalance(donor); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter); + + const {tokenId} = await collection.mintToken(minter, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + { + // Can transferCross to ethereum address: + const result = await collectionEvm.methods.transferCross(receiverCrossEth, tokenId).send({from: owner}); + // Check events: + const event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(owner); + expect(event.returnValues.to).to.be.equal(receiverEth); + expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); + + // owner has balance = 0: + const ownerBalance = await collectionEvm.methods.balanceOf(owner).call(); + expect(+ownerBalance).to.equal(0); + // receiver owns token: + const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); + expect(+receiverBalance).to.equal(1); + expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()}); + } + + { + // Can transferCross to substrate address: + const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, tokenId).send({from: receiverEth}); + // Check events: + const event = substrateResult.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(receiverEth); + expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address)); + expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); + + // owner has balance = 0: + const ownerBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); + expect(+ownerBalance).to.equal(0); + // receiver owns token: + const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address}); + expect(receiverBalance).to.contain(tokenId); + } + }); + + ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => { + const sender = await helper.eth.createAccountWithBalance(donor); + const tokenOwner = await helper.eth.createAccountWithBalance(donor); + const receiverSub = minter; + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter); + + const collection = await helper.nft.mintCollection(minter, {}); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', sender); + + await collection.mintToken(minter, {Ethereum: sender}); + const nonSendersToken = await collection.mintToken(minter, {Ethereum: tokenOwner}); + + // Cannot transferCross someone else's token: + const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub; + await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected; + // Cannot transfer token if it does not exist: + await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected; + })); + + itEth('Check balanceOfCross()', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {}); + const owner = await helper.ethCrossAccount.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth); + + expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0'); + + for(let i = 1; i < 10; i++) { + await collection.mintToken(minter, {Ethereum: owner.eth}); + expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString()); + } + }); + + itEth('Check ownerOfCross()', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {}); + let owner = await helper.ethCrossAccount.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth); + const {tokenId} = await collection.mintToken(minter, {Ethereum: owner.eth}); + + for(let i = 1n; i < 10n; i++) { + const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth}); + expect(ownerCross.eth).to.be.eq(owner.eth); + expect(ownerCross.sub).to.be.eq(owner.sub); + + const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor); + await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth}); + owner = newOwner; + } + }); +}); + +describe('NFT: Fees', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + }); + }); + + itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + + const collection = await helper.nft.mintCollection(alice, {}); + const {tokenId} = await collection.mintToken(alice, {Ethereum: owner}); + + const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner); + + const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, tokenId).send({from: owner})); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + }); + + itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = await helper.eth.createAccountWithBalance(donor); + + const collection = await helper.nft.mintCollection(alice, {}); + const {tokenId} = await collection.mintToken(alice, {Ethereum: owner}); + + const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner); + + await contract.methods.approve(spender, tokenId).send({from: owner}); + + const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender})); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + }); + + itEth('Can perform transferFromCross()', async ({helper}) => { + const collectionMinter = alice; + const owner = bob; + const receiver = charlie; + const collection = await helper.nft.mintCollection(collectionMinter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const spender = await helper.eth.createAccountWithBalance(donor); + + const token = await collection.mintToken(collectionMinter, {Substrate: owner.address}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft'); + + await token.approve(owner, {Ethereum: spender}); + + { + const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner); + const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); + const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender}); + const event = result.events.Transfer; + expect(event).to.be.like({ + address: helper.ethAddress.fromCollectionId(collection.collectionId), + event: 'Transfer', + returnValues: { + from: helper.address.substrateToEth(owner.address), + to: helper.address.substrateToEth(receiver.address), + tokenId: token.tokenId.toString(), + }, + }); + } + + expect(await token.getOwner()).to.be.like({Substrate: receiver.address}); + }); + + itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const collection = await helper.nft.mintCollection(alice, {}); + const {tokenId} = await collection.mintToken(alice, {Ethereum: owner}); + + const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner); + + const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner})); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + }); +}); + +describe('NFT: Substrate calls', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([20n], donor); + }); + }); + + itEth('Events emitted for mint()', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft'); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + const {tokenId} = await collection.mintToken(alice); + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.event).to.be.equal('Transfer'); + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.tokenId).to.be.equal(tokenId.toString()); + }); + + itEth('Events emitted for burn()', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + const token = await collection.mintToken(alice); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft'); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + await token.burn(alice); + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.event).to.be.equal('Transfer'); + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString()); + }); + + itEth('Events emitted for approve()', async ({helper}) => { + const receiver = helper.eth.createAccount(); + + const collection = await helper.nft.mintCollection(alice, {}); + const token = await collection.mintToken(alice); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft'); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + await token.approve(alice, {Ethereum: receiver}); + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.event).to.be.equal('Approval'); + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.approved).to.be.equal(receiver); + expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString()); + }); + + itEth('Events emitted for transferFrom()', async ({helper}) => { + const [bob] = await helper.arrange.createAccounts([10n], donor); + const receiver = helper.eth.createAccount(); + + const collection = await helper.nft.mintCollection(alice, {}); + const token = await collection.mintToken(alice); + await token.approve(alice, {Substrate: bob.address}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft'); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}); + + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`); + }); + + itEth('Events emitted for transfer()', async ({helper}) => { + const receiver = helper.eth.createAccount(); + + const collection = await helper.nft.mintCollection(alice, {}); + const token = await collection.mintToken(alice); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft'); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + await token.transfer(alice, {Ethereum: receiver}); + + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`); + }); +}); + +describe('Common metadata', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([20n], donor); + }); + }); + + itEth('Returns collection name', async ({helper}) => { + const caller = helper.eth.createAccount(); + const tokenPropertyPermissions = [{ + key: 'URI', + permission: { + mutable: true, + collectionAdmin: true, + tokenOwner: false, + }, + }]; + const collection = await helper.nft.mintCollection( + alice, + { + name: 'oh River', + tokenPrefix: 'CHANGE', + properties: [{key: 'ERC721Metadata', value: '1'}], + tokenPropertyPermissions, + }, + ); + + const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller); + const name = await contract.methods.name().call(); + expect(name).to.equal('oh River'); + }); + + itEth('Returns symbol name', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const tokenPropertyPermissions = [{ + key: 'URI', + permission: { + mutable: true, + collectionAdmin: true, + tokenOwner: false, + }, + }]; + const collection = await helper.nft.mintCollection( + alice, + { + name: 'oh River', + tokenPrefix: 'CHANGE', + properties: [{key: 'ERC721Metadata', value: '1'}], + tokenPropertyPermissions, + }, + ); + + const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller); + const symbol = await contract.methods.symbol().call(); + expect(symbol).to.equal('CHANGE'); + }); +}); + +describe('Negative tests', () => { + let donor: IKeyringPair; + let minter: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itEth('[negative] Cant perform burn without approval', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = await helper.eth.createAccountWithBalance(donor); + + const token = await collection.mintToken(minter, {Ethereum: owner}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft'); + + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected; + + await contract.methods.setApprovalForAll(spender, true).send({from: owner}); + await contract.methods.setApprovalForAll(spender, false).send({from: owner}); + + await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected; + }); + + itEth('[negative] Cant perform transfer without approval', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const receiver = alice; + + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = await helper.eth.createAccountWithBalance(donor); + + const token = await collection.mintToken(minter, {Ethereum: owner}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft'); + + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); + + await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected; + + await contract.methods.setApprovalForAll(spender, true).send({from: owner}); + await contract.methods.setApprovalForAll(spender, false).send({from: owner}); + + await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected; + }); +}); --- /dev/null +++ b/js-packages/tests/eth/payable.test.ts @@ -0,0 +1,273 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; + +import {itEth, expect, usingEthPlaygrounds} from './util/index.js'; +import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; +import {makeNames} from '../util/index.js'; + +const {dirname} = makeNames(import.meta.url); + +describe('EVM payable contracts', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Evm contract can receive wei from eth account', async ({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const contract = await helper.eth.deployCollectorContract(deployer); + + const web3 = helper.getWeb3(); + + await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', gas: helper.eth.DEFAULT_GAS}); + + expect(await contract.methods.getCollected().call()).to.be.equal('10000'); + }); + + itEth('Evm contract can receive wei from substrate account', async ({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const contract = await helper.eth.deployCollectorContract(deployer); + const [alice] = await helper.arrange.createAccounts([40n], donor); + + const weiCount = '10000'; + + // Transaction fee/value will be payed from subToEth(sender) evm balance, + // which is backed by evmToAddress(subToEth(sender)) substrate balance + await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address), 5n); + + + await helper.eth.sendEVM(alice, contract.options.address, contract.methods.giveMoney().encodeABI(), weiCount); + + expect(await contract.methods.getCollected().call()).to.be.equal(weiCount); + }); + + // We can't handle sending balance to backing storage of evm balance, because evmToAddress operation is irreversible + itEth('Wei sent directly to backing storage of evm contract balance is unaccounted', async({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const contract = await helper.eth.deployCollectorContract(deployer); + const [alice] = await helper.arrange.createAccounts([10n], donor); + + const weiCount = 10_000n; + + await helper.eth.transferBalanceFromSubstrate(alice, contract.options.address, weiCount, false); + + expect(await contract.methods.getUnaccounted().call()).to.be.equal(weiCount.toString()); + }); + + itEth('Balance can be retrieved from evm contract', async({helper}) => { + const FEE_BALANCE = 10n * helper.balance.getOneTokenNominal(); + const CONTRACT_BALANCE = 1n * helper.balance.getOneTokenNominal(); + + const deployer = await helper.eth.createAccountWithBalance(donor); + const contract = await helper.eth.deployCollectorContract(deployer); + const [alice] = await helper.arrange.createAccounts([20n], donor); + + const web3 = helper.getWeb3(); + + await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS}); + + const [receiver] = await helper.arrange.createAccounts([0n], donor); + + // First receive balance on eth balance of bob + { + const ethReceiver = helper.address.substrateToEth(receiver.address); + expect(await web3.eth.getBalance(ethReceiver)).to.be.equal('0'); + await contract.methods.withdraw(ethReceiver).send({from: deployer}); + expect(await web3.eth.getBalance(ethReceiver)).to.be.equal(CONTRACT_BALANCE.toString()); + } + + // Some balance is required to pay fee for evm.withdraw call + await helper.balance.transferToSubstrate(alice, receiver.address, FEE_BALANCE); + // await transferBalanceExpectSuccess(api, alice, receiver.address, FEE_BALANCE.toString()); + + // Withdraw balance from eth to substrate + { + const initialReceiverBalance = await helper.balance.getSubstrate(receiver.address); + await helper.executeExtrinsic(receiver, 'api.tx.evm.withdraw', [helper.address.substrateToEth(receiver.address), CONTRACT_BALANCE.toString()], true); + const finalReceiverBalance = await helper.balance.getSubstrate(receiver.address); + + expect(finalReceiverBalance > initialReceiverBalance).to.be.true; + } + }); +}); + +describe('EVM transaction fees', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Fee is withdrawn from the user', async({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const contract = await helper.eth.deployFlipper(deployer); + + const initialCallerBalance = await helper.balance.getEthereum(caller); + await contract.methods.flip().send({from: caller}); + const finalCallerBalance = await helper.balance.getEthereum(caller); + expect(finalCallerBalance < initialCallerBalance).to.be.true; + }); + + itEth('Fee for nested calls is withdrawn from the user', async({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const contract = await deployProxyContract(helper, deployer); + + const initialCallerBalance = await helper.balance.getEthereum(caller); + const initialContractBalance = await helper.balance.getEthereum(contract.options.address); + await contract.methods.flip().send({from: caller}); + const finalCallerBalance = await helper.balance.getEthereum(caller); + const finalContractBalance = await helper.balance.getEthereum(contract.options.address); + expect(finalCallerBalance < initialCallerBalance).to.be.true; + expect(finalContractBalance == initialContractBalance).to.be.true; + }); + + itEth('Fee for nested calls to native methods is withdrawn from the user', async({helper}) => { + const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal(); + + const deployer = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const contract = await deployProxyContract(helper, deployer); + + const collectionAddress = (await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection; + const initialCallerBalance = await helper.balance.getEthereum(caller); + const initialContractBalance = await helper.balance.getEthereum(contract.options.address); + await contract.methods.mintNftToken(collectionAddress).send({from: caller}); + const finalCallerBalance = await helper.balance.getEthereum(caller); + const finalContractBalance = await helper.balance.getEthereum(contract.options.address); + expect(finalCallerBalance < initialCallerBalance).to.be.true; + expect(finalContractBalance == initialContractBalance).to.be.true; + }); + + itEth('Fee for nested calls to create*Collection methods is withdrawn from the user and from the contract', async({helper}) => { + const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal(); + const deployer = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const contract = await deployProxyContract(helper, deployer); + + const initialCallerBalance = await helper.balance.getEthereum(caller); + const initialContractBalance = await helper.balance.getEthereum(contract.options.address); + await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)}); + const finalCallerBalance = await helper.balance.getEthereum(caller); + const finalContractBalance = await helper.balance.getEthereum(contract.options.address); + expect(finalCallerBalance < initialCallerBalance).to.be.true; + expect(finalContractBalance == initialContractBalance).to.be.true; + }); + + itEth('Negative test: call createNFTCollection with wrong fee', async({helper}) => { + const SMALL_FEE = 1n * helper.balance.getOneTokenNominal(); + const BIG_FEE = 3n * helper.balance.getOneTokenNominal(); + const caller = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller); + + await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); + await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); + }); + + itEth('Negative test: call createRFTCollection with wrong fee', async({helper}) => { + const SMALL_FEE = 1n * helper.balance.getOneTokenNominal(); + const BIG_FEE = 3n * helper.balance.getOneTokenNominal(); + const caller = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller); + + await expect(collectionHelper.methods.createRFTCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); + await expect(collectionHelper.methods.createRFTCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); + }); + + itEth('Get collection creation fee', async({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + expect(await helper.eth.getCollectionCreationFee(deployer)).to.be.equal(String(2n * helper.balance.getOneTokenNominal())); + }); + + async function deployProxyContract(helper: EthUniqueHelper, deployer: string) { + return await helper.ethContract.deployByCode( + deployer, + 'ProxyContract', + ` + // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.6; + + import {CollectionHelpers} from "../api/CollectionHelpers.sol"; + import {UniqueNFT} from "../api/UniqueNFT.sol"; + + error Value(uint256 value); + + contract ProxyContract { + bool value = false; + address innerContract; + + event CollectionCreated(address collection); + event TokenMinted(uint256 tokenId); + + receive() external payable {} + + constructor() { + innerContract = address(new InnerContract()); + } + + function flip() public { + value = !value; + InnerContract(innerContract).flip(); + } + + function createNFTCollection() external payable { + address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F; + address nftCollection = CollectionHelpers(collectionHelpers).createNFTCollection{value: msg.value}("A", "B", "C"); + emit CollectionCreated(nftCollection); + } + + function mintNftToken(address collectionAddress) external { + UniqueNFT collection = UniqueNFT(collectionAddress); + uint256 tokenId = collection.mint(msg.sender); + emit TokenMinted(tokenId); + } + + function getValue() external view returns (bool) { + return InnerContract(innerContract).getValue(); + } + } + + contract InnerContract { + bool value = false; + function flip() external { + value = !value; + } + function getValue() external view returns (bool) { + return value; + } + } + `, + [ + { + solPath: 'api/CollectionHelpers.sol', + fsPath: `${dirname}/api/CollectionHelpers.sol`, + }, + { + solPath: 'api/UniqueNFT.sol', + fsPath: `${dirname}/api/UniqueNFT.sol`, + }, + ], + ); + } +}); --- /dev/null +++ b/js-packages/tests/eth/precompile.test.ts @@ -0,0 +1,112 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; + +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; + +describe('Precompiles', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('ecrecover is supported', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const ecrecoverCompiledСontract = await helper.ethContract.compile( + 'ECRECOVER', + ` + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.17; + + contract ECRECOVER{ + address addressTest = 0x12Cb274aAD8251C875c0bf6872b67d9983E53fDd; + bytes32 msgHash1 = 0xc51dac836bc7841a01c4b631fa620904fc8724d7f9f1d3c420f0e02adf229d50; + bytes32 msgHash2 = 0xc51dac836bc7841a01c4b631fa620904fc8724d7f9f1d3c420f0e02adf229d51; + uint8 v = 0x1b; + bytes32 r = 0x44287513919034a471a7dc2b2ed121f95984ae23b20f9637ba8dff471b6719ef; + bytes32 s = 0x7d7dc30309a3baffbfd9342b97d0e804092c0aeb5821319aa732bc09146eafb4; + + + function verifyValid() public view returns(bool) { + // Use ECRECOVER to verify address + return ecrecover(msgHash1, v, r, s) == (addressTest); + } + + function verifyInvalid() public view returns(bool) { + // Use ECRECOVER to verify address + return ecrecover(msgHash2, v, r, s) == (addressTest); + } + } + `, + ); + + const ecrecoverСontract = await helper.ethContract.deployByAbi(owner, ecrecoverCompiledСontract.abi, ecrecoverCompiledСontract.object); + expect(await ecrecoverСontract.methods.verifyValid().call({from: owner})).to.be.true; + expect(await ecrecoverСontract.methods.verifyInvalid().call({from: owner})).to.be.false; + }); + + itEth('sr25519 is supported', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sr25519CompiledСontract = await helper.ethContract.compile( + 'SR25519Contract', + ` + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.17; + + /** + * @title SR25519 signature interface. + */ + interface SR25519 { + /** + * @dev Verify signed message using SR25519 crypto. + * @return A boolean confirming whether the public key is signer for the message. + */ + function verify( + bytes32 public_key, + bytes calldata signature, + bytes calldata message + ) external view returns (bool); + } + + contract SR25519Contract{ + SR25519 public constant sr25519 = SR25519(0x0000000000000000000000000000000000005002); + + bytes32 public_key = 0x96b2f9237ed0890fbeed891ebb81b91ac0d5d5b6e3afcdbc95df1b68d9f14036; + bytes signature = hex"4a5d733d7c568f2e88abf0467fd497f87c1be3e940ed897efdf9da72eaad394ef9918cb574ee99c54485775b17a0deaf46ff7a1f10346cea39fff0e4ede97689"; + bytes message1 = hex"7372323535313920697320737570706f72746564"; + bytes message2 = hex"7372323535313920697320737570706f7274656401"; + + + function verifyValid() public view returns(bool) { + return sr25519.verify(public_key, signature, message1); + } + + function verifyInvalid() public view returns(bool) { + return sr25519.verify(public_key, signature, message2); + } + } + `, + ); + + const sr25519Сontract = await helper.ethContract.deployByAbi(owner, sr25519CompiledСontract.abi, sr25519CompiledСontract.object); + expect(await sr25519Сontract.methods.verifyValid().call({from: owner})).to.be.true; + expect(await sr25519Сontract.methods.verifyInvalid().call({from: owner})).to.be.false; + }); +}); --- /dev/null +++ b/js-packages/tests/eth/proxy/UniqueFungibleProxy.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_proxied","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"interfaceId","type":"uint32"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file --- /dev/null +++ b/js-packages/tests/eth/proxy/UniqueFungibleProxy.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5060405161098038038061098083398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b6108ed806100936000396000f3fe608060405234801561001057600080fd5b506004361061009e5760003560e01c806370a082311161006657806370a082311461012757806395d89b411461013a578063a9059cbb14610142578063dd62ed3e14610155578063f4f4b5001461016857600080fd5b806306fdde03146100a3578063095ea7b3146100c157806318160ddd146100e457806323b872dd146100fa578063313ce5671461010d575b600080fd5b6100ab61017b565b6040516100b8919061083e565b60405180910390f35b6100d46100cf3660046106e3565b610200565b60405190151581526020016100b8565b6100ec61028f565b6040519081526020016100b8565b6100d46101083660046106a7565b610316565b6101156103ad565b60405160ff90911681526020016100b8565b6100ec610135366004610659565b610434565b6100ab6104b8565b6100d46101503660046106e3565b6104fc565b6100ec610163366004610674565b610536565b6100d46101763660046107f5565b6105bc565b60008054604080516306fdde0360e01b815290516060936001600160a01b03909316926306fdde039260048082019391829003018186803b1580156101bf57600080fd5b505afa1580156101d3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526101fb919081019061072f565b905090565b6000805460405163095ea7b360e01b81526001600160a01b038581166004830152602482018590529091169063095ea7b3906044015b602060405180830381600087803b15801561025057600080fd5b505af1158015610264573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610288919061070d565b9392505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156102de57600080fd5b505afa1580156102f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fb91906107dc565b600080546040516323b872dd60e01b81526001600160a01b038681166004830152858116602483015260448201859052909116906323b872dd90606401602060405180830381600087803b15801561036d57600080fd5b505af1158015610381573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a5919061070d565b949350505050565b60008060009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156103fc57600080fd5b505afa158015610410573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fb919061081b565b600080546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a082319060240160206040518083038186803b15801561047a57600080fd5b505afa15801561048e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b291906107dc565b92915050565b60008054604080516395d89b4160e01b815290516060936001600160a01b03909316926395d89b419260048082019391829003018186803b1580156101bf57600080fd5b6000805460405163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529091169063a9059cbb90604401610236565b60008054604051636eb1769f60e11b81526001600160a01b03858116600483015284811660248301529091169063dd62ed3e9060440160206040518083038186803b15801561058457600080fd5b505afa158015610598573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061028891906107dc565b6000805460405162f4f4b560e81b815263ffffffff841660048201526001600160a01b039091169063f4f4b5009060240160206040518083038186803b15801561060557600080fd5b505afa158015610619573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b2919061070d565b80356001600160a01b038116811461065457600080fd5b919050565b60006020828403121561066b57600080fd5b6102888261063d565b6000806040838503121561068757600080fd5b6106908361063d565b915061069e6020840161063d565b90509250929050565b6000806000606084860312156106bc57600080fd5b6106c58461063d565b92506106d36020850161063d565b9150604084013590509250925092565b600080604083850312156106f657600080fd5b6106ff8361063d565b946020939093013593505050565b60006020828403121561071f57600080fd5b8151801515811461028857600080fd5b60006020828403121561074157600080fd5b815167ffffffffffffffff8082111561075957600080fd5b818401915084601f83011261076d57600080fd5b81518181111561077f5761077f6108a1565b604051601f8201601f19908116603f011681019083821181831017156107a7576107a76108a1565b816040528281528760208487010111156107c057600080fd5b6107d1836020830160208801610871565b979650505050505050565b6000602082840312156107ee57600080fd5b5051919050565b60006020828403121561080757600080fd5b813563ffffffff8116811461028857600080fd5b60006020828403121561082d57600080fd5b815160ff8116811461028857600080fd5b602081526000825180602084015261085d816040850160208701610871565b601f01601f19169190910160400192915050565b60005b8381101561088c578181015183820152602001610874565b8381111561089b576000848401525b50505050565b634e487b7160e01b600052604160045260246000fdfea2646970667358221220c05e4cb7ab439b86c0b6ac8a84eec6a230b592fa63d1ab2e3a7f1474c27338f364736f6c63430008070033 \ No newline at end of file --- /dev/null +++ b/js-packages/tests/eth/proxy/UniqueFungibleProxy.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: OTHER + +pragma solidity >=0.8.0 <0.9.0; + +import "../api/UniqueFungible.sol"; + +contract UniqueFungibleProxy is UniqueFungible { + UniqueFungible proxied; + + constructor(address _proxied) UniqueFungible() { + proxied = UniqueFungible(_proxied); + } + + function name() external view override returns (string memory) { + return proxied.name(); + } + + function symbol() external view override returns (string memory) { + return proxied.symbol(); + } + + function totalSupply() external view override returns (uint256) { + return proxied.totalSupply(); + } + + function supportsInterface(uint32 interfaceId) + external + view + override + returns (bool) + { + return proxied.supportsInterface(interfaceId); + } + + function decimals() external view override returns (uint8) { + return proxied.decimals(); + } + + function balanceOf(address owner) external view override returns (uint256) { + return proxied.balanceOf(owner); + } + + function transfer(address to, uint256 amount) + external + override + returns (bool) + { + return proxied.transfer(to, amount); + } + + function transferFrom( + address from, + address to, + uint256 amount + ) external override returns (bool) { + return proxied.transferFrom(from, to, amount); + } + + function approve(address spender, uint256 amount) + external + override + returns (bool) + { + return proxied.approve(spender, amount); + } + + function allowance(address owner, address spender) + external + view + override + returns (uint256) + { + return proxied.allowance(owner, spender); + } +} --- /dev/null +++ b/js-packages/tests/eth/proxy/UniqueNFTProxy.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_proxied","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[],"name":"MintingFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"approved","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"}],"name":"deleteProperty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finishMinting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"mintBulk","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"uint256","name":"field_0","type":"uint256"},{"internalType":"string","name":"field_1","type":"string"}],"internalType":"struct Tuple0[]","name":"tokens","type":"tuple[]"}],"name":"mintBulkWithTokenURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"tokenUri","type":"string"}],"name":"mintWithTokenURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintingFinished","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFromWithData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string","name":"value","type":"string"}],"name":"setProperty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file --- /dev/null +++ b/js-packages/tests/eth/proxy/UniqueNFTProxy.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506040516116bb3803806116bb83398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b611628806100936000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80634f6ccce7116100f957806379cc679011610097578063a22cb46511610071578063a22cb46514610399578063a9059cbb146103ac578063c87b56dd146103bf578063e985e9c5146103d257600080fd5b806379cc6790146103765780637d64bcb41461038957806395d89b411461039157600080fd5b806362d9491f116100d357806362d9491f146103355780636352211e1461034857806370a082311461035b57806375794a3c1461036e57600080fd5b80634f6ccce7146102fc57806350bb4e7f1461030f57806360a116721461032257600080fd5b80632f745c591161016657806340c10f191161014057806340c10f19146102b057806342842e0e146102c357806342966c68146102d657806344a9945e146102e957600080fd5b80632f745c5914610277578063342419141461028a578063365430061461029d57600080fd5b8063081812fc116101a2578063081812fc1461020e578063095ea7b31461023957806318160ddd1461024e57806323b872dd1461026457600080fd5b806301ffc9a7146101c957806305d2035b146101f157806306fdde03146101f9575b600080fd5b6101dc6101d7366004610dca565b6103e5565b60405190151581526020015b60405180910390f35b6101dc610462565b6102016104df565b6040516101e89190610e4c565b61022161021c366004610e5f565b610550565b6040516001600160a01b0390911681526020016101e8565b61024c610247366004610e90565b6105bf565b005b61025661062a565b6040519081526020016101e8565b61024c610272366004610ebc565b6106a2565b610256610285366004610e90565b610716565b61024c610298366004610ff3565b610793565b6101dc6102ab36600461104c565b6107f8565b6101dc6102be366004610e90565b61086e565b61024c6102d1366004610ebc565b6108a8565b61024c6102e4366004610e5f565b6108e9565b6101dc6102f736600461114f565b61091a565b61025661030a366004610e5f565b61094d565b6101dc61031d3660046111f5565b6109bc565b61024c61033036600461124e565b610a3c565b61024c6103433660046112ce565b610aab565b610221610356366004610e5f565b610add565b610256610369366004611332565b610b0f565b610256610b42565b61024c610384366004610e90565b610b96565b6101dc610bcf565b610201610c25565b61024c6103a736600461135d565b610c6e565b61024c6103ba366004610e90565b610ca8565b6102016103cd366004610e5f565b610ce1565b6102216103e0366004611396565b610d53565b600080546040516301ffc9a760e01b81526001600160e01b0319841660048201526001600160a01b03909116906301ffc9a790602401602060405180830381865afa158015610438573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c91906113c4565b92915050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166305d2035b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104da91906113c4565b905090565b60008054604080516306fdde0360e01b815290516060936001600160a01b03909316926306fdde0392600480820193918290030181865afa158015610528573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104da91908101906113e1565b6000805460405163020604bf60e21b8152600481018490526001600160a01b039091169063081812fc906024015b602060405180830381865afa15801561059b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c9190611458565b60005460405163095ea7b360e01b81526001600160a01b038481166004830152602482018490529091169063095ea7b3906044015b600060405180830381600087803b15801561060e57600080fd5b505af1158015610622573d6000803e3d6000fd5b505050505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561067e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104da9190611475565b6000546040516323b872dd60e01b81526001600160a01b038581166004830152848116602483015260448201849052909116906323b872dd906064015b600060405180830381600087803b1580156106f957600080fd5b505af115801561070d573d6000803e3d6000fd5b50505050505050565b60008054604051632f745c5960e01b81526001600160a01b0385811660048301526024820185905290911690632f745c5990604401602060405180830381865afa158015610768573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c9190611475565b9392505050565b600054604051630d09064560e21b81526001600160a01b03909116906334241914906107c3908490600401610e4c565b600060405180830381600087803b1580156107dd57600080fd5b505af11580156107f1573d6000803e3d6000fd5b5050505050565b60008054604051631b2a180360e11b81526001600160a01b039091169063365430069061082b908690869060040161148e565b6020604051808303816000875af115801561084a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c91906113c4565b600080546040516340c10f1960e01b81526001600160a01b03858116600483015260248201859052909116906340c10f199060440161082b565b600054604051632142170760e11b81526001600160a01b038581166004830152848116602483015260448201849052909116906342842e0e906064016106df565b600054604051630852cd8d60e31b8152600481018390526001600160a01b03909116906342966c68906024016107c3565b60008054604051632254ca2f60e11b81526001600160a01b03909116906344a9945e9061082b9086908690600401611513565b60008054604051634f6ccce760e01b8152600481018490526001600160a01b0390911690634f6ccce7906024015b602060405180830381865afa158015610998573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c9190611475565b600080546040516350bb4e7f60e01b81526001600160a01b03909116906350bb4e7f906109f190879087908790600401611569565b6020604051808303816000875af1158015610a10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3491906113c4565b949350505050565b6000546040516330508b3960e11b81526001600160a01b03909116906360a1167290610a72908790879087908790600401611590565b600060405180830381600087803b158015610a8c57600080fd5b505af1158015610aa0573d6000803e3d6000fd5b505050505b50505050565b6000546040516362d9491f60e01b81526001600160a01b03909116906362d9491f906105f490859085906004016115cd565b600080546040516331a9108f60e11b8152600481018490526001600160a01b0390911690636352211e9060240161057e565b600080546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a082319060240161097b565b60008060009054906101000a90046001600160a01b03166001600160a01b03166375794a3c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561067e573d6000803e3d6000fd5b60005460405163079cc67960e41b81526001600160a01b03848116600483015260248201849052909116906379cc6790906044016105f4565b60008060009054906101000a90046001600160a01b03166001600160a01b0316637d64bcb46040518163ffffffff1660e01b81526004016020604051808303816000875af11580156104b6573d6000803e3d6000fd5b60008054604080516395d89b4160e01b815290516060936001600160a01b03909316926395d89b4192600480820193918290030181865afa158015610528573d6000803e3d6000fd5b60005460405163a22cb46560e01b81526001600160a01b03848116600483015283151560248301529091169063a22cb465906044016105f4565b60005460405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490529091169063a9059cbb906044016105f4565b60005460405163c87b56dd60e01b8152600481018390526060916001600160a01b03169063c87b56dd90602401600060405180830381865afa158015610d2b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261045c91908101906113e1565b6000805460405163e985e9c560e01b81526001600160a01b03858116600483015284811660248301529091169063e985e9c590604401602060405180830381865afa158015610da6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c9190611458565b600060208284031215610ddc57600080fd5b81356001600160e01b03198116811461078c57600080fd5b60005b83811015610e0f578181015183820152602001610df7565b83811115610aa55750506000910152565b60008151808452610e38816020860160208601610df4565b601f01601f19169290920160200192915050565b60208152600061078c6020830184610e20565b600060208284031215610e7157600080fd5b5035919050565b6001600160a01b0381168114610e8d57600080fd5b50565b60008060408385031215610ea357600080fd5b8235610eae81610e78565b946020939093013593505050565b600080600060608486031215610ed157600080fd5b8335610edc81610e78565b92506020840135610eec81610e78565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610f3657610f36610efd565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610f6557610f65610efd565b604052919050565b600067ffffffffffffffff821115610f8757610f87610efd565b50601f01601f191660200190565b6000610fa8610fa384610f6d565b610f3c565b9050828152838383011115610fbc57600080fd5b828260208301376000602084830101529392505050565b600082601f830112610fe457600080fd5b61078c83833560208501610f95565b60006020828403121561100557600080fd5b813567ffffffffffffffff81111561101c57600080fd5b610a3484828501610fd3565b600067ffffffffffffffff82111561104257611042610efd565b5060051b60200190565b600080604080848603121561106057600080fd5b833561106b81610e78565b925060208481013567ffffffffffffffff8082111561108957600080fd5b818701915087601f83011261109d57600080fd5b81356110ab610fa382611028565b81815260059190911b8301840190848101908a8311156110ca57600080fd5b8585015b8381101561113d578035858111156110e65760008081fd5b8601808d03601f19018913156110fc5760008081fd5b611104610f13565b888201358152898201358781111561111c5760008081fd5b61112a8f8b83860101610fd3565b828b0152508452509186019186016110ce565b50809750505050505050509250929050565b6000806040838503121561116257600080fd5b823561116d81610e78565b915060208381013567ffffffffffffffff81111561118a57600080fd5b8401601f8101861361119b57600080fd5b80356111a9610fa382611028565b81815260059190911b820183019083810190888311156111c857600080fd5b928401925b828410156111e6578335825292840192908401906111cd565b80955050505050509250929050565b60008060006060848603121561120a57600080fd5b833561121581610e78565b925060208401359150604084013567ffffffffffffffff81111561123857600080fd5b61124486828701610fd3565b9150509250925092565b6000806000806080858703121561126457600080fd5b843561126f81610e78565b9350602085013561127f81610e78565b925060408501359150606085013567ffffffffffffffff8111156112a257600080fd5b8501601f810187136112b357600080fd5b6112c287823560208401610f95565b91505092959194509250565b600080604083850312156112e157600080fd5b823567ffffffffffffffff808211156112f957600080fd5b61130586838701610fd3565b9350602085013591508082111561131b57600080fd5b5061132885828601610fd3565b9150509250929050565b60006020828403121561134457600080fd5b813561078c81610e78565b8015158114610e8d57600080fd5b6000806040838503121561137057600080fd5b823561137b81610e78565b9150602083013561138b8161134f565b809150509250929050565b600080604083850312156113a957600080fd5b82356113b481610e78565b9150602083013561138b81610e78565b6000602082840312156113d657600080fd5b815161078c8161134f565b6000602082840312156113f357600080fd5b815167ffffffffffffffff81111561140a57600080fd5b8201601f8101841361141b57600080fd5b8051611429610fa382610f6d565b81815285602083850101111561143e57600080fd5b61144f826020830160208601610df4565b95945050505050565b60006020828403121561146a57600080fd5b815161078c81610e78565b60006020828403121561148757600080fd5b5051919050565b6001600160a01b0383168152604060208083018290528351828401819052600092916060600583901b860181019290860190878301865b8281101561150457888603605f190184528151805187528501518587018890526114f188880182610e20565b96505092840192908401906001016114c5565b50939998505050505050505050565b6001600160a01b038316815260406020808301829052835191830182905260009184820191906060850190845b8181101561155c57845183529383019391830191600101611540565b5090979650505050505050565b60018060a01b038416815282602082015260606040820152600061144f6060830184610e20565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906115c390830184610e20565b9695505050505050565b6040815260006115e06040830185610e20565b828103602084015261144f8185610e2056fea26469706673582212205bdf4275f4b714a1029ccfe77c0f9a0e20e121a932e1e5840cace97dfa886da964736f6c634300080d0033 \ No newline at end of file --- /dev/null +++ b/js-packages/tests/eth/proxy/UniqueNFTProxy.sol @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: OTHER + +pragma solidity >=0.8.0 <0.9.0; + +import "../api/UniqueNFT.sol"; + +contract UniqueNFTProxy is UniqueNFT { + UniqueNFT proxied; + + constructor(address _proxied) UniqueNFT() { + proxied = UniqueNFT(_proxied); + } + + function name() external view override returns (string memory) { + return proxied.name(); + } + + function symbol() external view override returns (string memory) { + return proxied.symbol(); + } + + function totalSupply() external view override returns (uint256) { + return proxied.totalSupply(); + } + + function balanceOf(address owner) external view override returns (uint256) { + return proxied.balanceOf(owner); + } + + function ownerOf(uint256 tokenId) external view override returns (address) { + return proxied.ownerOf(tokenId); + } + + function safeTransferFromWithData( + address from, + address to, + uint256 tokenId, + bytes memory data + ) external override { + return proxied.safeTransferFromWithData(from, to, tokenId, data); + } + + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) external override { + return proxied.safeTransferFrom(from, to, tokenId); + } + + function transferFrom( + address from, + address to, + uint256 tokenId + ) external override { + return proxied.transferFrom(from, to, tokenId); + } + + function approve(address approved, uint256 tokenId) external override { + return proxied.approve(approved, tokenId); + } + + function setApprovalForAll(address operator, bool approved) + external + override + { + return proxied.setApprovalForAll(operator, approved); + } + + function getApproved(uint256 tokenId) + external + view + override + returns (address) + { + return proxied.getApproved(tokenId); + } + + function isApprovedForAll(address owner, address operator) + external + view + override + returns (address) + { + return proxied.isApprovedForAll(owner, operator); + } + + function burn(uint256 tokenId) external override { + return proxied.burn(tokenId); + } + + function tokenByIndex(uint256 index) + external + view + override + returns (uint256) + { + return proxied.tokenByIndex(index); + } + + function tokenOfOwnerByIndex(address owner, uint256 index) + external + view + override + returns (uint256) + { + return proxied.tokenOfOwnerByIndex(owner, index); + } + + function tokenURI(uint256 tokenId) + external + view + override + returns (string memory) + { + return proxied.tokenURI(tokenId); + } + + function mintingFinished() external view override returns (bool) { + return proxied.mintingFinished(); + } + + function mint(address to) + external + override + returns (uint256) + { + return proxied.mint(to); + } + + function mintWithTokenURI( + address to, + string memory tokenUri + ) external override returns (uint256) { + return proxied.mintWithTokenURI(to, tokenUri); + } + + function finishMinting() external override returns (bool) { + return proxied.finishMinting(); + } + + function transfer(address to, uint256 tokenId) external override { + return proxied.transfer(to, tokenId); + } + + function nextTokenId() external view override returns (uint256) { + return proxied.nextTokenId(); + } + + function burnFrom(address from, uint256 tokenId) external override { + return proxied.burnFrom(from, tokenId); + } + + function supportsInterface(bytes4 interfaceId) + external + view + override + returns (bool) + { + return proxied.supportsInterface(interfaceId); + } + + function mintBulk(address to, uint256[] memory tokenIds) + external + override + returns (bool) + { + return proxied.mintBulk(to, tokenIds); + } + + function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) + external + override + returns (bool) + { + return proxied.mintBulkWithTokenURI(to, tokens); + } + + function setProperty(string memory key, string memory value) external override { + return proxied.setProperty(key, value); + } + + function deleteProperty(string memory key) external override { + return proxied.deleteProperty(key); + } +} --- /dev/null +++ b/js-packages/tests/eth/proxy/fungibleProxy.test.ts @@ -0,0 +1,207 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {expect} from 'chai'; +import {readFile} from 'fs/promises'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {itEth, usingEthPlaygrounds} from '../util/index.js'; +import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js'; +import {makeNames} from '../../util/index.js'; + +const {dirname} = makeNames(import.meta.url); + +async function proxyWrap(helper: EthUniqueHelper, wrapped: any, donor: IKeyringPair) { + // Proxy owner has no special privilegies, we don't need to reuse them + const owner = await helper.eth.createAccountWithBalance(donor); + const web3 = helper.getWeb3(); + const proxyContract = new web3.eth.Contract(JSON.parse((await readFile(`${dirname}/UniqueFungibleProxy.abi`)).toString()), undefined, { + from: owner, + gas: helper.eth.DEFAULT_GAS, + }); + const proxy = await proxyContract.deploy({data: (await readFile(`${dirname}/UniqueFungibleProxy.bin`)).toString(), arguments: [wrapped.options.address]}).send({from: owner}); + return proxy; +} + +describe('Fungible (Via EVM proxy): Information getting', () => { + let alice: IKeyringPair; + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + + itEth('totalSupply', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); + const caller = await helper.eth.createAccountWithBalance(donor); + await collection.mint(alice, 200n, {Substrate: alice.address}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + const totalSupply = await contract.methods.totalSupply().call(); + + expect(totalSupply).to.equal('200'); + }); + + itEth('balanceOf', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); + const caller = await helper.eth.createAccountWithBalance(donor); + + await collection.mint(alice, 200n, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + const balance = await contract.methods.balanceOf(caller).call(); + + expect(balance).to.equal('200'); + }); +}); + +describe('Fungible (Via EVM proxy): Plain calls', () => { + let alice: IKeyringPair; + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + + itEth('Can perform approve()', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); + const caller = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + await collection.mint(alice, 200n, {Ethereum: contract.options.address}); + + { + const result = await contract.methods.approve(spender, 100).send({from: caller}); + const events = helper.eth.normalizeEvents(result.events); + + expect(events).to.be.deep.equal([ + { + address, + event: 'Approval', + args: { + owner: contract.options.address, + spender, + value: '100', + }, + }, + ]); + } + + { + const allowance = await contract.methods.allowance(contract.options.address, spender).call(); + expect(+allowance).to.equal(100); + } + }); + + itEth('Can perform transferFrom()', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); + const caller = await helper.eth.createAccountWithBalance(donor); + const owner = await helper.eth.createAccountWithBalance(donor); + + await collection.mint(alice, 200n, {Ethereum: owner}); + const receiver = helper.eth.createAccount(); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + + await evmCollection.methods.approve(contract.options.address, 100).send({from: owner}); + + { + const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: caller}); + const events = helper.eth.normalizeEvents(result.events); + expect(events).to.be.deep.equal([ + { + address, + event: 'Transfer', + args: { + from: owner, + to: receiver, + value: '49', + }, + }, + { + address, + event: 'Approval', + args: { + owner, + spender: contract.options.address, + value: '51', + }, + }, + ]); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(49); + } + + { + const balance = await contract.methods.balanceOf(owner).call(); + expect(+balance).to.equal(151); + } + }); + + itEth('Can perform transfer()', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.eth.createAccountWithBalance(donor); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + await collection.mint(alice, 200n, {Ethereum: contract.options.address}); + + { + const result = await contract.methods.transfer(receiver, 50).send({from: caller}); + const events = helper.eth.normalizeEvents(result.events); + expect(events).to.be.deep.equal([ + { + address, + event: 'Transfer', + args: { + from: contract.options.address, + to: receiver, + value: '50', + }, + }, + ]); + } + + { + const balance = await contract.methods.balanceOf(contract.options.address).call(); + expect(+balance).to.equal(150); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(50); + } + }); +}); --- /dev/null +++ b/js-packages/tests/eth/proxy/nonFungibleProxy.test.ts @@ -0,0 +1,372 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {readFile} from 'fs/promises'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {itEth, usingEthPlaygrounds, expect} from '../util/index.js'; +import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js'; +import {makeNames} from '../../util/index.js'; + +const {dirname} = makeNames(import.meta.url); + + +async function proxyWrap(helper: EthUniqueHelper, wrapped: any, donor: IKeyringPair) { + // Proxy owner has no special privilegies, we don't need to reuse them + const owner = await helper.eth.createAccountWithBalance(donor); + const web3 = helper.getWeb3(); + const proxyContract = new web3.eth.Contract(JSON.parse((await readFile(`${dirname}/UniqueNFTProxy.abi`)).toString()), undefined, { + from: owner, + gas: helper.eth.DEFAULT_GAS, + }); + const proxy = await proxyContract.deploy({data: (await readFile(`${dirname}/UniqueNFTProxy.bin`)).toString(), arguments: [wrapped.options.address]}).send({from: owner}); + return proxy; +} + +describe('NFT (Via EVM proxy): Information getting', () => { + let alice: IKeyringPair; + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + + itEth('totalSupply', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const caller = await helper.eth.createAccountWithBalance(donor); + await collection.mintToken(alice, {Substrate: alice.address}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + const totalSupply = await contract.methods.totalSupply().call(); + + expect(totalSupply).to.equal('1'); + }); + + itEth('balanceOf', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + + const caller = await helper.eth.createAccountWithBalance(donor); + await collection.mintMultipleTokens(alice, [ + {owner: {Ethereum: caller}}, + {owner: {Ethereum: caller}}, + {owner: {Ethereum: caller}}, + ]); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + const balance = await contract.methods.balanceOf(caller).call(); + + expect(balance).to.equal('3'); + }); + + itEth('ownerOf', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + + const caller = await helper.eth.createAccountWithBalance(donor); + const {tokenId} = await collection.mintToken(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + const owner = await contract.methods.ownerOf(tokenId).call(); + + expect(owner).to.equal(caller); + }); +}); + +describe('NFT (Via EVM proxy): Plain calls', () => { + let alice: IKeyringPair; + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + + // Soft-deprecated + itEth('[eth] Can perform mint()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'A', 'A', ''); + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const collectionEvmOwned = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller, true); + const contract = await proxyWrap(helper, collectionEvm, donor); + await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send(); + + { + const nextTokenId = await contract.methods.nextTokenId().call(); + const result = await contract.methods.mintWithTokenURI(receiver, nextTokenId, 'Test URI').send({from: caller}); + const tokenId = result.events.Transfer.returnValues.tokenId; + expect(tokenId).to.be.equal('1'); + + const event = helper.eth.normalizeEvents(result.events) + .find(event => event.event === 'Transfer')!; + event.address = event.address.toLocaleLowerCase(); + + expect(event).to.be.deep.equal({ + address: collectionAddress.toLocaleLowerCase(), + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: receiver, + tokenId, + }, + }); + + expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); + } + }); + + itEth('[cross] Can perform mint()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'A', 'A', ''); + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const collectionEvmOwned = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); + const contract = await proxyWrap(helper, collectionEvm, donor); + const contractAddressCross = helper.ethCrossAccount.fromAddress(contract.options.address); + await collectionEvmOwned.methods.addCollectionAdminCross(contractAddressCross).send(); + + { + const nextTokenId = await contract.methods.nextTokenId().call(); + const result = await contract.methods.mintWithTokenURI(receiver, nextTokenId, 'Test URI').send({from: caller}); + const tokenId = result.events.Transfer.returnValues.tokenId; + expect(tokenId).to.be.equal('1'); + + const event = helper.eth.normalizeEvents(result.events) + .find(event => event.event === 'Transfer')!; + event.address = event.address.toLocaleLowerCase(); + + expect(event).to.be.deep.equal({ + address: collectionAddress.toLocaleLowerCase(), + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: receiver, + tokenId, + }, + }); + + expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); + } + }); + + //TODO: CORE-302 add eth methods + itEth.skip('Can perform mintBulk()', async ({helper}) => { + const collection = await helper.nft.mintCollection(donor, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); + + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + await collection.addAdmin(donor, {Ethereum: contract.options.address}); + + { + const nextTokenId = await contract.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + const result = await contract.methods.mintBulkWithTokenURI( + receiver, + [ + [nextTokenId, 'Test URI 0'], + [+nextTokenId + 1, 'Test URI 1'], + [+nextTokenId + 2, 'Test URI 2'], + ], + ).send({from: caller}); + const events = helper.eth.normalizeEvents(result.events); + + expect(events).to.be.deep.equal([ + { + address, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: receiver, + tokenId: nextTokenId, + }, + }, + { + address, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: receiver, + tokenId: String(+nextTokenId + 1), + }, + }, + { + address, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: receiver, + tokenId: String(+nextTokenId + 2), + }, + }, + ]); + + expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0'); + expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1'); + expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2'); + } + }); + + itEth('Can perform burn()', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const caller = await helper.eth.createAccountWithBalance(donor); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address}); + await collection.addAdmin(alice, {Ethereum: contract.options.address}); + + { + const result = await contract.methods.burn(tokenId).send({from: caller}); + const events = helper.eth.normalizeEvents(result.events); + + expect(events).to.be.deep.equal([ + { + address, + event: 'Transfer', + args: { + from: contract.options.address, + to: '0x0000000000000000000000000000000000000000', + tokenId: tokenId.toString(), + }, + }, + ]); + } + }); + + itEth('Can perform approve()', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const caller = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address}); + + { + const result = await contract.methods.approve(spender, tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}); + const events = helper.eth.normalizeEvents(result.events); + + expect(events).to.be.deep.equal([ + { + address, + event: 'Approval', + args: { + owner: contract.options.address, + approved: spender, + tokenId: tokenId.toString(), + }, + }, + ]); + } + }); + + itEth('Can perform transferFrom()', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const caller = await helper.eth.createAccountWithBalance(donor); + const owner = await helper.eth.createAccountWithBalance(donor); + + const receiver = helper.eth.createAccount(); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + const {tokenId} = await collection.mintToken(alice, {Ethereum: owner}); + + await evmCollection.methods.approve(contract.options.address, tokenId).send({from: owner}); + + { + const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: caller}); + const events = helper.eth.normalizeEvents(result.events); + expect(events).to.be.deep.equal([ + { + address, + event: 'Transfer', + args: { + from: owner, + to: receiver, + tokenId: tokenId.toString(), + }, + }, + ]); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(1); + } + + { + const balance = await contract.methods.balanceOf(contract.options.address).call(); + expect(+balance).to.equal(0); + } + }); + + itEth('Can perform transfer()', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); + const contract = await proxyWrap(helper, evmCollection, donor); + const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address}); + + { + const result = await contract.methods.transfer(receiver, tokenId).send({from: caller}); + const events = helper.eth.normalizeEvents(result.events); + expect(events).to.be.deep.equal([ + { + address, + event: 'Transfer', + args: { + from: contract.options.address, + to: receiver, + tokenId: tokenId.toString(), + }, + }, + ]); + } + + { + const balance = await contract.methods.balanceOf(contract.options.address).call(); + expect(+balance).to.equal(0); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(1); + } + }); +}); --- /dev/null +++ b/js-packages/tests/eth/proxyContract.test.ts @@ -0,0 +1,153 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; + +import {itEth, expect, usingEthPlaygrounds} from './util/index.js'; +import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; + +describe('EVM payable contracts', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Update proxy contract', async({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const proxyContract = await deployProxyContract(helper, deployer); + + const realContractV1 = await deployRealContractV1(helper, deployer); + const realContractV1proxy = new helper.web3!.eth.Contract(realContractV1.options.jsonInterface, proxyContract.options.address, {from: caller, gas: helper.eth.DEFAULT_GAS}); + await proxyContract.methods.updateVersion(realContractV1.options.address).send(); + + await realContractV1proxy.methods.flip().send(); + await realContractV1proxy.methods.flip().send(); + await realContractV1proxy.methods.flip().send(); + const value1 = await realContractV1proxy.methods.getValue().call(); + const flipCount1 = await realContractV1proxy.methods.getFlipCount().call(); + expect(flipCount1).to.be.equal('3'); + expect(value1).to.be.equal(true); + + const realContractV2 = await deployRealContractV2(helper, deployer); + const realContractV2proxy = new helper.web3!.eth.Contract(realContractV2.options.jsonInterface, proxyContract.options.address, {from: caller, gas: helper.eth.DEFAULT_GAS}); + await proxyContract.methods.updateVersion(realContractV2.options.address).send(); + + await realContractV2proxy.methods.flip().send(); + await realContractV2proxy.methods.flip().send(); + await realContractV2proxy.methods.setStep(5).send(); + await realContractV2proxy.methods.increaseFlipCount().send(); + const value2 = await realContractV2proxy.methods.getValue().call(); + const flipCount2 = await realContractV2proxy.methods.getFlipCount().call(); + expect(value2).to.be.equal(true); + expect(flipCount2).to.be.equal('6'); + }); + + async function deployProxyContract(helper: EthUniqueHelper, deployer: string) { + return await helper.ethContract.deployByCode(deployer, 'ProxyContract', ` + // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.6; + + contract ProxyContract { + event NewEvent(uint data); + receive() external payable {} + bytes32 private constant implementationSlot = bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1); + constructor() {} + function updateVersion(address newContractAddress) external { + bytes32 slot = implementationSlot; + assembly { + sstore(slot, newContractAddress) + } + } + fallback() external { + bytes32 slot = implementationSlot; + assembly { + let ptr := mload(0x40) + let contractAddress := sload(slot) + + calldatacopy(ptr, 0, calldatasize()) + + let result := delegatecall(gas(), contractAddress, ptr, calldatasize(), 0, 0) + let size := returndatasize() + + returndatacopy(ptr, 0, size) + + switch result + case 0 { revert(ptr, size) } + default { return(ptr, size) } + } + } + } + + interface RealContract { + function flip() external; + function getValue() external view returns (bool); + function getFlipCount() external view returns (uint); + }`); + } + + async function deployRealContractV1(helper: EthUniqueHelper, deployer: string) { + return await helper.ethContract.deployByCode(deployer, 'RealContractV1', ` + // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.6; + + contract RealContractV1 { + bool value = false; + uint flipCount = 0; + function flip() external { + value = !value; + flipCount++; + } + function getValue() external view returns (bool) { + return value; + } + function getFlipCount() external view returns (uint) { + return flipCount; + } + }`); + } + + async function deployRealContractV2(helper: EthUniqueHelper, deployer: string) { + return await helper.ethContract.deployByCode(deployer, 'RealContractV2', ` + // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.6; + + contract RealContractV2 { + bool value = false; + uint flipCount = 10; + uint step = 1; + function flip() external { + value = !value; + flipCount--; + } + function setStep(uint value) external { + step = value; + } + function increaseFlipCount() external { + flipCount = flipCount + step; + } + function getValue() external view returns (bool) { + return value; + } + function getFlipCount() external view returns (uint) { + return flipCount; + } + }`); + } +}); --- /dev/null +++ b/js-packages/tests/eth/reFungible.test.ts @@ -0,0 +1,1019 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {Pallets, requirePalletsOrSkip} from '../util/index.js'; +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import type {ITokenPropertyPermission} from '@unique/playgrounds/types.js'; +import {CREATE_COLLECTION_DATA_DEFAULTS, TokenPermissionField} from './util/playgrounds/types.js'; + +describe('Refungible: Plain calls', () => { + let donor: IKeyringPair; + let minter: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + [ + 'substrate' as const, + 'ethereum' as const, + ].map(testCase => { + itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => { + const collectionAdmin = await helper.eth.createAccountWithBalance(donor); + + const receiverEth = helper.eth.createAccount(); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const receiverSub = bob; + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub); + + const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)})); + const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: { + tokenOwner: false, + collectionAdmin: true, + mutable: false}})); + + + const collection = await helper.rft.mintCollection(minter, { + tokenPrefix: 'ethp', + tokenPropertyPermissions: permissions, + }); + await collection.addAdmin(minter, {Ethereum: collectionAdmin}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', collectionAdmin, true); + let expectedTokenId = await contract.methods.nextTokenId().call(); + let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send(); + let tokenId = result.events.Transfer.returnValues.tokenId; + expect(tokenId).to.be.equal(expectedTokenId); + + let event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address)); + expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); + + expectedTokenId = await contract.methods.nextTokenId().call(); + result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send(); + event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address)); + expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); + + tokenId = result.events.Transfer.returnValues.tokenId; + + expect(tokenId).to.be.equal(expectedTokenId); + + expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties + .map(p => helper.ethProperty.property(p.key, p.value.toString()))); + + expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)) + .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address}); + }); + }); + + itEth.skip('Can perform mintBulk()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'MintBulky', '6', '6', ''); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + + { + const nextTokenId = await contract.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + const result = await contract.methods.mintBulkWithTokenURI( + receiver, + [ + [nextTokenId, 'Test URI 0'], + [+nextTokenId + 1, 'Test URI 1'], + [+nextTokenId + 2, 'Test URI 2'], + ], + ).send(); + + const events = result.events.Transfer; + for(let i = 0; i < 2; i++) { + const event = events[i]; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal(receiver); + expect(event.returnValues.tokenId).to.equal(String(+nextTokenId + i)); + } + + expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0'); + expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1'); + expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2'); + } + }); + + itEth('Can perform mintBulkCross() with multiple tokens', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const callerCross = helper.ethCrossAccount.fromAddress(caller); + const receiver = helper.eth.createAccount(); + const receiverCross = helper.ethCrossAccount.fromAddress(receiver); + + const permissions = [ + {code: TokenPermissionField.Mutable, value: true}, + {code: TokenPermissionField.TokenOwner, value: true}, + {code: TokenPermissionField.CollectionAdmin, value: true}, + ]; + const {collectionAddress} = await helper.eth.createCollection( + caller, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: 'rft', + adminList: [callerCross], + tokenPropertyPermissions: [ + {key: 'key_0_0', permissions}, + {key: 'key_1_0', permissions}, + {key: 'key_1_1', permissions}, + {key: 'key_2_0', permissions}, + {key: 'key_2_1', permissions}, + {key: 'key_2_2', permissions}, + ], + }, + ).send(); + + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + const nextTokenId = await contract.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + const result = await contract.methods.mintBulkCross([ + { + owners: [{ + owner: receiverCross, + pieces: 1, + }], + properties: [ + {key: 'key_0_0', value: Buffer.from('value_0_0')}, + ], + }, + { + owners: [{ + owner: receiverCross, + pieces: 2, + }], + properties: [ + {key: 'key_1_0', value: Buffer.from('value_1_0')}, + {key: 'key_1_1', value: Buffer.from('value_1_1')}, + ], + }, + { + owners: [{ + owner: receiverCross, + pieces: 1, + }], + properties: [ + {key: 'key_2_0', value: Buffer.from('value_2_0')}, + {key: 'key_2_1', value: Buffer.from('value_2_1')}, + {key: 'key_2_2', value: Buffer.from('value_2_2')}, + ], + }, + ]).send({from: caller}); + const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId); + const bulkSize = 3; + for(let i = 0; i < bulkSize; i++) { + const event = events[i]; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal(receiver); + expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`); + } + + const properties = [ + await contract.methods.properties(+nextTokenId, []).call(), + await contract.methods.properties(+nextTokenId + 1, []).call(), + await contract.methods.properties(+nextTokenId + 2, []).call(), + ]; + expect(properties).to.be.deep.equal([ + [ + ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')], + ], + [ + ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')], + ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')], + ], + [ + ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')], + ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')], + ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')], + ], + ]); + }); + + itEth('Can perform mintBulkCross() with multiple owners', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const callerCross = helper.ethCrossAccount.fromAddress(caller); + const receiver = helper.eth.createAccount(); + const receiverCross = helper.ethCrossAccount.fromAddress(receiver); + const receiver2 = helper.eth.createAccount(); + const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2); + + const permissions = [ + {code: TokenPermissionField.Mutable, value: true}, + {code: TokenPermissionField.TokenOwner, value: true}, + {code: TokenPermissionField.CollectionAdmin, value: true}, + ]; + const {collectionAddress} = await helper.eth.createCollection( + caller, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: 'rft', + adminList: [callerCross], + tokenPropertyPermissions: [ + {key: 'key_2_0', permissions}, + {key: 'key_2_1', permissions}, + {key: 'key_2_2', permissions}, + ], + }, + ).send(); + + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + const nextTokenId = await contract.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + const result = await contract.methods.mintBulkCross([{ + owners: [ + { + owner: receiverCross, + pieces: 1, + }, + { + owner: receiver2Cross, + pieces: 2, + }, + ], + properties: [ + {key: 'key_2_0', value: Buffer.from('value_2_0')}, + {key: 'key_2_1', value: Buffer.from('value_2_1')}, + {key: 'key_2_2', value: Buffer.from('value_2_2')}, + ], + }]).send({from: caller}); + const event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'); + expect(event.returnValues.tokenId).to.equal(`${+nextTokenId}`); + + const properties = [ + await contract.methods.properties(+nextTokenId, []).call(), + ]; + expect(properties).to.be.deep.equal([[ + ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')], + ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')], + ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')], + ]]); + }); + + itEth('Can perform setApprovalForAll()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const operator = helper.eth.createAccount(); + + const collection = await helper.rft.mintCollection(minter, {}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + + const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call(); + expect(approvedBefore).to.be.equal(false); + + { + const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner}); + + expect(result.events.ApprovalForAll).to.be.like({ + address: collectionAddress, + event: 'ApprovalForAll', + returnValues: { + owner, + operator, + approved: true, + }, + }); + + const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call(); + expect(approvedAfter).to.be.equal(true); + } + + { + const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner}); + + expect(result.events.ApprovalForAll).to.be.like({ + address: collectionAddress, + event: 'ApprovalForAll', + returnValues: { + owner, + operator, + approved: false, + }, + }); + + const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call(); + expect(approvedAfter).to.be.equal(false); + } + }); + + itEth('Can perform burn with ApprovalForAll', async ({helper}) => { + const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const owner = await helper.eth.createAccountWithBalance(donor); + const operator = await helper.eth.createAccountWithBalance(donor); + + const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'rft'); + + { + await contract.methods.setApprovalForAll(operator, true).send({from: owner}); + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator}); + const events = result.events.Transfer; + + expect(events).to.be.like({ + address, + event: 'Transfer', + returnValues: { + from: owner, + to: '0x0000000000000000000000000000000000000000', + tokenId: token.tokenId.toString(), + }, + }); + } + }); + + itEth('Can perform burn with approve and approvalForAll', async ({helper}) => { + const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const owner = await helper.eth.createAccountWithBalance(donor); + const operator = await helper.eth.createAccountWithBalance(donor); + + const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'rft'); + + const rftToken = await helper.ethNativeContract.rftTokenById(token.collectionId, token.tokenId, owner, true); + + { + await rftToken.methods.approve(operator, 15n).send({from: owner}); + await contract.methods.setApprovalForAll(operator, true).send({from: owner}); + await rftToken.methods.burnFrom(owner, 10n).send({from: operator}); + } + { + const allowance = await rftToken.methods.allowance(owner, operator).call(); + expect(+allowance).to.be.equal(5); + } + { + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const operatorCross = helper.ethCrossAccount.fromAddress(operator); + const allowance = await rftToken.methods.allowanceCross(ownerCross, operatorCross).call(); + expect(+allowance).to.equal(5); + } + }); + + itEth('Can perform transfer with ApprovalForAll', async ({helper}) => { + const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const owner = await helper.eth.createAccountWithBalance(donor); + const operator = await helper.eth.createAccountWithBalance(donor); + const receiver = charlie; + + const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'rft'); + + { + await contract.methods.setApprovalForAll(operator, true).send({from: owner}); + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); + const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator}); + const event = result.events.Transfer; + expect(event).to.be.like({ + address: helper.ethAddress.fromCollectionId(collection.collectionId), + event: 'Transfer', + returnValues: { + from: owner, + to: helper.address.substrateToEth(receiver.address), + tokenId: token.tokenId.toString(), + }, + }); + } + + expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]); + }); + + itEth('Can perform burn()', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Burny', '6', '6'); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + + const result = await contract.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + { + const result = await contract.methods.burn(tokenId).send(); + const event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal(caller); + expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.tokenId).to.equal(tokenId.toString()); + } + }); + + itEth('Can perform transferFrom()', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'TransferFromy', '6', '6'); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + + const result = await contract.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + + const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId); + + const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, caller); + await tokenContract.methods.repartition(15).send(); + + { + const tokenEvents: any = []; + tokenContract.events.allEvents((_: any, event: any) => { + tokenEvents.push(event); + }); + const result = await contract.methods.transferFrom(caller, receiver, tokenId).send(); + if(tokenEvents.length == 0) await helper.wait.newBlocks(1); + + let event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal(caller); + expect(event.returnValues.to).to.equal(receiver); + expect(event.returnValues.tokenId).to.equal(tokenId.toString()); + + event = tokenEvents[0]; + expect(event.address).to.equal(tokenAddress); + expect(event.returnValues.from).to.equal(caller); + expect(event.returnValues.to).to.equal(receiver); + expect(event.returnValues.value).to.equal('15'); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(1); + } + + { + const balance = await contract.methods.balanceOf(caller).call(); + expect(+balance).to.equal(0); + } + }); + + // Soft-deprecated + itEth('Can perform burnFrom()', async ({helper}) => { + const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = await helper.eth.createAccountWithBalance(donor); + + const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'rft', spender, true); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId); + const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + await tokenContract.methods.repartition(15).send(); + await tokenContract.methods.approve(spender, 15).send(); + + { + const result = await contract.methods.burnFrom(owner, token.tokenId).send(); + const event = result.events.Transfer; + expect(event).to.be.like({ + address: helper.ethAddress.fromCollectionId(collection.collectionId), + event: 'Transfer', + returnValues: { + from: owner, + to: '0x0000000000000000000000000000000000000000', + tokenId: token.tokenId.toString(), + }, + }); + } + + expect(await collection.getTokenBalance(token.tokenId, {Ethereum: owner})).to.be.eq(0n); + }); + + itEth('Can perform burnFromCross()', async ({helper}) => { + const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const owner = bob; + const spender = await helper.eth.createAccountWithBalance(donor); + + const token = await collection.mintToken(minter, 100n, {Substrate: owner.address}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'rft'); + + await token.repartition(owner, 15n); + await token.approve(owner, {Ethereum: spender}, 15n); + + { + const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner); + const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender}); + const event = result.events.Transfer; + expect(event).to.be.like({ + address: helper.ethAddress.fromCollectionId(collection.collectionId), + event: 'Transfer', + returnValues: { + from: helper.address.substrateToEth(owner.address), + to: '0x0000000000000000000000000000000000000000', + tokenId: token.tokenId.toString(), + }, + }); + } + + expect(await collection.getTokenBalance(token.tokenId, {Substrate: owner.address})).to.be.eq(0n); + }); + + itEth('Can perform transferFromCross()', async ({helper}) => { + const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const owner = bob; + const spender = await helper.eth.createAccountWithBalance(donor); + const receiver = charlie; + + const token = await collection.mintToken(minter, 100n, {Substrate: owner.address}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'rft'); + + await token.repartition(owner, 15n); + await token.approve(owner, {Ethereum: spender}, 15n); + + { + const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner); + const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); + const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender}); + const event = result.events.Transfer; + expect(event).to.be.like({ + address: helper.ethAddress.fromCollectionId(collection.collectionId), + event: 'Transfer', + returnValues: { + from: helper.address.substrateToEth(owner.address), + to: helper.address.substrateToEth(receiver.address), + tokenId: token.tokenId.toString(), + }, + }); + } + + expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]); + }); + + itEth('Can perform transfer()', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry', '6', '6'); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + + const result = await contract.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + + { + const result = await contract.methods.transfer(receiver, tokenId).send(); + + const event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal(caller); + expect(event.returnValues.to).to.equal(receiver); + expect(event.returnValues.tokenId).to.equal(tokenId.toString()); + } + + { + const balance = await contract.methods.balanceOf(caller).call(); + expect(+balance).to.equal(0); + } + + { + const balance = await contract.methods.balanceOf(receiver).call(); + expect(+balance).to.equal(1); + } + }); + + itEth('Can perform transferCross()', async ({helper}) => { + const sender = await helper.eth.createAccountWithBalance(donor); + const receiverEth = await helper.eth.createAccountWithBalance(donor); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter); + + const collection = await helper.rft.mintCollection(minter, {}); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', sender); + + const token = await collection.mintToken(minter, 50n, {Ethereum: sender}); + + { + // Can transferCross to ethereum address: + const result = await collectionEvm.methods.transferCross(receiverCrossEth, token.tokenId).send({from: sender}); + // Check events: + const event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal(sender); + expect(event.returnValues.to).to.equal(receiverEth); + expect(event.returnValues.tokenId).to.equal(token.tokenId.toString()); + // Sender's balance decreased: + const senderBalance = await collectionEvm.methods.balanceOf(sender).call(); + expect(+senderBalance).to.equal(0); + expect(await token.getBalance({Ethereum: sender})).to.eq(0n); + // Receiver's balance increased: + const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); + expect(+receiverBalance).to.equal(1); + expect(await token.getBalance({Ethereum: receiverEth})).to.eq(50n); + } + + { + // Can transferCross to substrate address: + const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, token.tokenId).send({from: receiverEth}); + // Check events: + const event = substrateResult.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal(receiverEth); + expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address)); + expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`); + // Sender's balance decreased: + const senderBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); + expect(+senderBalance).to.equal(0); + expect(await token.getBalance({Ethereum: receiverEth})).to.eq(0n); + // Receiver's balance increased: + const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address}); + expect(receiverBalance).to.contain(token.tokenId); + expect(await token.getBalance({Substrate: minter.address})).to.eq(50n); + } + }); + + ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => { + const sender = await helper.eth.createAccountWithBalance(donor); + const tokenOwner = await helper.eth.createAccountWithBalance(donor); + const receiverSub = minter; + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter); + + const collection = await helper.rft.mintCollection(minter, {}); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', sender); + + await collection.mintToken(minter, 50n, {Ethereum: sender}); + const nonSendersToken = await collection.mintToken(minter, 50n, {Ethereum: tokenOwner}); + + // Cannot transferCross someone else's token: + const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub; + await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected; + // Cannot transfer token if it does not exist: + await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected; + })); + + itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Partial-to-Full', '6', '6'); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + + const result = await contract.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + + const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller); + + await tokenContract.methods.repartition(2).send(); + await tokenContract.methods.transfer(receiver, 1).send(); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + await tokenContract.methods.transfer(receiver, 1).send(); + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'); + expect(event.returnValues.to).to.equal(receiver); + expect(event.returnValues.tokenId).to.equal(tokenId.toString()); + }); + + itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Full-to-Partial', '6', '6'); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + + const result = await contract.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + + const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller); + + await tokenContract.methods.repartition(2).send(); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + await tokenContract.methods.transfer(receiver, 1).send(); + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal(caller); + expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'); + expect(event.returnValues.tokenId).to.equal(tokenId.toString()); + }); + + itEth('Check balanceOfCross()', async ({helper}) => { + const collection = await helper.rft.mintCollection(minter, {}); + const owner = await helper.ethCrossAccount.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth); + + expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0'); + + for(let i = 1n; i < 10n; i++) { + await collection.mintToken(minter, 100n, {Ethereum: owner.eth}); + expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString()); + } + }); + + itEth('Check ownerOfCross()', async ({helper}) => { + const collection = await helper.rft.mintCollection(minter, {}); + let owner = await helper.ethCrossAccount.createAccountWithBalance(donor); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth); + const {tokenId} = await collection.mintToken(minter, 100n,{Ethereum: owner.eth}); + + for(let i = 1n; i < 10n; i++) { + const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth}); + expect(ownerCross.eth).to.be.eq(owner.eth); + expect(ownerCross.sub).to.be.eq(owner.sub); + + const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor); + await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth}); + owner = newOwner; + } + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth, true); + const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor); + await tokenContract.methods.transferCross(newOwner, 50).send({from: owner.eth}); + const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth}); + expect(ownerCross.eth.toUpperCase()).to.be.eq('0XFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'); + expect(ownerCross.sub).to.be.eq('0'); + }); +}); + +describe('RFT: Fees', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer-From', '6', '6'); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + + const result = await contract.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + + const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send()); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + expect(cost > 0n); + }); + + itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer', '6', '6'); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + + const result = await contract.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + + const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send()); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + expect(cost > 0n); + }); +}); + +describe('Common metadata', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([1000n], donor); + }); + }); + + itEth('Returns collection name', async ({helper}) => { + const caller = helper.eth.createAccount(); + const tokenPropertyPermissions = [{ + key: 'URI', + permission: { + mutable: true, + collectionAdmin: true, + tokenOwner: false, + }, + }]; + const collection = await helper.rft.mintCollection( + alice, + { + name: 'Leviathan', + tokenPrefix: '11', + properties: [{key: 'ERC721Metadata', value: '1'}], + tokenPropertyPermissions, + }, + ); + + const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller); + const name = await contract.methods.name().call(); + expect(name).to.equal('Leviathan'); + }); + + itEth('Returns symbol name', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const tokenPropertyPermissions = [{ + key: 'URI', + permission: { + mutable: true, + collectionAdmin: true, + tokenOwner: false, + }, + }]; + const {collectionId} = await helper.rft.mintCollection( + alice, + { + name: 'Leviathan', + tokenPrefix: '12', + properties: [{key: 'ERC721Metadata', value: '1'}], + tokenPropertyPermissions, + }, + ); + + const contract = await helper.ethNativeContract.collectionById(collectionId, 'rft', caller); + const symbol = await contract.methods.symbol().call(); + expect(symbol).to.equal('12'); + }); +}); + +describe('Negative tests', () => { + let donor: IKeyringPair; + let minter: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itEth('[negative] Cant perform burn without approval', async ({helper}) => { + const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = await helper.eth.createAccountWithBalance(donor); + + const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'rft'); + + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + + await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected; + + await contract.methods.setApprovalForAll(spender, true).send({from: owner}); + await contract.methods.setApprovalForAll(spender, false).send({from: owner}); + + await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected; + }); + + itEth('[negative] Cant perform transfer without approval', async ({helper}) => { + const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = alice; + + const spender = await helper.eth.createAccountWithBalance(donor); + + const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'rft'); + + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); + + await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected; + + await contract.methods.setApprovalForAll(spender, true).send({from: owner}); + await contract.methods.setApprovalForAll(spender, false).send({from: owner}); + + await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected; + }); + + itEth('[negative] Can perform mintBulkCross() with multiple owners and multiple tokens', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const callerCross = helper.ethCrossAccount.fromAddress(caller); + const receiver = helper.eth.createAccount(); + const receiverCross = helper.ethCrossAccount.fromAddress(receiver); + const receiver2 = helper.eth.createAccount(); + const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2); + + const permissions = [ + {code: TokenPermissionField.Mutable, value: true}, + {code: TokenPermissionField.TokenOwner, value: true}, + {code: TokenPermissionField.CollectionAdmin, value: true}, + ]; + const {collectionAddress} = await helper.eth.createCollection( + caller, + { + ...CREATE_COLLECTION_DATA_DEFAULTS, + name: 'A', + description: 'B', + tokenPrefix: 'C', + collectionMode: 'rft', + adminList: [callerCross], + tokenPropertyPermissions: [ + {key: 'key_0_0', permissions}, + {key: 'key_2_0', permissions}, + {key: 'key_2_1', permissions}, + {key: 'key_2_2', permissions}, + ], + }, + ).send(); + + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + const nextTokenId = await contract.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + const createData = [ + { + owners: [{ + owner: receiverCross, + pieces: 1, + }], + properties: [ + {key: 'key_0_0', value: Buffer.from('value_0_0')}, + ], + }, + { + owners: [ + { + owner: receiverCross, + pieces: 1, + }, + { + owner: receiver2Cross, + pieces: 2, + }, + ], + properties: [ + {key: 'key_2_0', value: Buffer.from('value_2_0')}, + {key: 'key_2_1', value: Buffer.from('value_2_1')}, + {key: 'key_2_2', value: Buffer.from('value_2_2')}, + ], + }, + ]; + + await expect(contract.methods.mintBulkCross(createData).call({from: caller})).to.be.rejectedWith('creation of multiple tokens supported only if they have single owner each'); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/reFungibleToken.test.ts @@ -0,0 +1,677 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {Pallets, requirePalletsOrSkip} from '../util/index.js'; +import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; +import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {Contract} from 'web3-eth-contract'; + +// FIXME: Need erc721 for ReFubgible. +describe('Check ERC721 token URI for ReFungible', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + }); + }); + + async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Mint collection', 'a', 'b', baseUri); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + + const result = await contract.methods.mint(receiver).send(); + + const event = result.events.Transfer; + const tokenId = event.returnValues.tokenId; + expect(tokenId).to.be.equal('1'); + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(receiver); + + if(propertyKey && propertyValue) { + // Set URL or suffix + + await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send(); + } + + return {contract, nextTokenId: tokenId}; + } + + itEth('Empty tokenURI', async ({helper}) => { + const {contract, nextTokenId} = await setup(helper, ''); + expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal(''); + }); + + itEth('TokenURI from url', async ({helper}) => { + const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI'); + expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI'); + }); + + itEth('TokenURI from baseURI', async ({helper}) => { + const {contract, nextTokenId} = await setup(helper, 'BaseURI_'); + expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_'); + }); + + itEth('TokenURI from baseURI + suffix', async ({helper}) => { + const suffix = '/some/suffix'; + const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix); + expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix); + }); +}); + +describe('Refungible: Plain calls', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([50n], donor); + }); + }); + + itEth('Can perform approve()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + const collection = await helper.rft.mintCollection(alice); + const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + { + const result = await contract.methods.approve(spender, 100).send({from: owner}); + const event = result.events.Approval; + expect(event.address).to.be.equal(tokenAddress); + expect(event.returnValues.owner).to.be.equal(owner); + expect(event.returnValues.spender).to.be.equal(spender); + expect(event.returnValues.value).to.be.equal('100'); + } + + { + const allowance = await contract.methods.allowance(owner, spender).call(); + expect(+allowance).to.equal(100); + } + }); + + itEth('Can perform approveCross()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + const spenderSub = (await helper.arrange.createAccounts([1n], donor))[0]; + const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender); + const spenderCrossSub = helper.ethCrossAccount.fromKeyringPair(spenderSub); + + + const collection = await helper.rft.mintCollection(alice); + const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + { + const result = await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner}); + const event = result.events.Approval; + expect(event.address).to.be.equal(tokenAddress); + expect(event.returnValues.owner).to.be.equal(owner); + expect(event.returnValues.spender).to.be.equal(spender); + expect(event.returnValues.value).to.be.equal('100'); + } + + { + const allowance = await contract.methods.allowance(owner, spender).call(); + expect(+allowance).to.equal(100); + } + + + { + const result = await contract.methods.approveCross(spenderCrossSub, 100).send({from: owner}); + const event = result.events.Approval; + expect(event.address).to.be.equal(tokenAddress); + expect(event.returnValues.owner).to.be.equal(owner); + expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(spenderSub.address)); + expect(event.returnValues.value).to.be.equal('100'); + } + + { + const allowance = await collection.getTokenApprovedPieces(tokenId, {Ethereum: owner}, {Substrate: spenderSub.address}); + expect(allowance).to.equal(100n); + } + + { + //TO-DO expect with future allowanceCross(owner, spenderCrossEth).call() + } + }); + + itEth('Non-owner and non admin cannot approveCross', async ({helper}) => { + const nonOwner = await helper.eth.createAccountWithBalance(donor); + const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner); + const owner = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const token = await collection.mintToken(alice, 100n, {Ethereum: owner}); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId); + const tokenEvm = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + await expect(tokenEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned'); + }); + + [ + 'transferFrom', + 'transferFromCross', + ].map(testCase => + itEth(`Can perform ${testCase}`, async ({helper}) => { + const isCross = testCase === 'transferFromCross'; + const owner = await helper.eth.createAccountWithBalance(donor); + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const spender = await helper.eth.createAccountWithBalance(donor); + const receiverEth = helper.eth.createAccount(); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const [receiverSub] = await helper.arrange.createAccounts([1n], donor); + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub); + + const collection = await helper.rft.mintCollection(alice); + const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + await contract.methods.approve(spender, 100).send({from: owner}); + + // 1. Can transfer from + // 1.1 Plain ethereum or cross address: + { + const result = await contract.methods[testCase]( + isCross ? ownerCross : owner, + isCross ? receiverCrossEth : receiverEth, + 49, + ).send({from: spender}); + + // Check events: + const transferEvent = result.events.Transfer; + expect(transferEvent.address).to.be.equal(tokenAddress); + expect(transferEvent.returnValues.from).to.be.equal(owner); + expect(transferEvent.returnValues.to).to.be.equal(receiverEth); + expect(transferEvent.returnValues.value).to.be.equal('49'); + + const approvalEvent = result.events.Approval; + expect(approvalEvent.address).to.be.equal(tokenAddress); + expect(approvalEvent.returnValues.owner).to.be.equal(owner); + expect(approvalEvent.returnValues.spender).to.be.equal(spender); + expect(approvalEvent.returnValues.value).to.be.equal('51'); + + // Check balances: + const receiverBalance = await contract.methods.balanceOf(receiverEth).call(); + const ownerBalance = await contract.methods.balanceOf(owner).call(); + + expect(+receiverBalance).to.equal(49); + expect(+ownerBalance).to.equal(151); + } + + // 1.2 Cross substrate address: + if(testCase === 'transferFromCross') { + const result = await contract.methods.transferFromCross(ownerCross, receiverCrossSub, 51).send({from: spender}); + // Check events: + const transferEvent = result.events.Transfer; + expect(transferEvent.address).to.be.equal(tokenAddress); + expect(transferEvent.returnValues.from).to.be.equal(owner); + expect(transferEvent.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address)); + expect(transferEvent.returnValues.value).to.be.equal('51'); + + const approvalEvent = result.events.Approval; + expect(approvalEvent.address).to.be.equal(tokenAddress); + expect(approvalEvent.returnValues.owner).to.be.equal(owner); + expect(approvalEvent.returnValues.spender).to.be.equal(spender); + expect(approvalEvent.returnValues.value).to.be.equal('0'); + + // Check balances: + const receiverBalance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address}); + const ownerBalance = await contract.methods.balanceOf(owner).call(); + expect(receiverBalance).to.equal(51n); + expect(+ownerBalance).to.equal(100); + } + })); + + [ + 'transfer', + 'transferCross', + ].map(testCase => + itEth(`Can perform ${testCase}()`, async ({helper}) => { + const isCross = testCase === 'transferCross'; + const owner = await helper.eth.createAccountWithBalance(donor); + const receiverEth = helper.eth.createAccount(); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const [receiverSub] = await helper.arrange.createAccounts([1n], donor); + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub); + const collection = await helper.rft.mintCollection(alice); + const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + // 1. Can transfer to plain ethereum or cross-ethereum account: + { + const result = await contract.methods[testCase](isCross ? receiverCrossEth : receiverEth, 50).send({from: owner}); + // Check events + const transferEvent = result.events.Transfer; + expect(transferEvent.address).to.be.equal(tokenAddress); + expect(transferEvent.returnValues.from).to.be.equal(owner); + expect(transferEvent.returnValues.to).to.be.equal(receiverEth); + expect(transferEvent.returnValues.value).to.be.equal('50'); + // Check balances: + const ownerBalance = await contract.methods.balanceOf(owner).call(); + const receiverBalance = await contract.methods.balanceOf(receiverEth).call(); + expect(+ownerBalance).to.equal(150); + expect(+receiverBalance).to.equal(50); + } + + // 2. Can transfer to cross-substrate account: + if(isCross) { + const result = await contract.methods.transferCross(receiverCrossSub, 50).send({from: owner}); + // Check events: + const event = result.events.Transfer; + expect(event.address).to.be.equal(tokenAddress); + expect(event.returnValues.from).to.be.equal(owner); + expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address)); + expect(event.returnValues.value).to.be.equal('50'); + // Check balances: + const ownerBalance = await contract.methods.balanceOf(owner).call(); + const receiverBalance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address}); + expect(+ownerBalance).to.equal(100); + expect(receiverBalance).to.equal(50n); + } + })); + + [ + 'transfer', + 'transferCross', + ].map(testCase => + itEth(`Cannot ${testCase}() non-owned token`, async ({helper}) => { + const isCross = testCase === 'transferCross'; + const owner = await helper.eth.createAccountWithBalance(donor); + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const receiverEth = await helper.eth.createAccountWithBalance(donor); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const collection = await helper.rft.mintCollection(alice); + const rftOwner = await collection.mintToken(alice, 10n, {Ethereum: owner}); + const rftReceiver = await collection.mintToken(alice, 10n, {Ethereum: receiverEth}); + const tokenIdNonExist = 9999999; + + const tokenAddress1 = helper.ethAddress.fromTokenId(collection.collectionId, rftOwner.tokenId); + const tokenAddress2 = helper.ethAddress.fromTokenId(collection.collectionId, rftReceiver.tokenId); + const tokenAddressNonExist = helper.ethAddress.fromTokenId(collection.collectionId, tokenIdNonExist); + const tokenEvmOwner = await helper.ethNativeContract.rftToken(tokenAddress1, owner); + const tokenEvmReceiver = await helper.ethNativeContract.rftToken(tokenAddress2, owner); + const tokenEvmNonExist = await helper.ethNativeContract.rftToken(tokenAddressNonExist, owner); + + // 1. Can transfer zero amount (EIP-20): + await tokenEvmOwner.methods[testCase](isCross ? receiverCrossEth : receiverEth, 0).send({from: owner}); + // 2. Cannot transfer non-owned token: + await expect(tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 0).send({from: owner})).to.be.rejected; + await expect(tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 5).send({from: owner})).to.be.rejected; + // 3. Cannot transfer non-existing token: + await expect(tokenEvmNonExist.methods[testCase](isCross ? ownerCross : owner, 0).send({from: owner})).to.be.rejected; + await expect(tokenEvmNonExist.methods[testCase](isCross ? ownerCross : owner, 5).send({from: owner})).to.be.rejected; + + // 4. Storage is not corrupted: + expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]); + expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: receiverEth.toLowerCase()}]); + expect(await helper.rft.getTokenTop10Owners(collection.collectionId, tokenIdNonExist)).to.deep.eq([]); + + // 4.1 Tokens can be transferred: + await tokenEvmOwner.methods[testCase](isCross ? receiverCrossEth : receiverEth, 10).send({from: owner}); + await tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 10).send({from: receiverEth}); + expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: receiverEth.toLowerCase()}]); + expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]); + })); + + itEth('Can perform repartition()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.rft.mintCollection(alice); + const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner}); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + await contract.methods.repartition(200).send({from: owner}); + expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(200); + await contract.methods.transfer(receiver, 110).send({from: owner}); + expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(90); + expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(110); + + await expect(contract.methods.repartition(80).send({from: owner})).to.eventually.be.rejected; // Transaction is reverted + + await contract.methods.transfer(receiver, 90).send({from: owner}); + expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(0); + expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(200); + + await contract.methods.repartition(150).send({from: receiver}); + await expect(contract.methods.transfer(owner, 160).send({from: receiver})).to.eventually.be.rejected; // Transaction is reverted + expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(150); + }); + + itEth('Can repartition with increased amount', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.rft.mintCollection(alice); + const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner}); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + const result = await contract.methods.repartition(200).send(); + + const event = result.events.Transfer; + expect(event.address).to.be.equal(tokenAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(owner); + expect(event.returnValues.value).to.be.equal('100'); + }); + + itEth('Can repartition with decreased amount', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.rft.mintCollection(alice); + const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner}); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + const result = await contract.methods.repartition(50).send(); + const event = result.events.Transfer; + expect(event.address).to.be.equal(tokenAddress); + expect(event.returnValues.from).to.be.equal(owner); + expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.value).to.be.equal('50'); + }); + + itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Devastation', '6', '6'); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + + const result = await contract.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId); + const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, caller, true); + + await tokenContract.methods.repartition(2).send(); + await tokenContract.methods.transfer(receiver, 1).send(); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + await tokenContract.methods.burnFrom(caller, 1).send(); + + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.tokenId).to.be.equal(tokenId); + }); + + itEth('Can perform burnFromCross()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const ownerSub = (await helper.arrange.createAccounts([10n], donor))[0]; + const ownerCross = helper.ethCrossAccount.fromAddress(owner); + const spender = await helper.eth.createAccountWithBalance(donor); + + const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender); + const ownerSubCross = helper.ethCrossAccount.fromKeyringPair(ownerSub); + + const collection = await helper.rft.mintCollection(alice); + const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); + + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + { + await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner}); + + await expect(contract.methods.burnFromCross(ownerCross, 50).send({from: spender})).to.be.fulfilled; + await expect(contract.methods.burnFromCross(ownerCross, 100).send({from: spender})).to.be.rejected; + expect(await contract.methods.balanceOf(owner).call({from: owner})).to.be.equal('150'); + } + { + const {tokenId} = await collection.mintToken(alice, 200n, {Substrate: ownerSub.address}); + await collection.approveToken(ownerSub, tokenId, {Ethereum: spender}, 100n); + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + await expect(contract.methods.burnFromCross(ownerSubCross, 50).send({from: spender})).to.be.fulfilled; + await expect(contract.methods.burnFromCross(ownerSubCross, 100).send({from: spender})).to.be.rejected; + expect(await collection.getTokenBalance(tokenId, {Substrate: ownerSub.address})).to.be.equal(150n); + } + }); + + itEth('Check balanceOfCross()', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + const owner = await helper.ethCrossAccount.createAccountWithBalance(donor); + const other = await helper.ethCrossAccount.createAccountWithBalance(donor); + const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner.eth}); + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth); + + expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('200'); + expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0'); + + await tokenContract.methods.repartition(100n).send({from: owner.eth}); + expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100'); + expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0'); + + await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth}); + expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50'); + expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50'); + + await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth}); + expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0'); + expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100'); + + await tokenContract.methods.repartition(1000n).send({from: other.eth}); + await tokenContract.methods.transferCross(owner, 500n).send({from: other.eth}); + expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('500'); + expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('500'); + }); +}); + +describe('Refungible: Fees', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([50n], donor); + }); + }); + + itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + const collection = await helper.rft.mintCollection(alice); + const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner}); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner})); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + }); + + itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.rft.mintCollection(alice); + const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + await contract.methods.approve(spender, 100).send({from: owner}); + + const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender})); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + }); + + itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const collection = await helper.rft.mintCollection(alice); + const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner})); + expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); + }); +}); + +describe('Refungible: Substrate calls', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([50n], donor); + }); + }); + + itEth('Events emitted for approve()', async ({helper}) => { + const receiver = helper.eth.createAccount(); + const collection = await helper.rft.mintCollection(alice); + const token = await collection.mintToken(alice, 200n); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + expect(await token.approve(alice, {Ethereum: receiver}, 100n)).to.be.true; + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.event).to.be.equal('Approval'); + expect(event.address).to.be.equal(tokenAddress); + expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.spender).to.be.equal(receiver); + expect(event.returnValues.value).to.be.equal('100'); + }); + + itEth('Events emitted for transferFrom()', async ({helper}) => { + const [bob] = await helper.arrange.createAccounts([10n], donor); + const receiver = helper.eth.createAccount(); + const collection = await helper.rft.mintCollection(alice); + const token = await collection.mintToken(alice, 200n); + await token.approve(alice, {Substrate: bob.address}, 100n); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + expect(await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n)).to.be.true; + if(events.length == 0) await helper.wait.newBlocks(1); + + let event = events[0]; + expect(event.event).to.be.equal('Transfer'); + expect(event.address).to.be.equal(tokenAddress); + expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.value).to.be.equal('51'); + + event = events[1]; + expect(event.event).to.be.equal('Approval'); + expect(event.address).to.be.equal(tokenAddress); + expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address)); + expect(event.returnValues.value).to.be.equal('49'); + }); + + itEth('Events emitted for transfer()', async ({helper}) => { + const receiver = helper.eth.createAccount(); + const collection = await helper.rft.mintCollection(alice); + const token = await collection.mintToken(alice, 200n); + + const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId); + const contract = await helper.ethNativeContract.rftToken(tokenAddress); + + const events: any = []; + contract.events.allEvents((_: any, event: any) => { + events.push(event); + }); + + expect(await token.transfer(alice, {Ethereum: receiver}, 51n)).to.be.true; + if(events.length == 0) await helper.wait.newBlocks(1); + const event = events[0]; + + expect(event.event).to.be.equal('Transfer'); + expect(event.address).to.be.equal(tokenAddress); + expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); + expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.value).to.be.equal('51'); + }); +}); + +describe('ERC 1633 implementation', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Default parent token address and id', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sands', '', 'GRAIN'); + const collectionContract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + + const result = await collectionContract.methods.mint(owner).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + + const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId); + const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner); + + expect(await tokenContract.methods.parentToken().call()).to.be.equal(collectionAddress); + expect(await tokenContract.methods.parentTokenId().call()).to.be.equal(tokenId); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/scheduling.test.ts @@ -0,0 +1,64 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {expect} from 'chai'; +import {itSchedEth} from './util/index.js'; +import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; +import {Pallets, usingPlaygrounds} from '../util/index.js'; + +describe('Scheduing EVM smart contracts', () => { + + before(async () => { + await usingPlaygrounds(async (helper) => { + await helper.testUtils.enable(Pallets.TestUtils); + }); + }); + + itSchedEth.ifWithPallets('Successfully schedules and periodically executes an EVM contract', [Pallets.UniqueScheduler], async (scheduleKind, {helper, privateKey}) => { + const donor = await privateKey({url: import.meta.url}); + const [alice] = await helper.arrange.createAccounts([1000n], donor); + + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; + + const deployer = await helper.eth.createAccountWithBalance(alice); + const flipper = await helper.eth.deployFlipper(deployer); + + const initialValue = await flipper.methods.getValue().call(); + await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address)); + + const waitForBlocks = 4; + const periodic = { + period: 2, + repetitions: 2, + }; + + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) + .eth.sendEVM( + alice, + flipper.options.address, + flipper.methods.flip().encodeABI(), + '0', + ); + + expect(await flipper.methods.getValue().call()).to.be.equal(initialValue); + + await helper.wait.newBlocks(waitForBlocks + 1); + expect(await flipper.methods.getValue().call()).to.be.not.equal(initialValue); + + await helper.wait.newBlocks(periodic.period); + expect(await flipper.methods.getValue().call()).to.be.equal(initialValue); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/sponsoring.test.ts @@ -0,0 +1,97 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itEth, expect, SponsoringMode} from './util/index.js'; +import {usingPlaygrounds} from '../util/index.js'; + +describe('EVM sponsoring', () => { + let donor: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Fee is deducted from contract if sponsoring is enabled', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const caller = helper.eth.createAccount(); + const originalCallerBalance = await helper.balance.getEthereum(caller); + + expect(originalCallerBalance).to.be.equal(0n); + + const flipper = await helper.eth.deployFlipper(owner); + + const helpers = await helper.ethNativeContract.contractHelpers(owner); + + await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); + await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner}); + + await helpers.methods.setSponsor(flipper.options.address, sponsor).send({from: owner}); + await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); + + expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false; + await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); + expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true; + + const originalSponsorBalance = await helper.balance.getEthereum(sponsor); + expect(originalSponsorBalance).to.be.not.equal(0n); + + await flipper.methods.flip().send({from: caller}); + expect(await flipper.methods.getValue().call()).to.be.true; + + // Balance should be taken from flipper instead of caller + expect(await helper.balance.getEthereum(caller)).to.be.equal(originalCallerBalance); + expect(await helper.balance.getEthereum(sponsor)).to.be.not.equal(originalSponsorBalance); + }); + + itEth('...but this doesn\'t applies to payable value', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + const originalCallerBalance = await helper.balance.getEthereum(caller); + + expect(originalCallerBalance).to.be.not.equal(0n); + + const collector = await helper.eth.deployCollectorContract(owner); + + const helpers = await helper.ethNativeContract.contractHelpers(owner); + + await helpers.methods.toggleAllowlist(collector.options.address, true).send({from: owner}); + await helpers.methods.toggleAllowed(collector.options.address, caller, true).send({from: owner}); + + expect(await helpers.methods.sponsoringEnabled(collector.options.address).call()).to.be.false; + await helpers.methods.setSponsoringMode(collector.options.address, SponsoringMode.Allowlisted).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(collector.options.address, 0).send({from: owner}); + expect(await helpers.methods.sponsoringEnabled(collector.options.address).call()).to.be.true; + + await helpers.methods.setSponsor(collector.options.address, sponsor).send({from: owner}); + await helpers.methods.confirmSponsorship(collector.options.address).send({from: sponsor}); + + const originalSponsorBalance = await helper.balance.getEthereum(sponsor); + expect(originalSponsorBalance).to.be.not.equal(0n); + + await collector.methods.giveMoney().send({from: caller, value: '10000'}); + + // Balance will be taken from both caller (value) and from collector (fee) + expect(await helper.balance.getEthereum(caller)).to.be.equals((originalCallerBalance - 10000n)); + expect(await helper.balance.getEthereum(sponsor)).to.be.not.equals(originalSponsorBalance); + expect(await collector.methods.getCollected().call()).to.be.equal('10000'); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/tokenProperties.test.ts @@ -0,0 +1,625 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {Contract} from 'web3-eth-contract'; +import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; +import type {ITokenPropertyPermission} from '@unique/playgrounds/types.js'; +import {Pallets} from '../util/index.js'; +import {UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection} from '@unique/playgrounds/unique.js'; +import {CreateCollectionData, TokenPermissionField} from './util/playgrounds/types.js'; + +describe('EVM token properties', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([1000n], donor); + }); + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.ethCrossAccount.createAccountWithBalance(donor); + for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) { + const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + await collection.methods.addCollectionAdminCross(caller).send({from: owner}); + + await collection.methods.setTokenPropertyPermissions([ + ['testKey', [ + [TokenPermissionField.Mutable, mutable], + [TokenPermissionField.TokenOwner, tokenOwner], + [TokenPermissionField.CollectionAdmin, collectionAdmin]], + ], + ]).send({from: caller.eth}); + + expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{ + key: 'testKey', + permission: {mutable, collectionAdmin, tokenOwner}, + }]); + + expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([ + ['testKey', [ + [TokenPermissionField.Mutable.toString(), mutable], + [TokenPermissionField.TokenOwner.toString(), tokenOwner], + [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]], + ], + ]); + } + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as owner`, testCase.requiredPallets, async({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + + await collection.methods.setTokenPropertyPermissions([ + ['testKey_0', [ + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], + ], + ['testKey_1', [ + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, false], + [TokenPermissionField.CollectionAdmin, true]], + ], + ['testKey_2', [ + [TokenPermissionField.Mutable, false], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, false]], + ], + ]).send({from: owner}); + + expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([ + { + key: 'testKey_0', + permission: {mutable: true, tokenOwner: true, collectionAdmin: true}, + }, + { + key: 'testKey_1', + permission: {mutable: true, tokenOwner: false, collectionAdmin: true}, + }, + { + key: 'testKey_2', + permission: {mutable: false, tokenOwner: true, collectionAdmin: false}, + }, + ]); + + expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([ + ['testKey_0', [ + [TokenPermissionField.Mutable.toString(), true], + [TokenPermissionField.TokenOwner.toString(), true], + [TokenPermissionField.CollectionAdmin.toString(), true]], + ], + ['testKey_1', [ + [TokenPermissionField.Mutable.toString(), true], + [TokenPermissionField.TokenOwner.toString(), false], + [TokenPermissionField.CollectionAdmin.toString(), true]], + ], + ['testKey_2', [ + [TokenPermissionField.Mutable.toString(), false], + [TokenPermissionField.TokenOwner.toString(), true], + [TokenPermissionField.CollectionAdmin.toString(), false]], + ], + ]); + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as admin`, testCase.requiredPallets, async({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.ethCrossAccount.createAccountWithBalance(donor); + + const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + await collection.methods.addCollectionAdminCross(caller).send({from: owner}); + + await collection.methods.setTokenPropertyPermissions([ + ['testKey_0', [ + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], + ], + ['testKey_1', [ + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, false], + [TokenPermissionField.CollectionAdmin, true]], + ], + ['testKey_2', [ + [TokenPermissionField.Mutable, false], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, false]], + ], + ]).send({from: caller.eth}); + + expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([ + { + key: 'testKey_0', + permission: {mutable: true, tokenOwner: true, collectionAdmin: true}, + }, + { + key: 'testKey_1', + permission: {mutable: true, tokenOwner: false, collectionAdmin: true}, + }, + { + key: 'testKey_2', + permission: {mutable: false, tokenOwner: true, collectionAdmin: false}, + }, + ]); + + expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([ + ['testKey_0', [ + [TokenPermissionField.Mutable.toString(), true], + [TokenPermissionField.TokenOwner.toString(), true], + [TokenPermissionField.CollectionAdmin.toString(), true]], + ], + ['testKey_1', [ + [TokenPermissionField.Mutable.toString(), true], + [TokenPermissionField.TokenOwner.toString(), false], + [TokenPermissionField.CollectionAdmin.toString(), true]], + ], + ['testKey_2', [ + [TokenPermissionField.Mutable.toString(), false], + [TokenPermissionField.TokenOwner.toString(), true], + [TokenPermissionField.CollectionAdmin.toString(), false]], + ], + ]); + + })); + + [ + { + method: 'setProperties', + methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], + expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}], + }, + { + method: 'setProperty' /*Soft-deprecated*/, + methodParams: ['testKey1', Buffer.from('testValue1')], + expectedProps: [{key: 'testKey1', value: 'testValue1'}], + }, + ].map(testCase => + itEth(`[${testCase.method}] Can be set`, async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.nft.mintCollection(alice, { + tokenPropertyPermissions: [{ + key: 'testKey1', + permission: { + collectionAdmin: true, + }, + }, { + key: 'testKey2', + permission: { + collectionAdmin: true, + }, + }], + }); + + await collection.addAdmin(alice, {Ethereum: caller}); + const token = await collection.mintToken(alice); + + const collectionEvm = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller, testCase.method === 'setProperty'); + + await collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller}); + + const properties = await token.getProperties(); + expect(properties).to.deep.equal(testCase.expectedProps); + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`Can be multiple set/read for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + + const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)})); + const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {tokenOwner: true, + collectionAdmin: true, + mutable: true}})); + + const collection = await helper[testCase.mode].mintCollection(alice, { + tokenPrefix: 'ethp', + tokenPropertyPermissions: permissions, + }) as UniqueNFTCollection | UniqueRFTCollection; + + const token = await collection.mintToken(alice); + + const valuesBefore = await token.getProperties(properties.map(p => p.key)); + expect(valuesBefore).to.be.deep.equal([]); + + + await collection.addAdmin(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller); + + expect(await contract.methods.properties(token.tokenId, []).call()).to.be.deep.equal([]); + + await contract.methods.setProperties(token.tokenId, properties).send({from: caller}); + + const values = await token.getProperties(properties.map(p => p.key)); + expect(values).to.be.deep.equal(properties.map(p => ({key: p.key, value: p.value.toString()}))); + + expect(await contract.methods.properties(token.tokenId, []).call()).to.be.like(properties + .map(p => helper.ethProperty.property(p.key, p.value.toString()))); + + expect(await contract.methods.properties(token.tokenId, [properties[0].key]).call()) + .to.be.like([helper.ethProperty.property(properties[0].key, properties[0].value.toString())]); + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`Can be deleted for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const collection = await helper[testCase.mode].mintCollection(alice, { + tokenPropertyPermissions: [{ + key: 'testKey', + permission: { + mutable: true, + collectionAdmin: true, + }, + }, + { + key: 'testKey_1', + permission: { + mutable: true, + collectionAdmin: true, + }, + }], + }); + + const token = await collection.mintToken(alice); + await token.setProperties(alice, [{key: 'testKey', value: 'testValue'}, {key: 'testKey_1', value: 'testValue_1'}]); + expect(await token.getProperties()).to.has.length(2); + + await collection.addAdmin(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller); + + await contract.methods.deleteProperties(token.tokenId, ['testKey', 'testKey_1']).send({from: caller}); + + const result = await token.getProperties(['testKey', 'testKey_1']); + expect(result.length).to.equal(0); + })); + + itEth('Can be read', async({helper}) => { + const caller = helper.eth.createAccount(); + const collection = await helper.nft.mintCollection(alice, { + tokenPropertyPermissions: [{ + key: 'testKey', + permission: { + collectionAdmin: true, + }, + }], + }); + + const token = await collection.mintToken(alice); + await token.setProperties(alice, [{key: 'testKey', value: 'testValue'}]); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, 'nft', caller); + + const value = await contract.methods.property(token.tokenId, 'testKey').call(); + expect(value).to.equal(helper.getWeb3().utils.toHex('testValue')); + }); +}); + +describe('EVM token properties negative', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let caller: string; + let aliceCollection: UniqueNFTCollection; + let token: UniqueNFToken; + const tokenProps = [{key: 'testKey_1', value: 'testValue_1'}, {key: 'testKey_2', value: 'testValue_2'}]; + let collectionEvm: Contract; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([100n], donor); + }); + }); + + beforeEach(async () => { + // 1. create collection with props: testKey_1, testKey_2 + // 2. create token and set props testKey_1, testKey_2 + await usingEthPlaygrounds(async (helper) => { + aliceCollection = await helper.nft.mintCollection(alice, { + tokenPropertyPermissions: [{ + key: 'testKey_1', + permission: { + mutable: true, + collectionAdmin: true, + }, + }, + { + key: 'testKey_2', + permission: { + mutable: true, + collectionAdmin: true, + }, + }], + }); + token = await aliceCollection.mintToken(alice); + await token.setProperties(alice, tokenProps); + collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true); + }); + }); + + [ + {method: 'setProperty', methodParams: [tokenProps[1].key, Buffer.from('newValue')]}, + {method: 'setProperties', methodParams: [[{key: tokenProps[1].key, value: Buffer.from('newValue')}]]}, + ].map(testCase => + itEth(`[${testCase.method}] Cannot set properties of non-owned collection`, async ({helper}) => { + caller = await helper.eth.createAccountWithBalance(donor); + collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true); + // Caller not an owner and not an admin, so he cannot set properties: + await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission'); + await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected; + + // Props have not changed: + const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString())); + const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call(); + expect(actualProps).to.deep.eq(expectedProps); + })); + + [ + {method: 'setProperty', methodParams: ['testKey_3', Buffer.from('testValue3')]}, + {method: 'setProperties', methodParams: [[{key: 'testKey_3', value: Buffer.from('testValue3')}]]}, + ].map(testCase => + itEth(`[${testCase.method}] Cannot set non-existing properties`, async ({helper}) => { + caller = await helper.eth.createAccountWithBalance(donor); + collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true); + await helper.collection.addAdmin(alice, aliceCollection.collectionId, {Ethereum: caller}); + + await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission'); + await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected; + + // Props have not changed: + const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString())); + const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call(); + expect(actualProps).to.deep.eq(expectedProps); + })); + + [ + {method: 'deleteProperty', methodParams: ['testKey_2']}, + {method: 'deleteProperties', methodParams: [['testKey_2']]}, + ].map(testCase => + itEth(`[${testCase.method}] Cannot delete properties of non-owned collection`, async ({helper}) => { + caller = await helper.eth.createAccountWithBalance(donor); + collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, testCase.method == 'deleteProperty'); + // Caller not an owner and not an admin, so he cannot set properties: + await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission'); + await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected; + + // Props have not changed: + const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString())); + const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call(); + expect(actualProps).to.deep.eq(expectedProps); + })); + + [ + {method: 'deleteProperty', methodParams: ['testKey_3']}, + {method: 'deleteProperties', methodParams: [['testKey_3']]}, + ].map(testCase => + itEth(`[${testCase.method}] Cannot delete non-existing properties`, async ({helper}) => { + caller = await helper.eth.createAccountWithBalance(donor); + collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, testCase.method == 'deleteProperty'); + await helper.collection.addAdmin(alice, aliceCollection.collectionId, {Ethereum: caller}); + // Caller cannot delete non-existing properties: + await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission'); + await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected; + // Props have not changed: + const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString())); + const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call(); + expect(actualProps).to.deep.eq(expectedProps); + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions as non owner or admin`, testCase.requiredPallets, async({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const caller = await helper.eth.createAccountWithBalance(donor); + + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + + await expect(collection.methods.setTokenPropertyPermissions([ + ['testKey_0', [ + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], + ], + ]).call({from: caller})).to.be.rejectedWith('NoPermission'); + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions with invalid character`, testCase.requiredPallets, async({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + + await expect(collection.methods.setTokenPropertyPermissions([ + // "Space" is invalid character + ['testKey 0', [ + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], + ], + ]).call({from: owner})).to.be.rejectedWith('InvalidCharacterInPropertyKey'); + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`[${testCase.mode}] Can reconfigure token property permissions to stricter ones`, testCase.requiredPallets, async({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + + // 1. Owner sets strict property-permissions: + await collection.methods.setTokenPropertyPermissions([ + ['testKey', [ + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], + ], + ]).send({from: owner}); + + // 2. Owner can set stricter property-permissions: + for(const values of [[true, true, false], [true, false, false], [false, false, false]]) { + await collection.methods.setTokenPropertyPermissions([ + ['testKey', [ + [TokenPermissionField.Mutable, values[0]], + [TokenPermissionField.TokenOwner, values[1]], + [TokenPermissionField.CollectionAdmin, values[2]]], + ], + ]).send({from: owner}); + } + + expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{ + key: 'testKey', + permission: {mutable: false, collectionAdmin: false, tokenOwner: false}, + }]); + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`[${testCase.mode}] Cannot reconfigure token property permissions to less strict ones`, testCase.requiredPallets, async({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); + const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + + // 1. Owner sets strict property-permissions: + await collection.methods.setTokenPropertyPermissions([ + ['testKey', [ + [TokenPermissionField.Mutable, false], + [TokenPermissionField.TokenOwner, false], + [TokenPermissionField.CollectionAdmin, false]], + ], + ]).send({from: owner}); + + // 2. Owner cannot set less strict property-permissions: + for(const values of [[true, false, false], [false, true, false], [false, false, true]]) { + await expect(collection.methods.setTokenPropertyPermissions([ + ['testKey', [ + [TokenPermissionField.Mutable, values[0]], + [TokenPermissionField.TokenOwner, values[1]], + [TokenPermissionField.CollectionAdmin, values[2]]], + ], + ]).call({from: owner})).to.be.rejectedWith('NoPermission'); + } + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`[${testCase.mode}] Can't be multiple set/read for non-existent token`, testCase.requiredPallets, async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + + const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)})); + const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {tokenOwner: true, + collectionAdmin: true, + mutable: true}})); + + const collection = await helper[testCase.mode].mintCollection(alice, { + tokenPrefix: 'ethp', + tokenPropertyPermissions: permissions, + }) as UniqueNFTCollection | UniqueRFTCollection; + + await collection.addAdmin(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller); + + await expect(contract.methods.setProperties(1, properties).call({from: caller})).to.be.rejectedWith('TokenNotFound'); + })); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`[${testCase.mode}] Can't be deleted for non-existent token`, testCase.requiredPallets, async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const collection = await helper[testCase.mode].mintCollection(alice, { + tokenPropertyPermissions: [{ + key: 'testKey', + permission: { + mutable: true, + collectionAdmin: true, + }, + }, + { + key: 'testKey_1', + permission: { + mutable: true, + collectionAdmin: true, + }, + }], + }); + + + await collection.addAdmin(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller); + + await expect(contract.methods.deleteProperties(1, ['testKey', 'testKey_1']).call({from: caller})).to.be.rejectedWith('TokenNotFound'); + })); +}); + + +type ElementOf = A extends readonly (infer T)[] ? T : never; +function* cartesian>, R extends Array>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf}]> { + if(args.length === 0) { + yield internalRest as any; + return; + } + for(const value of args[0]) { + yield* cartesian([...internalRest, value], ...args.slice(1)) as any; + } +} --- /dev/null +++ b/js-packages/tests/eth/tokens/callMethodsERC20.test.ts @@ -0,0 +1,92 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {Pallets, requirePalletsOrSkip} from '../../util/index.js'; +import {expect, itEth, usingEthPlaygrounds} from '../util/index.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {CreateCollectionData} from '../util/playgrounds/types.js'; + +[ + {mode: 'ft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, +].map(testCase => { + describe(`${testCase.mode.toUpperCase()}: ERC-20 call methods`, () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, testCase.requiredPallets); + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('totalSupply', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller]; + + const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('TotalSupply', '6', '6', testCase.mode)).send(); + if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller}); + + // Use collection contract for FT or token contract for RFT: + const contract = testCase.mode === 'ft' + ? collection + : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller); + + // Mint tokens: + testCase.mode === 'ft' + ? await contract.methods.mint(...mintingParams).send({from: caller}) + : await contract.methods.repartition(200).send({from: caller}); + + const totalSupply = await contract.methods.totalSupply().call(); + expect(totalSupply).to.equal('200'); + }); + + itEth('balanceOf', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller]; + + const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send(); + if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller}); + + // Use collection contract for FT or token contract for RFT: + const contract = testCase.mode === 'ft' + ? collection + : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller); + + // Mint tokens: + testCase.mode === 'ft' + ? await contract.methods.mint(...mintingParams).send({from: caller}) + : await contract.methods.repartition(200).send({from: caller}); + + const balance = await contract.methods.balanceOf(caller).call(); + expect(balance).to.equal('200'); + }); + + itEth('decimals', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send(); + if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller}); + + // Use collection contract for FT or token contract for RFT: + const contract = testCase.mode === 'ft' + ? collection + : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller); + + const decimals = await contract.methods.decimals().call(); + expect(decimals).to.equal(testCase.mode === 'rft' ? '0' : '18'); + }); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/tokens/callMethodsERC721.test.ts @@ -0,0 +1,144 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {Pallets} from '../../util/index.js'; +import {expect, itEth, usingEthPlaygrounds} from '../util/index.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {CreateCollectionData} from '../util/playgrounds/types.js'; + + +describe('ERC-721 call methods', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + [ + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {mode: 'nft' as const, requiredPallets: []}, + ].map(testCase => { + itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: name/symbol/description`, testCase.requiredPallets, async ({helper}) => { + const callerEth = await helper.eth.createAccountWithBalance(donor); + const [callerSub] = await helper.arrange.createAccounts([100n], donor); + const [name, description, tokenPrefix] = ['Name', 'Description', 'Symbol']; + + const {collection: collectionEth} = await helper.eth.createCollection(callerEth, new CreateCollectionData(name, description, tokenPrefix, testCase.mode)).send(); + await collectionEth.methods.mint(callerEth).send({from: callerEth}); + const {collectionId} = await helper[testCase.mode].mintCollection(callerSub, {name, description, tokenPrefix}); + const collectionSub = await helper.ethNativeContract.collectionById(collectionId, testCase.mode, callerEth); + + // Can get name/symbol/description for Eth collection + expect(await collectionEth.methods.name().call()).to.eq(name); + expect(await collectionEth.methods.symbol().call()).to.eq(tokenPrefix); + expect(await collectionEth.methods.description().call()).to.eq(description); + // Can get name/symbol/description for Sub collection + expect(await collectionSub.methods.name().call()).to.eq(name); + expect(await collectionSub.methods.symbol().call()).to.eq(tokenPrefix); + expect(await collectionSub.methods.description().call()).to.eq(description); + }); + }); + + [ + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {mode: 'nft' as const, requiredPallets: []}, + ].map(testCase => { + itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: totalSupply`, testCase.requiredPallets, async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + + const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('TotalSupply', '6', '6', testCase.mode)).send(); + await collection.methods.mint(caller).send({from: caller}); + + const totalSupply = await collection.methods.totalSupply().call(); + expect(totalSupply).to.equal('1'); + }); + }); + + [ + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {mode: 'nft' as const, requiredPallets: []}, + ].map(testCase => { + itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: balanceOf`, testCase.requiredPallets, async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + + const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send(); + await collection.methods.mint(caller).send({from: caller}); + await collection.methods.mint(caller).send({from: caller}); + await collection.methods.mint(caller).send({from: caller}); + + const balance = await collection.methods.balanceOf(caller).call(); + expect(balance).to.equal('3'); + }); + }); + + [ + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {mode: 'nft' as const, requiredPallets: []}, + ].map(testCase => { + itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: ownerOf`, testCase.requiredPallets, async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('OwnerOf', '6', '6', testCase.mode)).send(); + + const result = await collection.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + + const owner = await collection.methods.ownerOf(tokenId).call(); + expect(owner).to.equal(caller); + }); + }); + + [ + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + // TODO {mode: 'nft' as const, requiredPallets: []}, + ].map(testCase => { + itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: ownerOf after burn`, testCase.requiredPallets, async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('OwnerOf-AfterBurn', '6', '6', testCase.mode)).send(); + + const result = await collection.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller, true); + + await tokenContract.methods.repartition(2).send(); + await tokenContract.methods.transfer(receiver, 1).send(); + + await tokenContract.methods.burnFrom(caller, 1).send(); + + const owner = await collection.methods.ownerOf(tokenId).call(); + expect(owner).to.equal(receiver); + }); + }); + + itEth.ifWithPallets('RFT: ownerOf for partial ownership', [Pallets.ReFungible], async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Partial-OwnerOf', '6', '6'); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + + const result = await contract.methods.mint(caller).send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller); + + await tokenContract.methods.repartition(2).send(); + await tokenContract.methods.transfer(receiver, 1).send(); + + const owner = await contract.methods.ownerOf(tokenId).call(); + expect(owner).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/tokens/minting.test.ts @@ -0,0 +1,168 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {Pallets} from '../../util/index.js'; +import {expect, itEth, usingEthPlaygrounds} from '../util/index.js'; +import {CreateCollectionData} from '../util/playgrounds/types.js'; + + +describe('Minting tokens', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([30n, 20n], donor); + }); + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {mode: 'ft' as const, requiredPallets: []}, + ].map(testCase => { + itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Substrate collection`, testCase.requiredPallets, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver]; + + const collection = await helper[testCase.mode].mintCollection(alice); + await collection.addAdmin(alice, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + + const result = await contract.methods.mint(...mintingParams).send({from: owner}); + + // Check events: + const event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal(receiver); + if(testCase.mode === 'ft') + expect(event.returnValues.value).to.equal('100'); + + // Check token exist: + if(testCase.mode === 'ft') { + expect(await helper.ft.getBalance(collection.collectionId, {Ethereum: receiver})).to.eq(100n); + } else { + const tokenId = event.returnValues.tokenId; + expect(tokenId).to.be.equal('1'); + expect(await helper.collection.getLastTokenId(collection.collectionId)).to.eq(1); + expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']); + } + }); + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {mode: 'ft' as const, requiredPallets: []}, + ].map(testCase => { + itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver]; + + const {collection, collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Name', 'Desc', 'Prefix', testCase.mode)).send(); + + const result = await collection.methods.mint(...mintingParams).send({from: owner}); + + // Check events: + const event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal(receiver); + if(testCase.mode === 'ft') + expect(event.returnValues.value).to.equal('100'); + + // Check token exist: + if(testCase.mode === 'ft') { + expect(await helper.ft.getBalance(collectionId, {Ethereum: receiver})).to.eq(100n); + } else { + const tokenId = event.returnValues.tokenId; + expect(tokenId).to.be.equal('1'); + expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1); + expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']); + } + }); + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + {mode: 'ft' as const, requiredPallets: []}, + ].map(testCase => { + itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver]; + + const {collection, collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Name', 'Desc', 'Prefix', testCase.mode)).send(); + + const result = await collection.methods.mint(...mintingParams).send({from: owner}); + + // Check events: + const event = result.events.Transfer; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal(receiver); + if(testCase.mode === 'ft') + expect(event.returnValues.value).to.equal('100'); + + // Check token exist: + if(testCase.mode === 'ft') { + expect(await helper.ft.getBalance(collectionId, {Ethereum: receiver})).to.eq(100n); + } else { + const tokenId = event.returnValues.tokenId; + expect(tokenId).to.be.equal('1'); + expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1); + expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']); + } + }); + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => { + itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mintWithTokenURI() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', ''); + const contract = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); + + const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send(); + const tokenId = result.events.Transfer.returnValues.tokenId; + expect(tokenId).to.be.equal('1'); + + const event = result.events.Transfer; + expect(event.address).to.be.equal(collectionAddress); + expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.be.equal(receiver); + + expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); + expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']); + // TODO: this wont work right now, need release 919000 first + // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send(); + // const tokenUri = await contract.methods.tokenURI(nextTokenId).call(); + // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`); + }); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/transferValue.test.ts @@ -0,0 +1,64 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itEth, usingEthPlaygrounds} from './util/index.js'; +import {expect} from 'chai'; + +describe('Send value to contract', () => { + let donor: IKeyringPair; + + before(async () => { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Send to and from contract', async ({helper}) => { + const contractOwner = await helper.eth.createAccountWithBalance(donor, 600n); + const [buyer, receiver] = await helper.arrange.createAccounts([600n, 600n], donor); + const receiverMirror = helper.address.substrateToEth(receiver.address); + const contract = await helper.ethContract.deployByCode( + contractOwner, + 'Test', + ` + // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.18; + + contract Test { + function send() public payable { + if (msg.value < 1000000000000000000) + revert("Not enough gold"); + } + + function withdraw(address to) public { + payable(to).transfer(1000000000000000000); + } + } + `, + ); + + const balanceBefore = await helper.balance.getSubstrate(buyer.address); + await helper.eth.sendEVM(buyer, contract.options.address, contract.methods.send().encodeABI(), '2000000000000000000'); + const balanceAfter = await helper.balance.getSubstrate(buyer.address); + expect(balanceBefore - balanceAfter > 2000000000000000000n).to.be.true; + expect(await helper.balance.getEthereum(contract.options.address)).to.be.equal(2000000000000000000n); + + await helper.eth.sendEVM(buyer, contract.options.address, contract.methods.withdraw(receiverMirror).encodeABI(), '0'); + expect(await helper.balance.getEthereum(receiverMirror)).to.be.equal(1000000000000000000n); + expect(await helper.balance.getEthereum(contract.options.address)).to.be.equal(1000000000000000000n); + }); +}); --- /dev/null +++ b/js-packages/tests/eth/util/index.ts @@ -0,0 +1,107 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +import * as path from 'path'; +import type {IKeyringPair} from '@polkadot/types/types'; + +import config from '../../config.js'; + +import {EthUniqueHelper} from './playgrounds/unique.dev.js'; +import {SilentLogger, SilentConsole} from '@unique/playgrounds/unique.dev.js'; +import {makeNames} from '../../util/index.js'; +import type {SchedKind} from '../../util/index.js'; + +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiLike from 'chai-like'; +import {getTestSeed, MINIMUM_DONOR_FUND, requirePalletsOrSkip} from '../../util/index.js'; + +chai.use(chaiAsPromised); +chai.use(chaiLike); +export const expect = chai.expect; + +export enum SponsoringMode { + Disabled = 0, + Allowlisted = 1, + Generous = 2, +} + +type PrivateKeyFn = (seed: string | {filename?: string, url?: string}) => Promise; + +export const usingEthPlaygrounds = async (code: (helper: EthUniqueHelper, privateKey: PrivateKeyFn) => Promise) => { + const silentConsole = new SilentConsole(); + silentConsole.enable(); + + const helper = new EthUniqueHelper(new SilentLogger()); + + try { + await helper.connect(config.substrateUrl); + await helper.connectWeb3(config.substrateUrl); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const privateKey: PrivateKeyFn = async (seed) => { + if(typeof seed === 'string') { + return helper.util.fromSeed(seed, ss58Format); + } + if(seed.url) { + const {filename} = makeNames(seed.url); + seed.filename = filename; + } else if(seed.filename) { + // Pass + } else { + throw new Error('no url nor filename set'); + } + const actualSeed = getTestSeed(seed.filename); + let account = helper.util.fromSeed(actualSeed, ss58Format); + if(await helper.balance.getSubstrate(account.address) < MINIMUM_DONOR_FUND) { + console.warn(`${path.basename(seed.filename)}: Not enough funds present on the filename account. Using the default one as the donor instead.`); + account = helper.util.fromSeed('//Alice', ss58Format); + } + return account; + }; + await code(helper, privateKey); + } + finally { + await helper.disconnect(); + silentConsole.disable(); + } +}; + +export function itEth(name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) { + (opts.only ? it.only : + opts.skip ? it.skip : it)(name, async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + if(opts.requiredPallets) { + requirePalletsOrSkip(this, helper, opts.requiredPallets); + } + + await cb({helper, privateKey}); + }); + }); +} + +export function itEthIfWithPallet(name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) { + return itEth(name, cb, {requiredPallets: required, ...opts}); +} + +itEth.only = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEth(name, cb, {only: true}); +itEth.skip = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any) => itEth(name, cb, {skip: true}); + +itEthIfWithPallet.only = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEthIfWithPallet(name, required, cb, {only: true}); +itEthIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEthIfWithPallet(name, required, cb, {skip: true}); +itEth.ifWithPallets = itEthIfWithPallet; + +export function itSchedEth( + name: string, + cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, + opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}, +) { + itEth(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts); + itEth(name + ' (named scheduling)', (apis) => cb('named', apis), opts); +} +itSchedEth.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any) => itSchedEth(name, cb, {only: true}); +itSchedEth.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any) => itSchedEth(name, cb, {skip: true}); +itSchedEth.ifWithPallets = itSchedIfWithPallets; + +function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) { + return itSchedEth(name, cb, {requiredPallets: required, ...opts}); +} --- /dev/null +++ b/js-packages/tests/eth/util/playgrounds/types.ts @@ -0,0 +1,133 @@ +import {CollectionFlag} from '@unique/playgrounds/types.js'; +import type {TCollectionMode} from '@unique/playgrounds/types.js'; + +export interface ContractImports { + solPath: string; + fsPath: string; +} + +export interface CompiledContract { + abi: any; + object: string; +} + +export type NormalizedEvent = { + address: string, + event: string, + args: { [key: string]: string } +}; + +export interface OptionUint { + status: boolean, + value: bigint, +} + +export type EthAddress = string; + +export interface CrossAddress { + readonly eth: EthAddress, + readonly sub: string | Uint8Array, +} + +export type EthProperty = string[]; + +export enum TokenPermissionField { + Mutable, + TokenOwner, + CollectionAdmin +} + +export enum CollectionLimitField { + AccountTokenOwnership, + SponsoredDataSize, + SponsoredDataRateLimit, + TokenLimit, + SponsorTransferTimeout, + SponsorApproveTimeout, + OwnerCanTransfer, + OwnerCanDestroy, + TransferEnabled +} + +export interface CollectionLimit { + field: CollectionLimitField, + value: OptionUint, +} + +export interface CollectionLimitValue { + field: CollectionLimitField, + value: bigint, +} + +export enum CollectionMode { + Nonfungible, + Fungible, + Refungible, +} + +export interface PropertyPermission { + code: TokenPermissionField, + value: boolean, +} +export interface TokenPropertyPermission { + key: string, + permissions: PropertyPermission[], +} +export interface CollectionNestingAndPermission { + token_owner: boolean, + collection_admin: boolean, + restricted: string[], +} + +export const emptyAddress: [string, string] = [ + '0x0000000000000000000000000000000000000000', + '0', +]; + +export const CREATE_COLLECTION_DATA_DEFAULTS = { + decimals: 0, + properties: [], + tokenPropertyPermissions: [], + adminList: [], + nestingSettings: {token_owner: false, collection_admin: false, restricted: []}, + limits: [], + pendingSponsor: emptyAddress, + flags: 0, +}; + +export interface Property { + key: string; + value: Buffer; +} + +export class CreateCollectionData { + name: string; + description: string; + tokenPrefix: string; + collectionMode: TCollectionMode; + decimals? = 0; + properties?: Property[] = []; + tokenPropertyPermissions?: TokenPropertyPermission[] = []; + adminList?: CrossAddress[] = []; + nestingSettings?: CollectionNestingAndPermission = {token_owner: false, collection_admin: false, restricted: []}; + limits?: CollectionLimitValue[] = []; + pendingSponsor?: [string, string] = emptyAddress; + flags?: number | CollectionFlag[] = [0]; + + constructor( + name: string, + description: string, + tokenPrefix: string, + collectionMode: TCollectionMode, + decimals = 18, + ) { + this.name = name; + this.description = description; + this.tokenPrefix = tokenPrefix; + this.collectionMode = collectionMode; + if(collectionMode == 'ft') + this.decimals = decimals; + else + this.decimals = 0; + } +} --- /dev/null +++ b/js-packages/tests/eth/util/playgrounds/unique.dev.ts @@ -0,0 +1,616 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +/* eslint-disable function-call-argument-newline */ + +import {readFile} from 'fs/promises'; + +import * as web3 from 'web3'; +import {WebsocketProvider} from 'web3-core'; +import {Contract} from 'web3-eth-contract'; + +// @ts-ignore +import solc from 'solc'; + +import {evmToAddress} from '@polkadot/util-crypto'; +import type {IKeyringPair} from '@polkadot/types/types'; + +import {ArrangeGroup, DevUniqueHelper} from '@unique/playgrounds/unique.dev.js'; + +import type {ContractImports, CompiledContract, CrossAddress, NormalizedEvent, EthProperty} from './types.js'; +import {CollectionMode, CreateCollectionData} from './types.js'; + +// Native contracts ABI +import collectionHelpersAbi from '../../abi/collectionHelpers.json' assert {type: 'json'}; +import nativeFungibleAbi from '../../abi/nativeFungible.json' assert {type: 'json'}; +import fungibleAbi from '../../abi/fungible.json' assert {type: 'json'}; +import fungibleDeprecatedAbi from '../../abi/fungibleDeprecated.json' assert {type: 'json'}; +import nonFungibleAbi from '../../abi/nonFungible.json' assert {type: 'json'}; +import nonFungibleDeprecatedAbi from '../../abi/nonFungibleDeprecated.json' assert {type: 'json'}; +import refungibleAbi from '../../abi/reFungible.json' assert {type: 'json'}; +import refungibleDeprecatedAbi from '../../abi/reFungibleDeprecated.json' assert {type: 'json'}; +import refungibleTokenAbi from '../../abi/reFungibleToken.json' assert {type: 'json'}; +import refungibleTokenDeprecatedAbi from '../../abi/reFungibleTokenDeprecated.json' assert {type: 'json'}; +import contractHelpersAbi from '../../abi/contractHelpers.json' assert {type: 'json'}; +import type {ICrossAccountId, TEthereumAccount, TCollectionMode} from '@unique/playgrounds/types.js'; + +class EthGroupBase { + helper: EthUniqueHelper; + gasPrice?: string; + + constructor(helper: EthUniqueHelper) { + this.helper = helper; + } + async getGasPrice() { + if(this.gasPrice) + return this.gasPrice; + this.gasPrice = await this.helper.getWeb3().eth.getGasPrice(); + return this.gasPrice; + } +} + +class ContractGroup extends EthGroupBase { + async findImports(imports?: ContractImports[]) { + if(!imports) return function(path: string) { + return {error: `File not found: ${path}`}; + }; + + const knownImports = {} as { [key: string]: string }; + for(const imp of imports) { + knownImports[imp.solPath] = (await readFile(imp.fsPath)).toString(); + } + + return function(path: string) { + if(path in knownImports) return {contents: knownImports[path]}; + return {error: `File not found: ${path}`}; + }; + } + + async compile(name: string, src: string, imports?: ContractImports[]): Promise { + const compiled = JSON.parse(solc.compile(JSON.stringify({ + language: 'Solidity', + sources: { + [`${name}.sol`]: { + content: src, + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['*'], + }, + }, + }, + }), {import: await this.findImports(imports)})); + + const hasErrors = compiled['errors'] + && compiled['errors'].length > 0 + && compiled.errors.some(function(err: any) { + return err.severity == 'error'; + }); + + if(hasErrors) { + throw compiled.errors; + } + const out = compiled.contracts[`${name}.sol`][name]; + + return { + abi: out.abi, + object: '0x' + out.evm.bytecode.object, + }; + } + + async deployByCode(signer: string, name: string, src: string, imports?: ContractImports[], gas?: number, args?: any[]): Promise { + const compiledContract = await this.compile(name, src, imports); + return this.deployByAbi(signer, compiledContract.abi, compiledContract.object, gas, args); + } + + async deployByAbi(signer: string, abi: any, object: string, gas?: number, args?: any[]): Promise { + const web3 = this.helper.getWeb3(); + const contract = new web3.eth.Contract(abi, undefined, { + data: object, + from: signer, + gas: gas ?? this.helper.eth.DEFAULT_GAS, + }); + return await contract.deploy({data: object, arguments: args}).send({from: signer}); + } + +} + +class NativeContractGroup extends EthGroupBase { + + contractHelpers(caller: string) { + const web3 = this.helper.getWeb3(); + return new web3.eth.Contract(contractHelpersAbi as any, this.helper.getApi().consts.evmContractHelpers.contractAddress.toString(), { + from: caller, + gas: this.helper.eth.DEFAULT_GAS, + }); + } + + collectionHelpers(caller: string) { + const web3 = this.helper.getWeb3(); + return new web3.eth.Contract(collectionHelpersAbi as any, this.helper.getApi().consts.common.contractAddress.toString(), { + from: caller, + gas: this.helper.eth.DEFAULT_GAS, + }); + } + + collection(address: string, mode: TCollectionMode, caller?: string, mergeDeprecated = false) { + let abi; + if(address === this.helper.ethAddress.fromCollectionId(0)) { + abi = nativeFungibleAbi; + } else { + abi ={ + 'nft': nonFungibleAbi, + 'rft': refungibleAbi, + 'ft': fungibleAbi, + }[mode]; + } + if(mergeDeprecated) { + const deprecated = { + 'nft': nonFungibleDeprecatedAbi, + 'rft': refungibleDeprecatedAbi, + 'ft': fungibleDeprecatedAbi, + }[mode]; + abi = [...abi, ...deprecated]; + } + const web3 = this.helper.getWeb3(); + return new web3.eth.Contract(abi as any, address, { + gas: this.helper.eth.DEFAULT_GAS, + ...(caller ? {from: caller} : {}), + }); + } + + collectionById(collectionId: number, mode: 'nft' | 'rft' | 'ft', caller?: string, mergeDeprecated = false) { + return this.collection(this.helper.ethAddress.fromCollectionId(collectionId), mode, caller, mergeDeprecated); + } + + rftToken(address: string, caller?: string, mergeDeprecated = false) { + const web3 = this.helper.getWeb3(); + const abi = mergeDeprecated ? [...refungibleTokenAbi, ...refungibleTokenDeprecatedAbi] : refungibleTokenAbi; + return new web3.eth.Contract(abi as any, address, { + gas: this.helper.eth.DEFAULT_GAS, + ...(caller ? {from: caller} : {}), + }); + } + + rftTokenById(collectionId: number, tokenId: number, caller?: string, mergeDeprecated = false) { + return this.rftToken(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller, mergeDeprecated); + } +} + +class CreateCollectionTransaction { + signer: string; + data: CreateCollectionData; + mergeDeprecated: boolean; + helper: EthUniqueHelper; + + constructor(helper: EthUniqueHelper, signer: string, data: CreateCollectionData, mergeDeprecated = false) { + this.helper = helper; + this.signer = signer; + + let flags = 0; + // convert CollectionFlags to number and join them in one number + if(!data.flags) { + flags = 0; + } else if(typeof data.flags == 'number') { + flags = data.flags; + } else { + for(let i = 0; i < data.flags.length; i++){ + const flag = data.flags[i]; + flags = flags | flag; + } + } + data.flags = flags; + + this.data = data; + this.mergeDeprecated = mergeDeprecated; + } + + // eslint-disable-next-line require-await + private async createTransaction() { + const collectionHelper = this.helper.ethNativeContract.collectionHelpers(this.signer); + let collectionMode; + switch (this.data.collectionMode) { + case 'nft': collectionMode = CollectionMode.Nonfungible; break; + case 'rft': collectionMode = CollectionMode.Refungible; break; + case 'ft': collectionMode = CollectionMode.Fungible; break; + } + + const tx = collectionHelper.methods.createCollection([ + this.data.name, + this.data.description, + this.data.tokenPrefix, + collectionMode, + this.data.decimals, + this.data.properties, + this.data.tokenPropertyPermissions, + this.data.adminList, + this.data.nestingSettings, + this.data.limits, + this.data.pendingSponsor, + this.data.flags, + ]); + return tx; + } + + async send(options?: any): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[], collection: Contract }> { + const collectionCreationPrice = { + value: Number(this.helper.balance.getCollectionCreationPrice()), + }; + const tx = await this.createTransaction(); + const result = await tx.send({...options, ...collectionCreationPrice}); + + const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); + const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress); + const events = this.helper.eth.normalizeEvents(result.events); + const collection = await this.helper.ethNativeContract.collectionById(collectionId, this.data.collectionMode, this.signer, this.mergeDeprecated); + + return {collectionId, collectionAddress, events, collection}; + } + + async call(options?: any) { + const collectionCreationPrice = { + value: Number(this.helper.balance.getCollectionCreationPrice()), + }; + const tx = await this.createTransaction(); + + return await tx.call({...options, ...collectionCreationPrice}); + } +} + + +class EthGroup extends EthGroupBase { + DEFAULT_GAS = 2_500_000; + + createAccount() { + const web3 = this.helper.getWeb3(); + const account = web3.eth.accounts.create(); + web3.eth.accounts.wallet.add(account.privateKey); + return account.address; + } + + async createAccountWithBalance(donor: IKeyringPair, amount = 600n) { + const account = this.createAccount(); + await this.transferBalanceFromSubstrate(donor, account, amount); + + return account; + } + + async transferBalanceFromSubstrate(donor: IKeyringPair, recepient: string, amount = 100n, inTokens = true) { + return await this.helper.balance.transferToSubstrate(donor, evmToAddress(recepient), amount * (inTokens ? this.helper.balance.getOneTokenNominal() : 1n)); + } + + async getCollectionCreationFee(signer: string) { + const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer); + return await collectionHelper.methods.collectionCreationFee().call(); + } + + async sendEVM(signer: IKeyringPair, contractAddress: string, abi: string, value: string, gasLimit?: number) { + if(!gasLimit) gasLimit = this.DEFAULT_GAS; + const web3 = this.helper.getWeb3(); + // FIXME: can't send legacy transaction using tx.evm.call + const gasPrice = await web3.eth.getGasPrice(); + // TODO: check execution status + await this.helper.executeExtrinsic( + signer, + 'api.tx.evm.call', [this.helper.address.substrateToEth(signer.address), contractAddress, abi, value, gasLimit, gasPrice, null, null, []], + true, + ); + } + + async callEVM(signer: TEthereumAccount, contractAddress: string, abi: string) { + return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]); + } + + createCollectionMethodName(mode: TCollectionMode) { + switch (mode) { + case 'ft': + return 'createFTCollection'; + case 'nft': + return 'createNFTCollection'; + case 'rft': + return 'createRFTCollection'; + } + } + + createCollection(signer: string, data: CreateCollectionData, mergeDeprecated = false): CreateCollectionTransaction { + return new CreateCollectionTransaction(this.helper, signer, data, mergeDeprecated); + } + + createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { + return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send(); + } + + async createERC721MetadataCompatibleCollection(signer: string, data: CreateCollectionData, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { + const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer); + + const {collectionId, collectionAddress, events} = await this.createCollection(signer, data).send(); + + await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send(); + + return {collectionId, collectionAddress, events}; + } + + async createERC721MetadataCompatibleNFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { + const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer); + + const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send(); + + await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send(); + + return {collectionId, collectionAddress, events}; + } + + createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { + return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send(); + } + + createFungibleCollection(signer: string, name: string, _decimals: number, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { + return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'ft')).send(); + } + + async createERC721MetadataCompatibleRFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { + const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer); + + const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send(); + + await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send(); + + return {collectionId, collectionAddress, events}; + } + + async deployCollectorContract(signer: string): Promise { + return await this.helper.ethContract.deployByCode(signer, 'Collector', ` + // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.6; + + contract Collector { + uint256 collected; + fallback() external payable { + giveMoney(); + } + function giveMoney() public payable { + collected += msg.value; + } + function getCollected() public view returns (uint256) { + return collected; + } + function getUnaccounted() public view returns (uint256) { + return address(this).balance - collected; + } + + function withdraw(address payable target) public { + target.transfer(collected); + collected = 0; + } + } + `); + } + + async deployFlipper(signer: string): Promise { + return await this.helper.ethContract.deployByCode(signer, 'Flipper', ` + // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.6; + + contract Flipper { + bool value = false; + function flip() public { + value = !value; + } + function getValue() public view returns (bool) { + return value; + } + } + `); + } + + async recordCallFee(user: string, call: () => Promise): Promise { + const before = await this.helper.balance.getEthereum(user); + await call(); + // In dev mode, the transaction might not finish processing in time + await this.helper.wait.newBlocks(1); + const after = await this.helper.balance.getEthereum(user); + + return before - after; + } + + normalizeEvents(events: any): NormalizedEvent[] { + const output = []; + for(const key of Object.keys(events)) { + if(key.match(/^[0-9]+$/)) { + output.push(events[key]); + } else if(Array.isArray(events[key])) { + output.push(...events[key]); + } else { + output.push(events[key]); + } + } + output.sort((a, b) => a.logIndex - b.logIndex); + return output.map(({address, event, returnValues}) => { + const args: { [key: string]: string } = {}; + for(const key of Object.keys(returnValues)) { + if(!key.match(/^[0-9]+$/)) { + args[key] = returnValues[key]; + } + } + return { + address, + event, + args, + }; + }); + } + + async calculateFee(address: ICrossAccountId, code: () => Promise): Promise { + const wrappedCode = async () => { + await code(); + // In dev mode, the transaction might not finish processing in time + await this.helper.wait.newBlocks(1); + }; + return await this.helper.arrange.calculcateFee(address, wrappedCode); + } +} + +class EthAddressGroup extends EthGroupBase { + extractCollectionId(address: string): number { + if(!(address.length === 42 || address.length === 40)) throw new Error('address wrong format'); + return parseInt(address.slice(address.length - 8), 16); + } + + fromCollectionId(collectionId: number): string { + if(collectionId >= 0xffffffff || collectionId < 0) throw new Error('collectionId overflow'); + return (web3 as any).utils.toChecksumAddress(`0x17c4e6453cc49aaaaeaca894e6d9683e${collectionId.toString(16).padStart(8, '0')}`); + } + + extractTokenId(address: string): { collectionId: number, tokenId: number } { + if(!address.startsWith('0x')) + throw 'address not starts with "0x"'; + if(address.length > 42) + throw 'address length is more than 20 bytes'; + return { + collectionId: Number('0x' + address.substring(address.length - 16, address.length - 8)), + tokenId: Number('0x' + address.substring(address.length - 8)), + }; + } + + fromTokenId(collectionId: number, tokenId: number): string { + return this.helper.util.getTokenAddress({collectionId, tokenId}); + } + + normalizeAddress(address: string): string { + return '0x' + address.substring(address.length - 40); + } +} +export class EthPropertyGroup extends EthGroupBase { + property(key: string, value: string): EthProperty { + return [ + key, + '0x' + Buffer.from(value).toString('hex'), + ]; + } +} +export type EthUniqueHelperConstructor = new (...args: any[]) => EthUniqueHelper; + +export class EthCrossAccountGroup extends EthGroupBase { + createAccount(): CrossAddress { + return this.fromAddress(this.helper.eth.createAccount()); + } + + async createAccountWithBalance(donor: IKeyringPair, amount = 100n) { + return this.fromAddress(await this.helper.eth.createAccountWithBalance(donor, amount)); + } + + fromAddress(address: TEthereumAccount): CrossAddress { + return { + eth: address, + sub: '0', + }; + } + + fromAddr(address: TEthereumAccount): [string, string] { + return [ + address, + '0', + ]; + } + + fromKeyringPair(keyring: IKeyringPair): CrossAddress { + return { + eth: '0x0000000000000000000000000000000000000000', + sub: keyring.addressRaw, + }; + } +} + +export class FeeGas { + fee: number | bigint = 0n; + + gas: number | bigint = 0n; + + public static async build(helper: EthUniqueHelper, fee: bigint): Promise { + const instance = new FeeGas(); + instance.fee = instance.convertToTokens(fee); + instance.gas = await instance.convertToGas(fee, helper); + return instance; + } + + private async convertToGas(fee: bigint, helper: EthUniqueHelper): Promise { + const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice()); + return fee / gasPrice; + } + + private convertToTokens(value: bigint, nominal = 1_000_000_000_000_000_000n): number { + return Number((value * 1000n) / nominal) / 1000; + } +} + +class EthArrangeGroup extends ArrangeGroup { + declare helper: EthUniqueHelper; + + constructor(helper: EthUniqueHelper) { + super(helper); + this.helper = helper; + } + + async calculcateFeeGas(payer: ICrossAccountId, promise: () => Promise): Promise { + const fee = await this.calculcateFee(payer, promise); + return await FeeGas.build(this.helper, fee); + } +} +export class EthUniqueHelper extends DevUniqueHelper { + web3: web3.default | null = null; + web3Provider: WebsocketProvider | null = null; + + eth: EthGroup; + ethAddress: EthAddressGroup; + ethCrossAccount: EthCrossAccountGroup; + ethNativeContract: NativeContractGroup; + ethContract: ContractGroup; + ethProperty: EthPropertyGroup; + declare arrange: EthArrangeGroup; + constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: { [key: string]: any } = {}) { + options.helperBase = options.helperBase ?? EthUniqueHelper; + + super(logger, options); + this.eth = new EthGroup(this); + this.ethAddress = new EthAddressGroup(this); + this.ethCrossAccount = new EthCrossAccountGroup(this); + this.ethNativeContract = new NativeContractGroup(this); + this.ethContract = new ContractGroup(this); + this.ethProperty = new EthPropertyGroup(this); + this.arrange = new EthArrangeGroup(this); + super.arrange = this.arrange; + } + + getWeb3(): web3.default { + if(this.web3 === null) throw Error('Web3 not connected'); + return this.web3; + } + + connectWeb3(wsEndpoint: string) { + if(this.web3 !== null) return; + this.web3Provider = new (web3 as any).providers.WebsocketProvider(wsEndpoint); + this.web3 = new (web3 as any)(this.web3Provider); + } + + override async disconnect() { + if(this.web3 === null) return; + this.web3Provider?.connection.close(); + + await super.disconnect(); + } + + override clearApi() { + super.clearApi(); + this.web3 = null; + } + + override clone(helperCls: EthUniqueHelperConstructor, options?: { [key: string]: any; }): EthUniqueHelper { + const newHelper = super.clone(helperCls, options) as EthUniqueHelper; + newHelper.web3 = this.web3; + newHelper.web3Provider = this.web3Provider; + + return newHelper; + } +} --- /dev/null +++ b/js-packages/tests/fungible.test.ts @@ -0,0 +1,186 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect, requirePalletsOrSkip, Pallets} from './util/index.js'; + +const U128_MAX = (1n << 128n) - 1n; + +describe('integration test: Fungible functionality:', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); + }); + }); + + itSub('Create fungible collection and token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'trest'}); + const defaultTokenId = await collection.getLastTokenId(); + expect(defaultTokenId).to.be.equal(0); + + await collection.mint(alice, U128_MAX); + const aliceBalance = await collection.getBalance({Substrate: alice.address}); + const itemCountAfter = await collection.getLastTokenId(); + + expect(itemCountAfter).to.be.equal(defaultTokenId); + expect(aliceBalance).to.be.equal(U128_MAX); + }); + + itSub('RPC method tokenOnewrs for fungible collection and token', async ({helper}) => { + const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; + const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => ({Substrate: keyring.address})); + + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + + await collection.mint(alice, U128_MAX); + + await collection.transfer(alice, {Substrate: bob.address}, 1000n); + await collection.transfer(alice, ethAcc, 900n); + + for(let i = 0; i < 7; i++) { + await collection.transfer(alice, facelessCrowd[i], 1n); + } + + const owners = await collection.getTop10Owners(); + + // What to expect + expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]); + expect(owners.length).to.be.equal(10); + + const [eleven] = await helper.arrange.createAccounts([0n], donor); + expect(await collection.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true; + expect((await collection.getTop10Owners()).length).to.be.equal(10); + }); + + itSub('Transfer token', async ({helper}) => { + const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + await collection.mint(alice, 500n); + + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n); + expect(await collection.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true; + expect(await collection.transfer(alice, ethAcc, 140n)).to.be.true; + + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(300n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(60n); + expect(await collection.getBalance(ethAcc)).to.be.equal(140n); + + await expect(collection.transfer(alice, {Substrate: bob.address}, 350n)).to.eventually.be.rejectedWith(/common\.TokenValueTooLow/); + }); + + itSub('Tokens multiple creation', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + + await collection.mintWithOneOwner(alice, [ + {value: 500n}, + {value: 400n}, + {value: 300n}, + ]); + + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1200n); + }); + + itSub('Burn some tokens ', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + await collection.mint(alice, 500n); + + expect(await collection.doesTokenExist(0)).to.be.true; + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n); + expect(await collection.burnTokens(alice, 499n)).to.be.true; + expect(await collection.doesTokenExist(0)).to.be.true; + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n); + }); + + itSub('Burn all tokens ', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + await collection.mint(alice, 500n); + + expect(await collection.doesTokenExist(0)).to.be.true; + expect(await collection.burnTokens(alice, 500n)).to.be.true; + expect(await collection.doesTokenExist(0)).to.be.true; + + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(0n); + expect(await collection.getTotalPieces()).to.be.equal(0n); + }); + + itSub('Set allowance for token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; + await collection.mint(alice, 100n); + + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(100n); + + expect(await collection.approveTokens(alice, {Substrate: bob.address}, 60n)).to.be.true; + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(0n); + + expect(await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true; + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(80n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(20n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n); + + await collection.burnTokensFrom(bob, {Substrate: alice.address}, 10n); + + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(70n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(30n); + expect(await collection.transferFrom(bob, {Substrate: alice.address}, ethAcc, 10n)).to.be.true; + expect(await collection.getBalance(ethAcc)).to.be.equal(10n); + }); +}); + +describe('Fungible negative tests', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.Fungible]); + + donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('Cannot transfer incorrect amount of tokens', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const nonExistingCollection = helper.ft.getCollectionObject(99999); + await collection.mint(alice, 10n, {Substrate: bob.address}); + + // 1. Alice cannot transfer more than 0 tokens if balance low: + await expect(collection.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow'); + await expect(collection.transfer(alice, {Substrate: charlie.address}, 100n)).to.be.rejectedWith('common.TokenValueTooLow'); + + // 2. Alice cannot transfer non-existing token: + await expect(nonExistingCollection.transfer(alice, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.CollectionNotFound'); + await expect(nonExistingCollection.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.CollectionNotFound'); + + // 3. Zero transfer allowed (EIP-20): + await collection.transfer(bob, {Substrate: charlie.address}, 0n); + // 3.1 even if the balance = 0 + await collection.transfer(alice, {Substrate: charlie.address}, 0n); + + expect(await collection.getBalance({Substrate: alice.address})).to.eq(0n); + expect(await collection.getBalance({Substrate: bob.address})).to.eq(10n); + expect(await collection.getBalance({Substrate: charlie.address})).to.eq(0n); + }); +}); --- /dev/null +++ b/js-packages/tests/getPropertiesRpc.test.ts @@ -0,0 +1,150 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from './util/index.js'; +import {UniqueHelper, UniqueNFTCollection} from '@unique/playgrounds/unique.js'; + +const collectionProps = [ + {key: 'col-0', value: 'col-0-value'}, + {key: 'col-1', value: 'col-1-value'}, +]; + +const tokenProps = [ + {key: 'tok-0', value: 'tok-0-value'}, + {key: 'tok-1', value: 'tok-1-value'}, +]; + +const tokPropPermission = { + mutable: false, + tokenOwner: true, + collectionAdmin: false, +}; + +const tokenPropPermissions = [ + { + key: 'tok-0', + permission: tokPropPermission, + }, + { + key: 'tok-1', + permission: tokPropPermission, + }, +]; + +describe('query properties RPC', () => { + let alice: IKeyringPair; + + const mintCollection = async (helper: UniqueHelper) => await helper.nft.mintCollection(alice, { + tokenPrefix: 'prps', + properties: collectionProps, + tokenPropertyPermissions: tokenPropPermissions, + }); + + const mintToken = async (collection: UniqueNFTCollection) => await collection.mintToken(alice, {Substrate: alice.address}, tokenProps); + + + before(async () => { + await usingPlaygrounds(async (_, privateKey) => { + alice = await privateKey({url: import.meta.url}); + }); + }); + + itSub('query empty collection key set', async ({helper}) => { + const collection = await mintCollection(helper); + const props = await collection.getProperties([]); + expect(props).to.be.empty; + }); + + itSub('query empty token key set', async ({helper}) => { + const collection = await mintCollection(helper); + const token = await mintToken(collection); + const props = await token.getProperties([]); + expect(props).to.be.empty; + }); + + itSub('query empty token key permissions set', async ({helper}) => { + const collection = await mintCollection(helper); + const propPermissions = await collection.getPropertyPermissions([]); + expect(propPermissions).to.be.empty; + }); + + itSub('query all collection props by null arg', async ({helper}) => { + const collection = await mintCollection(helper); + const props = await collection.getProperties(null); + expect(props).to.be.deep.equal(collectionProps); + }); + + itSub('query all token props by null arg', async ({helper}) => { + const collection = await mintCollection(helper); + const token = await mintToken(collection); + const props = await token.getProperties(null); + expect(props).to.be.deep.equal(tokenProps); + }); + + itSub('query empty token key permissions by null arg', async ({helper}) => { + const collection = await mintCollection(helper); + const propPermissions = await collection.getPropertyPermissions(null); + expect(propPermissions).to.be.deep.equal(tokenPropPermissions); + }); + + itSub('query all collection props by undefined arg', async ({helper}) => { + const collection = await mintCollection(helper); + const props = await collection.getProperties(); + expect(props).to.be.deep.equal(collectionProps); + }); + + itSub('query all token props by undefined arg', async ({helper}) => { + const collection = await mintCollection(helper); + const token = await mintToken(collection); + const props = await token.getProperties(); + expect(props).to.be.deep.equal(tokenProps); + }); + + itSub('query empty token key permissions by undefined arg', async ({helper}) => { + const collection = await mintCollection(helper); + const propPermissions = await collection.getPropertyPermissions(); + expect(propPermissions).to.be.deep.equal(tokenPropPermissions); + }); +}); + +[ + {mode: 'nft' as const}, + {mode: 'rft' as const}, +].map(testCase => + describe('negative properties', () => { + let alice: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (_, privateKey) => { + alice = await privateKey({url: import.meta.url}); + }); + }); + + itSub(`[${testCase.mode}] set token property for non-existent token`, async ({helper}) => { + const collection = await helper[testCase.mode].mintCollection(alice); + await collection.setTokenPropertyPermissions(alice, [{key: 'key', permission: {mutable: true, tokenOwner: true, collectionAdmin: true}}]); + await expect(collection.setTokenProperties(alice, 1, [{key: 'key', value: 'value'}])).to.be.rejectedWith('common.TokenNotFound'); + expect(await collection.getTokenProperties(1, ['key'])).to.be.empty; + }); + + itSub(`[${testCase.mode}] delete token property for non-existent token`, async ({helper}) => { + const collection = await helper[testCase.mode].mintCollection(alice); + await collection.setTokenPropertyPermissions(alice, [{key: 'key', permission: {mutable: true, tokenOwner: true, collectionAdmin: true}}]); + await expect(collection.deleteTokenProperties(alice, 1, ['key'])).to.be.rejectedWith('common.TokenNotFound'); + expect(await collection.getTokenProperties(1, ['key'])).to.be.empty; + }); + })); --- /dev/null +++ b/js-packages/tests/governance/council.test.ts @@ -0,0 +1,457 @@ + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js'; +import {Event} from '@unique/playgrounds/unique.dev.js'; +import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, councilMotionDuration, democracyFastTrackVotingPeriod, fellowshipRankLimit, clearCouncil, clearTechComm, initTechComm, clearFellowship, dummyProposal, dummyProposalCall, initFellowship, defaultEnactmentMoment, fellowshipPropositionOrigin} from './util.js'; +import type {ICounselors} from './util.js'; + +describeGov('Governance: Council tests', () => { + let donor: IKeyringPair; + let counselors: ICounselors; + let sudoer: IKeyringPair; + + const moreThanHalfCouncilThreshold = 3; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.Council]); + + donor = await privateKey({url: import.meta.url}); + sudoer = await privateKey('//Alice'); + }); + }); + + beforeEach(async () => { + counselors = await initCouncil(donor, sudoer); + }); + + afterEach(async () => { + await clearCouncil(sudoer); + await clearTechComm(sudoer); + }); + + async function proposalFromMoreThanHalfCouncil(proposal: any) { + return await usingPlaygrounds(async (helper) => { + expect((await helper.callRpc('api.query.councilMembership.members')).toJSON().length).to.be.equal(5); + const proposeResult = await helper.council.collective.propose( + counselors.filip, + proposal, + moreThanHalfCouncilThreshold, + ); + + const councilProposedEvent = Event.Council.Proposed.expect(proposeResult); + const proposalIndex = councilProposedEvent.proposalIndex; + const proposalHash = councilProposedEvent.proposalHash; + + + await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true); + await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true); + await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true); + + return await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex); + }); + } + + async function proposalFromAllCouncil(proposal: any) { + return await usingPlaygrounds(async (helper) => { + expect((await helper.callRpc('api.query.councilMembership.members')).toJSON().length).to.be.equal(5); + const proposeResult = await helper.council.collective.propose( + counselors.filip, + proposal, + moreThanHalfCouncilThreshold, + ); + + const councilProposedEvent = Event.Council.Proposed.expect(proposeResult); + const proposalIndex = councilProposedEvent.proposalIndex; + const proposalHash = councilProposedEvent.proposalHash; + + + await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true); + await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true); + await helper.council.collective.vote(counselors.ildar, proposalHash, proposalIndex, true); + await helper.council.collective.vote(counselors.irina, proposalHash, proposalIndex, true); + await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true); + + return await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex); + }); + } + + itSub('>50% of Council can externally propose SuperMajorityAgainst', async ({helper}) => { + const forceSetBalanceReceiver = helper.arrange.createEmptyAccount(); + const forceSetBalanceTestValue = 20n * 10n ** 25n; + + const democracyProposal = await helper.constructApiCall('api.tx.balances.forceSetBalance', [ + forceSetBalanceReceiver.address, forceSetBalanceTestValue, + ]); + + const councilProposal = await helper.democracy.externalProposeDefaultCall(democracyProposal); + + const proposeResult = await helper.council.collective.propose( + counselors.filip, + councilProposal, + moreThanHalfCouncilThreshold, + ); + + const councilProposedEvent = Event.Council.Proposed.expect(proposeResult); + const proposalIndex = councilProposedEvent.proposalIndex; + const proposalHash = councilProposedEvent.proposalHash; + + await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true); + await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true); + await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true); + + await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex); + + const democracyStartedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); + const democracyReferendumIndex = democracyStartedEvent.referendumIndex; + const democracyThreshold = democracyStartedEvent.threshold; + + expect(democracyThreshold).to.be.equal('SuperMajorityAgainst'); + + await helper.democracy.vote(counselors.filip, democracyReferendumIndex, { + Standard: { + vote: { + aye: true, + conviction: 1, + }, + balance: 10_000n, + }, + }); + + await helper.democracy.vote(counselors.charu, democracyReferendumIndex, { + Standard: { + vote: { + aye: false, + conviction: 1, + }, + balance: 50_000n, + }, + }); + + const passedReferendumEvent = await helper.wait.expectEvent(democracyVotingPeriod, Event.Democracy.Passed); + expect(passedReferendumEvent.referendumIndex).to.be.equal(democracyReferendumIndex); + + await helper.wait.expectEvent(democracyEnactmentPeriod, Event.Scheduler.Dispatched); + const receiverBalance = await helper.balance.getSubstrate(forceSetBalanceReceiver.address); + expect(receiverBalance).to.be.equal(forceSetBalanceTestValue); + }); + + itSub('Council prime member vote is the default', async ({helper}) => { + const newTechCommMember = helper.arrange.createEmptyAccount(); + const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address); + const proposeResult = await helper.council.collective.propose( + counselors.filip, + addMemberProposal, + moreThanHalfCouncilThreshold, + ); + + const councilProposedEvent = Event.Council.Proposed.expect(proposeResult); + const proposalIndex = councilProposedEvent.proposalIndex; + const proposalHash = councilProposedEvent.proposalHash; + + await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true); + + await helper.wait.newBlocks(councilMotionDuration); + const closeResult = await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex); + const closeEvent = Event.Council.Closed.expect(closeResult); + const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as string[]; + expect(closeEvent.yes).to.be.equal(members.length); + }); + + itSub('Superuser can add a member', async ({helper}) => { + const newMember = helper.arrange.createEmptyAccount(); + await expect(helper.getSudo().council.membership.addMember(sudoer, newMember.address)).to.be.fulfilled; + + const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON(); + expect(members).to.contains(newMember.address); + }); + + itSub('Superuser can remove a member', async ({helper}) => { + await expect(helper.getSudo().council.membership.removeMember(sudoer, counselors.alex.address)).to.be.fulfilled; + + const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON(); + expect(members).to.not.contains(counselors.alex.address); + }); + + itSub('>50% Council can add TechComm member', async ({helper}) => { + const newTechCommMember = helper.arrange.createEmptyAccount(); + const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address); + + await proposalFromMoreThanHalfCouncil(addMemberProposal); + + const techCommMembers = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON(); + expect(techCommMembers).to.contains(newTechCommMember.address); + }); + + itSub('Council can remove TechComm member', async ({helper}) => { + const techComm = await initTechComm(donor, sudoer); + const removeMemberPrpoposal = helper.technicalCommittee.membership.removeMemberCall(techComm.andy.address); + await proposalFromMoreThanHalfCouncil(removeMemberPrpoposal); + + const techCommMembers = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON(); + expect(techCommMembers).to.not.contains(techComm.andy.address); + }); + + itSub.skip('Council member can add Fellowship member', async ({helper}) => { + const newFellowshipMember = helper.arrange.createEmptyAccount(); + await expect(helper.council.collective.execute( + counselors.alex, + helper.fellowship.collective.addMemberCall(newFellowshipMember.address), + )).to.be.fulfilled; + const fellowshipMembers = (await helper.callRpc('api.query.fellowshipCollective.members')).toJSON(); + expect(fellowshipMembers).to.contains(newFellowshipMember.address); + }); + + itSub('>50% Council can promote Fellowship member', async ({helper}) => { + const fellowship = await initFellowship(donor, sudoer); + const memberWithZeroRank = fellowship[0][0]; + + const proposal = helper.fellowship.collective.promoteCall(memberWithZeroRank.address); + await proposalFromMoreThanHalfCouncil(proposal); + const record = (await helper.callRpc('api.query.fellowshipCollective.members', [memberWithZeroRank.address])).toJSON(); + expect(record).to.be.deep.equal({rank: 1}); + + await clearFellowship(sudoer); + }); + + itSub('>50% Council can demote Fellowship member', async ({helper}) => { + const fellowship = await initFellowship(donor, sudoer); + const memberWithRankOne = fellowship[1][0]; + + const proposal = helper.fellowship.collective.demoteCall(memberWithRankOne.address); + await proposalFromMoreThanHalfCouncil(proposal); + + const record = (await helper.callRpc('api.query.fellowshipCollective.members', [memberWithRankOne.address])).toJSON(); + expect(record).to.be.deep.equal({rank: 0}); + + await clearFellowship(sudoer); + }); + + itSub('>50% Council can add\remove Fellowship member', async ({helper}) => { + try { + const newMember = helper.arrange.createEmptyAccount(); + + const proposalAdd = helper.fellowship.collective.addMemberCall(newMember.address); + const proposalRemove = helper.fellowship.collective.removeMemberCall(newMember.address, fellowshipRankLimit); + await expect(proposalFromMoreThanHalfCouncil(proposalAdd)).to.be.fulfilled; + expect(await helper.fellowship.collective.getMembers()).to.be.deep.contain(newMember.address); + await expect(proposalFromMoreThanHalfCouncil(proposalRemove)).to.be.fulfilled; + expect(await helper.fellowship.collective.getMembers()).to.be.not.deep.contain(newMember.address); + } + finally { + await clearFellowship(sudoer); + } + }); + + itSub('Council can blacklist Democracy proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + await expect(proposalFromAllCouncil(helper.democracy.blacklistCall(preimageHash, null))).to.be.fulfilled; + }); + + itSub('Sudo can blacklist Democracy proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + await expect(helper.getSudo().democracy.blacklist(sudoer, preimageHash)).to.be.fulfilled; + }); + + itSub('[Negative] Council cannot add Council member', async ({helper}) => { + const newCouncilMember = helper.arrange.createEmptyAccount(); + const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address); + + await expect(proposalFromAllCouncil(addMemberProposal)).to.be.rejected; + }); + + itSub('[Negative] Council cannot remove Council member', async ({helper}) => { + const removeMemberProposal = helper.council.membership.removeMemberCall(counselors.alex.address); + + await expect(proposalFromAllCouncil(removeMemberProposal)).to.be.rejected; + }); + + itSub('[Negative] Council cannot submit regular democracy proposal', async ({helper}) => { + const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n); + + await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/); + }); + + itSub('[Negative] Council cannot externally propose SimpleMajority', async ({helper}) => { + const councilProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper)); + + await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/); + }); + + itSub('[Negative] Council cannot externally propose SuperMajorityApprove', async ({helper}) => { + const councilProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper)); + + await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/); + }); + + itSub('[Negative] Council member cannot add/remove a Council member', async ({helper}) => { + const newCouncilMember = helper.arrange.createEmptyAccount(); + await expect(helper.council.collective.execute( + counselors.alex, + helper.council.membership.addMemberCall(newCouncilMember.address), + )).to.be.rejectedWith('BadOrigin'); + await expect(helper.council.collective.execute( + counselors.alex, + helper.council.membership.removeMemberCall(counselors.charu.address), + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] Council cannot set/clear Council prime member', async ({helper}) => { + const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address); + const proposalForClear = await helper.council.membership.clearPrimeCall(); + + await expect(proposalFromAllCouncil(proposalForSet)).to.be.rejectedWith(/BadOrigin/); + await expect(proposalFromAllCouncil(proposalForClear)).to.be.rejectedWith(/BadOrigin/); + + }); + + itSub('[Negative] Council member cannot set/clear Council prime member', async ({helper}) => { + await expect(helper.council.collective.execute( + counselors.alex, + helper.council.membership.setPrimeCall(counselors.charu.address), + )).to.be.rejectedWith('BadOrigin'); + await expect(helper.council.collective.execute( + counselors.alex, + helper.council.membership.clearPrimeCall(), + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] Council member cannot add/remove a TechComm member', async ({helper}) => { + const newTechCommMember = helper.arrange.createEmptyAccount(); + await expect(helper.council.collective.execute( + counselors.alex, + helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address), + )).to.be.rejectedWith('BadOrigin'); + await expect(helper.council.collective.execute( + counselors.alex, + helper.technicalCommittee.membership.removeMemberCall(newTechCommMember.address), + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] Council member cannot promote/demote a Fellowship member', async ({helper}) => { + const fellowship = await initFellowship(donor, sudoer); + const memberWithRankOne = fellowship[1][0]; + + await expect(helper.council.collective.execute( + counselors.alex, + helper.fellowship.collective.promoteCall(memberWithRankOne.address), + )).to.be.rejectedWith('BadOrigin'); + await expect(helper.council.collective.execute( + counselors.alex, + helper.fellowship.collective.demoteCall(memberWithRankOne.address), + )).to.be.rejectedWith('BadOrigin'); + await clearFellowship(sudoer); + }); + + itSub('[Negative] Council cannot fast-track Democracy proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); + + await expect(proposalFromAllCouncil(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0))) + .to.be.rejectedWith(/BadOrigin/); + }); + + itSub('[Negative] Council member cannot fast-track Democracy proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); + + await expect(helper.council.collective.execute( + counselors.alex, + helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0), + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] Council cannot cancel Democracy proposals', async ({helper}) => { + const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n); + const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex; + + await expect(proposalFromAllCouncil(helper.democracy.cancelProposalCall(proposalIndex))) + .to.be.rejectedWith(/BadOrigin/); + }); + + itSub('[Negative] Council member cannot cancel Democracy proposals', async ({helper}) => { + + const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n); + const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex; + + await expect(helper.council.collective.execute( + counselors.alex, + helper.democracy.cancelProposalCall(proposalIndex), + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] Council cannot cancel ongoing Democracy referendums', async ({helper}) => { + await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper)); + const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); + const referendumIndex = startedEvent.referendumIndex; + + await expect(proposalFromAllCouncil(helper.democracy.emergencyCancelCall(referendumIndex))) + .to.be.rejectedWith(/BadOrigin/); + }); + + itSub('[Negative] Council member cannot cancel ongoing Democracy referendums', async ({helper}) => { + await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper)); + const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); + const referendumIndex = startedEvent.referendumIndex; + + await expect(helper.council.collective.execute( + counselors.alex, + helper.democracy.emergencyCancelCall(referendumIndex), + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] Council cannot cancel Fellowship referendums', async ({helper}) => { + const fellowship = await initFellowship(donor, sudoer); + const fellowshipProposer = fellowship[5][0]; + const proposal = dummyProposal(helper); + + const submitResult = await helper.fellowship.referenda.submit( + fellowshipProposer, + fellowshipPropositionOrigin, + proposal, + defaultEnactmentMoment, + ); + + const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; + + await expect(proposalFromAllCouncil(helper.fellowship.referenda.cancelCall(referendumIndex))) + .to.be.rejectedWith(/BadOrigin/); + }); + + itSub('[Negative] Council member cannot cancel Fellowship referendums', async ({helper}) => { + const fellowship = await initFellowship(donor, sudoer); + const fellowshipProposer = fellowship[5][0]; + const proposal = dummyProposal(helper); + + const submitResult = await helper.fellowship.referenda.submit( + fellowshipProposer, + fellowshipPropositionOrigin, + proposal, + defaultEnactmentMoment, + ); + const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; + await expect(helper.council.collective.execute( + counselors.alex, + helper.fellowship.referenda.cancelCall(referendumIndex), + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] Council referendum cannot be closed until the voting threshold is met', async ({helper}) => { + const councilSize = (await helper.callRpc('api.query.councilMembership.members')).toJSON().length as any as number; + expect(councilSize).is.greaterThan(1); + const proposeResult = await helper.council.collective.propose( + counselors.filip, + dummyProposalCall(helper), + councilSize, + ); + + const councilProposedEvent = Event.Council.Proposed.expect(proposeResult); + const proposalIndex = councilProposedEvent.proposalIndex; + const proposalHash = councilProposedEvent.proposalHash; + + + await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true); + await expect(helper.council.collective.close(counselors.filip, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly'); + }); + +}); --- /dev/null +++ b/js-packages/tests/governance/democracy.test.ts @@ -0,0 +1,89 @@ +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js'; +import {clearFellowship, democracyLaunchPeriod, democracyTrackMinRank, dummyProposalCall, fellowshipConfirmPeriod, fellowshipMinEnactPeriod, fellowshipPreparePeriod, fellowshipPropositionOrigin, initFellowship, voteUnanimouslyInFellowship} from './util.js'; +import {Event} from '@unique/playgrounds/unique.dev.js'; + +describeGov('Governance: Democracy tests', () => { + let regularUser: IKeyringPair; + let donor: IKeyringPair; + let sudoer: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.Democracy]); + + donor = await privateKey({url: import.meta.url}); + sudoer = await privateKey('//Alice'); + + [regularUser] = await helper.arrange.createAccounts([1000n], donor); + }); + }); + + itSub('Regular user can vote', async ({helper}) => { + const fellows = await initFellowship(donor, sudoer); + const rank1Proposer = fellows[1][0]; + + const democracyProposalCall = dummyProposalCall(helper); + const fellowshipProposal = { + Inline: helper.democracy.proposeCall(democracyProposalCall, 0n).method.toHex(), + }; + + const submitResult = await helper.fellowship.referenda.submit( + rank1Proposer, + fellowshipPropositionOrigin, + fellowshipProposal, + {After: 0}, + ); + + const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; + await voteUnanimouslyInFellowship(helper, fellows, democracyTrackMinRank, fellowshipReferendumIndex); + await helper.fellowship.referenda.placeDecisionDeposit(donor, fellowshipReferendumIndex); + + await helper.wait.expectEvent( + fellowshipPreparePeriod + fellowshipConfirmPeriod + fellowshipMinEnactPeriod, + Event.Democracy.Proposed, + ); + + const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); + const referendumIndex = startedEvent.referendumIndex; + + const ayeBalance = 10_000n; + + await helper.democracy.vote(regularUser, referendumIndex, { + Standard: { + vote: { + aye: true, + conviction: 1, + }, + balance: ayeBalance, + }, + }); + + const referendumInfo = await helper.democracy.referendumInfo(referendumIndex); + const tally = referendumInfo.ongoing.tally; + + expect(BigInt(tally.ayes)).to.be.equal(ayeBalance); + + await clearFellowship(sudoer); + }); + + itSub('[Negative] Regular user cannot submit a regular proposal', async ({helper}) => { + const submitResult = helper.democracy.propose(regularUser, dummyProposalCall(helper), 0n); + await expect(submitResult).to.be.rejectedWith(/BadOrigin/); + }); + + itSub('[Negative] Regular user cannot externally propose SuperMajorityAgainst', async ({helper}) => { + const submitResult = helper.democracy.externalProposeDefault(regularUser, dummyProposalCall(helper)); + await expect(submitResult).to.be.rejectedWith(/BadOrigin/); + }); + + itSub('[Negative] Regular user cannot externally propose SimpleMajority', async ({helper}) => { + const submitResult = helper.democracy.externalProposeMajority(regularUser, dummyProposalCall(helper)); + await expect(submitResult).to.be.rejectedWith(/BadOrigin/); + }); + + itSub('[Negative] Regular user cannot externally propose SuperMajorityApprove', async ({helper}) => { + const submitResult = helper.democracy.externalPropose(regularUser, dummyProposalCall(helper)); + await expect(submitResult).to.be.rejectedWith(/BadOrigin/); + }); +}); --- /dev/null +++ b/js-packages/tests/governance/fellowship.test.ts @@ -0,0 +1,335 @@ +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js'; +import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js'; +import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyFastTrackVotingPeriod, fellowshipRankLimit, clearCouncil, clearTechComm, clearFellowship, defaultEnactmentMoment, dummyProposal, dummyProposalCall, fellowshipPropositionOrigin, initFellowship, initTechComm, voteUnanimouslyInFellowship, democracyTrackMinRank, fellowshipPreparePeriod, fellowshipConfirmPeriod, fellowshipMinEnactPeriod, democracyTrackId, hardResetFellowshipReferenda, hardResetDemocracy, hardResetGovScheduler} from './util.js'; +import type {ICounselors, ITechComms} from './util.js'; + +describeGov('Governance: Fellowship tests', () => { + let members: IKeyringPair[][]; + + let rank1Proposer: IKeyringPair; + + let sudoer: any; + let donor: any; + let counselors: ICounselors; + let techcomms: ITechComms; + + const submissionDeposit = 1000n; + + async function testBadFellowshipProposal( + helper: DevUniqueHelper, + proposalCall: any, + ) { + const badProposal = { + Inline: proposalCall.method.toHex(), + }; + const submitResult = await helper.fellowship.referenda.submit( + rank1Proposer, + fellowshipPropositionOrigin, + badProposal, + defaultEnactmentMoment, + ); + + const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; + await voteUnanimouslyInFellowship(helper, members, democracyTrackMinRank, referendumIndex); + await helper.fellowship.referenda.placeDecisionDeposit(donor, referendumIndex); + + const enactmentId = await helper.fellowship.referenda.enactmentEventId(referendumIndex); + const dispatchedEvent = await helper.wait.expectEvent( + fellowshipPreparePeriod + fellowshipConfirmPeriod + defaultEnactmentMoment.After, + Event.Scheduler.Dispatched, + (event: any) => event.id == enactmentId, + ); + + expect(dispatchedEvent.result.isErr, 'Bad Fellowship must fail') + .to.be.true; + + expect(dispatchedEvent.result.asErr.isBadOrigin, 'Bad Fellowship must fail with BadOrigin') + .to.be.true; + } + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.Democracy, Pallets.Fellowship, Pallets.TechnicalCommittee, Pallets.Council]); + + sudoer = await privateKey('//Alice'); + donor = await privateKey({url: import.meta.url}); + }); + + counselors = await initCouncil(donor, sudoer); + techcomms = await initTechComm(donor, sudoer); + members = await initFellowship(donor, sudoer); + + rank1Proposer = members[1][0]; + }); + + after(async () => { + await clearFellowship(sudoer); + await clearTechComm(sudoer); + await clearCouncil(sudoer); + await hardResetFellowshipReferenda(sudoer); + await hardResetDemocracy(sudoer); + await hardResetGovScheduler(sudoer); + }); + + itSub('FellowshipProposition can submit regular Democracy proposals', async ({helper}) => { + const democracyProposalCall = dummyProposalCall(helper); + const fellowshipProposal = { + Inline: helper.democracy.proposeCall(democracyProposalCall, 0n).method.toHex(), + }; + + const submitResult = await helper.fellowship.referenda.submit( + rank1Proposer, + fellowshipPropositionOrigin, + fellowshipProposal, + defaultEnactmentMoment, + ); + + const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; + await voteUnanimouslyInFellowship(helper, members, democracyTrackMinRank, fellowshipReferendumIndex); + await helper.fellowship.referenda.placeDecisionDeposit(donor, fellowshipReferendumIndex); + + const democracyProposed = await helper.wait.expectEvent( + fellowshipPreparePeriod + fellowshipConfirmPeriod + fellowshipMinEnactPeriod, + Event.Democracy.Proposed, + ); + + const democracyEnqueuedProposal = await helper.democracy.expectPublicProposal(democracyProposed.proposalIndex); + expect(democracyEnqueuedProposal.inline, 'Fellowship proposal expected to be in the Democracy') + .to.be.equal(democracyProposalCall.method.toHex()); + + await helper.wait.newBlocks(democracyVotingPeriod); + }); + + itSub('Fellowship (rank-1 or greater) member can submit Fellowship proposals on the Democracy track', async ({helper}) => { + for(let rank = 1; rank < fellowshipRankLimit; rank++) { + const rankMembers = members[rank]; + + for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) { + const member = rankMembers[memberIdx]; + const newDummyProposal = dummyProposal(helper); + + const submitResult = await helper.fellowship.referenda.submit( + member, + fellowshipPropositionOrigin, + newDummyProposal, + defaultEnactmentMoment, + ); + + const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; + const referendumInfo = await helper.fellowship.referenda.referendumInfo(referendumIndex); + expect(referendumInfo.ongoing.track, `${memberIdx}-th member of rank #${rank}: proposal #${referendumIndex} is on invalid track`) + .to.be.equal(democracyTrackId); + } + } + }); + + itSub(`Fellowship (rank-${democracyTrackMinRank} or greater) members can vote on the Democracy track`, async ({helper}) => { + const proposal = dummyProposal(helper); + + const submitResult = await helper.fellowship.referenda.submit( + rank1Proposer, + fellowshipPropositionOrigin, + proposal, + defaultEnactmentMoment, + ); + + const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; + + let expectedAyes = 0; + for(let rank = democracyTrackMinRank; rank < fellowshipRankLimit; rank++) { + const rankMembers = members[rank]; + + for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) { + const member = rankMembers[memberIdx]; + await helper.fellowship.collective.vote(member, referendumIndex, true); + expectedAyes += 1; + + const referendumInfo = await helper.fellowship.referenda.referendumInfo(referendumIndex); + expect(referendumInfo.ongoing.tally.bareAyes, `Vote from ${memberIdx}-th member of rank #${rank} isn't accounted`) + .to.be.equal(expectedAyes); + } + } + }); + + itSub('Fellowship rank vote strength is correct', async ({helper}) => { + const excessRankWeightTable = [ + 1, + 3, + 6, + 10, + 15, + 21, + ]; + + const proposal = dummyProposal(helper); + + const submitResult = await helper.fellowship.referenda.submit( + rank1Proposer, + fellowshipPropositionOrigin, + proposal, + defaultEnactmentMoment, + ); + + const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; + + for(let rank = democracyTrackMinRank; rank < fellowshipRankLimit; rank++) { + const rankMembers = members[rank]; + + for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) { + const member = rankMembers[memberIdx]; + + const referendumInfoBefore = await helper.fellowship.referenda.referendumInfo(referendumIndex); + const ayesBefore = referendumInfoBefore.ongoing.tally.ayes; + + await helper.fellowship.collective.vote(member, referendumIndex, true); + + const referendumInfoAfter = await helper.fellowship.referenda.referendumInfo(referendumIndex); + const ayesAfter = referendumInfoAfter.ongoing.tally.ayes; + + const expectedVoteWeight = excessRankWeightTable[rank - democracyTrackMinRank]; + const voteWeight = ayesAfter - ayesBefore; + + expect(voteWeight, `Vote weight of ${memberIdx}-th member of rank #${rank} is invalid`) + .to.be.equal(expectedVoteWeight); + } + } + }); + + itSub('[Negative] FellowshipProposition cannot externally propose SuperMajorityAgainst', async ({helper}) => { + await testBadFellowshipProposal(helper, helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper))); + }); + + itSub('[Negative] FellowshipProposition cannot externally propose SimpleMajority', async ({helper}) => { + await testBadFellowshipProposal(helper, helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper))); + }); + + itSub('[Negative] FellowshipProposition cannot externally propose SuperMajorityApprove', async ({helper}) => { + await testBadFellowshipProposal(helper, helper.democracy.externalProposeCall(dummyProposalCall(helper))); + }); + + itSub('[Negative] Fellowship (rank-0) member cannot submit Fellowship proposals on the Democracy track', async ({helper}) => { + const rank0Proposer = members[0][0]; + + const proposal = dummyProposal(helper); + + const submitResult = helper.fellowship.referenda.submit( + rank0Proposer, + fellowshipPropositionOrigin, + proposal, + defaultEnactmentMoment, + ); + + await expect(submitResult).to.be.rejectedWith(/BadOrigin/); + }); + + itSub('[Negative] Fellowship (rank-1 or greater) member cannot submit if no deposit can be provided', async ({helper}) => { + const poorMember = rank1Proposer; + + const balanceBefore = await helper.balance.getSubstrate(poorMember.address); + await helper.getSudo().balance.setBalanceSubstrate(sudoer, poorMember.address, submissionDeposit - 1n); + + const proposal = dummyProposal(helper); + + const submitResult = helper.fellowship.referenda.submit( + poorMember, + fellowshipPropositionOrigin, + proposal, + defaultEnactmentMoment, + ); + + await expect(submitResult).to.be.rejectedWith(/account balance too low/); + + await helper.getSudo().balance.setBalanceSubstrate(sudoer, poorMember.address, balanceBefore); + }); + + itSub(`[Negative] Fellowship (rank-${democracyTrackMinRank - 1} or less) members cannot vote on the Democracy track`, async ({helper}) => { + const proposal = dummyProposal(helper); + + const submitResult = await helper.fellowship.referenda.submit( + rank1Proposer, + fellowshipPropositionOrigin, + proposal, + defaultEnactmentMoment, + ); + + const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; + + for(let rank = 0; rank < democracyTrackMinRank; rank++) { + for(const member of members[rank]) { + const voteResult = helper.fellowship.collective.vote(member, referendumIndex, true); + await expect(voteResult).to.be.rejectedWith(/RankTooLow/); + } + } + }); + + itSub('[Negative] FellowshipProposition cannot add/remove a Council member', async ({helper}) => { + const [councilNonMember] = await helper.arrange.createAccounts([10n], donor); + + await testBadFellowshipProposal(helper, helper.council.membership.addMemberCall(councilNonMember.address)); + await testBadFellowshipProposal(helper, helper.council.membership.removeMemberCall(counselors.ildar.address)); + }); + + itSub('[Negative] FellowshipProposition cannot set/clear Council prime member', async ({helper}) => { + await testBadFellowshipProposal(helper, helper.council.membership.setPrimeCall(counselors.ildar.address)); + await testBadFellowshipProposal(helper, helper.council.membership.clearPrimeCall()); + }); + + itSub('[Negative] FellowshipProposition cannot add/remove a TechComm member', async ({helper}) => { + const [techCommNonMember] = await helper.arrange.createAccounts([10n], donor); + + await testBadFellowshipProposal(helper, helper.technicalCommittee.membership.addMemberCall(techCommNonMember.address)); + await testBadFellowshipProposal(helper, helper.technicalCommittee.membership.removeMemberCall(techcomms.constantine.address)); + }); + + itSub('[Negative] FellowshipProposition cannot add/remove a Fellowship member', async ({helper}) => { + const [fellowshipNonMember] = await helper.arrange.createAccounts([10n], donor); + + await testBadFellowshipProposal(helper, helper.fellowship.collective.addMemberCall(fellowshipNonMember.address)); + await testBadFellowshipProposal(helper, helper.fellowship.collective.removeMemberCall(rank1Proposer.address, 1)); + }); + + itSub('[Negative] FellowshipProposition cannot promote/demote a Fellowship member', async ({helper}) => { + await testBadFellowshipProposal(helper, helper.fellowship.collective.promoteCall(rank1Proposer.address)); + await testBadFellowshipProposal(helper, helper.fellowship.collective.demoteCall(rank1Proposer.address)); + }); + + itSub('[Negative] FellowshipProposition cannot fast-track Democracy proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + + await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); + + await testBadFellowshipProposal(helper, helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0)); + }); + + itSub('[Negative] FellowshipProposition cannot cancel Democracy proposals', async ({helper}) => { + const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n); + const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex; + + await testBadFellowshipProposal(helper, helper.democracy.cancelProposalCall(proposalIndex)); + }); + + itSub('[Negative] FellowshipProposition cannot cancel ongoing Democracy referendums', async ({helper}) => { + await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper)); + const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); + const referendumIndex = startedEvent.referendumIndex; + + await testBadFellowshipProposal(helper, helper.democracy.emergencyCancelCall(referendumIndex)); + }); + + itSub('[Negative] FellowshipProposition cannot blacklist Democracy proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + + await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); + + await testBadFellowshipProposal(helper, helper.democracy.blacklistCall(preimageHash, null)); + }); + + itSub('[Negative] FellowshipProposition cannot veto external proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + + await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); + + await testBadFellowshipProposal(helper, helper.democracy.vetoExternalCall(preimageHash)); + }); +}); --- /dev/null +++ b/js-packages/tests/governance/init.test.ts @@ -0,0 +1,193 @@ +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js'; +import {Event} from '@unique/playgrounds/unique.dev.js'; +import {democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, clearCouncil, clearTechComm, clearFellowship} from './util.js'; +import type {ICounselors, ITechComms} from './util.js'; + +describeGov('Governance: Initialization', () => { + let donor: IKeyringPair; + let sudoer: IKeyringPair; + let counselors: ICounselors; + let techcomms: ITechComms; + let coreDevs: any; + + const expectedAlexFellowRank = 7; + const expectedFellowRank = 6; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.Democracy, Pallets.Council, Pallets.TechnicalCommittee]); + + const councilMembers = await helper.council.membership.getMembers(); + const techcommMembers = await helper.technicalCommittee.membership.getMembers(); + expect(councilMembers.length == 0, 'The Council must be empty before the Gov Init'); + expect(techcommMembers.length == 0, 'The Technical Commettee must be empty before the Gov Init'); + + donor = await privateKey({url: import.meta.url}); + sudoer = await privateKey('//Alice'); + + const counselorsNum = 5; + const techCommsNum = 3; + const coreDevsNum = 2; + const [ + alex, + ildar, + charu, + filip, + irina, + + greg, + andy, + constantine, + + yaroslav, + daniel, + ] = await helper.arrange.createAccounts(new Array(counselorsNum + techCommsNum + coreDevsNum).fill(10_000n), donor); + + counselors = { + alex, + ildar, + charu, + filip, + irina, + }; + + techcomms = { + greg, + andy, + constantine, + }; + + coreDevs = { + yaroslav: yaroslav, + daniel: daniel, + }; + }); + }); + + itSub('Initialize Governance', async ({helper}) => { + const promoteFellow = (fellow: string, promotionsNum: number) => new Array(promotionsNum).fill(helper.fellowship.collective.promoteCall(fellow)); + + const expectFellowRank = async (fellow: string, expectedRank: number) => { + expect(await helper.fellowship.collective.getMemberRank(fellow)).to.be.equal(expectedRank); + }; + + console.log('\t- Setup the Prime of the Council via sudo'); + await helper.getSudo().utility.batchAll(sudoer, [ + helper.council.membership.addMemberCall(counselors.alex.address), + helper.council.membership.setPrimeCall(counselors.alex.address), + + helper.fellowship.collective.addMemberCall(counselors.alex.address), + ...promoteFellow(counselors.alex.address, expectedAlexFellowRank), + ]); + + let councilMembers = await helper.council.membership.getMembers(); + const councilPrime = await helper.council.collective.getPrimeMember(); + const alexFellowRank = await helper.fellowship.collective.getMemberRank(counselors.alex.address); + expect(councilMembers).to.be.deep.equal([counselors.alex.address]); + expect(councilPrime).to.be.equal(counselors.alex.address); + expect(alexFellowRank).to.be.equal(expectedAlexFellowRank); + + console.log('\t- The Council Prime initializes the Technical Commettee'); + const councilProposalThreshold = 1; + + await helper.council.collective.propose( + counselors.alex, + helper.utility.batchAllCall([ + helper.technicalCommittee.membership.addMemberCall(techcomms.greg.address), + helper.technicalCommittee.membership.addMemberCall(techcomms.andy.address), + helper.technicalCommittee.membership.addMemberCall(techcomms.constantine.address), + + helper.technicalCommittee.membership.setPrimeCall(techcomms.greg.address), + ]), + councilProposalThreshold, + ); + + const techCommMembers = await helper.technicalCommittee.membership.getMembers(); + const techCommPrime = await helper.technicalCommittee.membership.getPrimeMember(); + const expectedTechComms = [techcomms.greg.address, techcomms.andy.address, techcomms.constantine.address]; + expect(techCommMembers.length).to.be.equal(expectedTechComms.length); + expect(techCommMembers).to.containSubset(expectedTechComms); + expect(techCommPrime).to.be.equal(techcomms.greg.address); + + console.log('\t- The Council Prime initiates a referendum to add counselors'); + const returnPreimageHash = true; + const preimageHash = await helper.preimage.notePreimageFromCall(counselors.alex, helper.utility.batchAllCall([ + helper.council.membership.addMemberCall(counselors.ildar.address), + helper.council.membership.addMemberCall(counselors.charu.address), + helper.council.membership.addMemberCall(counselors.filip.address), + helper.council.membership.addMemberCall(counselors.irina.address), + + helper.fellowship.collective.addMemberCall(counselors.charu.address), + helper.fellowship.collective.addMemberCall(counselors.ildar.address), + helper.fellowship.collective.addMemberCall(counselors.irina.address), + helper.fellowship.collective.addMemberCall(counselors.filip.address), + helper.fellowship.collective.addMemberCall(techcomms.greg.address), + helper.fellowship.collective.addMemberCall(techcomms.andy.address), + helper.fellowship.collective.addMemberCall(techcomms.constantine.address), + helper.fellowship.collective.addMemberCall(coreDevs.yaroslav.address), + helper.fellowship.collective.addMemberCall(coreDevs.daniel.address), + + ...promoteFellow(counselors.charu.address, expectedFellowRank), + ...promoteFellow(counselors.ildar.address, expectedFellowRank), + ...promoteFellow(counselors.irina.address, expectedFellowRank), + ...promoteFellow(counselors.filip.address, expectedFellowRank), + ...promoteFellow(techcomms.greg.address, expectedFellowRank), + ...promoteFellow(techcomms.andy.address, expectedFellowRank), + ...promoteFellow(techcomms.constantine.address, expectedFellowRank), + ...promoteFellow(coreDevs.yaroslav.address, expectedFellowRank), + ...promoteFellow(coreDevs.daniel.address, expectedFellowRank), + ]), returnPreimageHash); + + await helper.council.collective.propose( + counselors.alex, + helper.democracy.externalProposeDefaultWithPreimageCall(preimageHash), + councilProposalThreshold, + ); + + console.log('\t- The referendum is being decided'); + const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); + + await helper.democracy.vote(counselors.filip, startedEvent.referendumIndex, { + Standard: { + vote: { + aye: true, + conviction: 1, + }, + balance: 10_000n, + }, + }); + + const passedReferendumEvent = await helper.wait.expectEvent(democracyVotingPeriod, Event.Democracy.Passed); + expect(passedReferendumEvent.referendumIndex).to.be.equal(startedEvent.referendumIndex); + + await helper.wait.expectEvent(democracyEnactmentPeriod, Event.Scheduler.Dispatched); + + councilMembers = await helper.council.membership.getMembers(); + const expectedCounselors = [ + counselors.alex.address, + counselors.ildar.address, + counselors.charu.address, + counselors.filip.address, + counselors.irina.address, + ]; + expect(councilMembers.length).to.be.equal(expectedCounselors.length); + expect(councilMembers).to.containSubset(expectedCounselors); + + await expectFellowRank(counselors.ildar.address, expectedFellowRank); + await expectFellowRank(counselors.charu.address, expectedFellowRank); + await expectFellowRank(counselors.filip.address, expectedFellowRank); + await expectFellowRank(counselors.irina.address, expectedFellowRank); + await expectFellowRank(techcomms.greg.address, expectedFellowRank); + await expectFellowRank(techcomms.andy.address, expectedFellowRank); + await expectFellowRank(techcomms.constantine.address, expectedFellowRank); + await expectFellowRank(coreDevs.yaroslav.address, expectedFellowRank); + await expectFellowRank(coreDevs.daniel.address, expectedFellowRank); + }); + + after(async function() { + await clearFellowship(sudoer); + await clearTechComm(sudoer); + await clearCouncil(sudoer); + }); +}); --- /dev/null +++ b/js-packages/tests/governance/technicalCommittee.test.ts @@ -0,0 +1,383 @@ +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js'; +import {Event} from '@unique/playgrounds/unique.dev.js'; +import {initCouncil, democracyLaunchPeriod, democracyFastTrackVotingPeriod, clearCouncil, clearTechComm, clearFellowship, defaultEnactmentMoment, dummyProposal, dummyProposalCall, fellowshipPropositionOrigin, initFellowship, initTechComm, hardResetFellowshipReferenda, hardResetDemocracy, hardResetGovScheduler} from './util.js'; +import type {ITechComms} from './util.js'; + +describeGov('Governance: Technical Committee tests', () => { + let sudoer: IKeyringPair; + let techcomms: ITechComms; + let donor: IKeyringPair; + let preImageHash: string; + + + const allTechCommitteeThreshold = 3; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.TechnicalCommittee]); + sudoer = await privateKey('//Alice'); + donor = await privateKey({url: import.meta.url}); + + techcomms = await initTechComm(donor, sudoer); + + const proposalCall = await helper.constructApiCall('api.tx.balances.forceSetBalance', [donor.address, 20n * 10n ** 25n]); + preImageHash = await helper.preimage.notePreimageFromCall(sudoer, proposalCall, true); + }); + }); + + after(async () => { + await usingPlaygrounds(async (helper) => { + await clearTechComm(sudoer); + + await helper.preimage.unnotePreimage(sudoer, preImageHash); + await hardResetFellowshipReferenda(sudoer); + await hardResetDemocracy(sudoer); + await hardResetGovScheduler(sudoer); + }); + }); + + function proposalFromAllCommittee(proposal: any) { + return usingPlaygrounds(async (helper) => { + expect((await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length).to.be.equal(allTechCommitteeThreshold); + const proposeResult = await helper.technicalCommittee.collective.propose( + techcomms.andy, + proposal, + allTechCommitteeThreshold, + ); + + const commiteeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult); + const proposalIndex = commiteeProposedEvent.proposalIndex; + const proposalHash = commiteeProposedEvent.proposalHash; + + + await helper.technicalCommittee.collective.vote(techcomms.andy, proposalHash, proposalIndex, true); + await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true); + await helper.technicalCommittee.collective.vote(techcomms.greg, proposalHash, proposalIndex, true); + + const closeResult = await helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex); + Event.TechnicalCommittee.Closed.expect(closeResult); + Event.TechnicalCommittee.Approved.expect(closeResult); + const {result} = Event.TechnicalCommittee.Executed.expect(closeResult); + expect(result).to.eq('Ok'); + + return closeResult; + }); + } + + itSub('TechComm can fast-track Democracy proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + await helper.wait.parachainBlockMultiplesOf(35n); + + await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); + + const fastTrackProposal = await proposalFromAllCommittee(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0)); + Event.Democracy.Started.expect(fastTrackProposal); + }); + + itSub('TechComm can cancel Democracy proposals', async ({helper}) => { + const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n); + const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex; + + const cancelProposal = await proposalFromAllCommittee(helper.democracy.cancelProposalCall(proposalIndex)); + Event.Democracy.ProposalCanceled.expect(cancelProposal); + }); + + itSub('TechComm can cancel ongoing Democracy referendums', async ({helper}) => { + await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper)); + const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); + const referendumIndex = startedEvent.referendumIndex; + + const emergencyCancelProposal = await proposalFromAllCommittee(helper.democracy.emergencyCancelCall(referendumIndex)); + Event.Democracy.Cancelled.expect(emergencyCancelProposal); + }); + + itSub('TechComm member can veto Democracy proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); + + const vetoExternalCall = await helper.technicalCommittee.collective.execute( + techcomms.andy, + helper.democracy.vetoExternalCall(preimageHash), + ); + Event.Democracy.Vetoed.expect(vetoExternalCall); + }); + + itSub('TechComm can cancel Fellowship referendums', async ({helper}) => { + const fellowship = await initFellowship(donor, sudoer); + const fellowshipProposer = fellowship[5][0]; + const proposal = dummyProposal(helper); + + const submitResult = await helper.fellowship.referenda.submit( + fellowshipProposer, + fellowshipPropositionOrigin, + proposal, + defaultEnactmentMoment, + ); + const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; + const cancelProposal = await proposalFromAllCommittee(helper.fellowship.referenda.cancelCall(referendumIndex)); + Event.FellowshipReferenda.Cancelled.expect(cancelProposal); + }); + + itSub.skip('TechComm member can add a Fellowship member', async ({helper}) => { + const newFellowshipMember = helper.arrange.createEmptyAccount(); + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + helper.fellowship.collective.addMemberCall(newFellowshipMember.address), + )).to.be.fulfilled; + const fellowshipMembers = (await helper.callRpc('api.query.fellowshipCollective.members')).toJSON(); + expect(fellowshipMembers).to.contains(newFellowshipMember.address); + await clearFellowship(sudoer); + }); + + itSub('[Negative] TechComm cannot submit regular democracy proposal', async ({helper}) => { + const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n); + + await expect(proposalFromAllCommittee(councilProposal)).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm cannot externally propose SuperMajorityAgainst', async ({helper}) => { + const commiteeProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper)); + + await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm cannot externally propose SimpleMajority', async ({helper}) => { + const commiteeProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper)); + + await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm cannot externally propose SuperMajorityApprove', async ({helper}) => { + const commiteeProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper)); + + await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm member cannot submit regular democracy proposal', async ({helper}) => { + const memberProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n); + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + memberProposal, + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm member cannot externally propose SuperMajorityAgainst', async ({helper}) => { + const memberProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper)); + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + memberProposal, + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm member cannot externally propose SimpleMajority', async ({helper}) => { + const memberProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper)); + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + memberProposal, + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm member cannot externally propose SuperMajorityApprove', async ({helper}) => { + const memberProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper)); + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + memberProposal, + )).to.be.rejectedWith('BadOrigin'); + }); + + + itSub.skip('[Negative] TechComm cannot promote/demote Fellowship member', async () => { + + }); + + itSub.skip('[Negative] TechComm member cannot promote/demote Fellowship member', async () => { + + }); + + itSub('[Negative] TechComm cannot add/remove a Council member', async ({helper}) => { + const newCouncilMember = helper.arrange.createEmptyAccount(); + const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address); + const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address); + + await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin'); + await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm member cannot add/remove a Council member', async ({helper}) => { + const newCouncilMember = helper.arrange.createEmptyAccount(); + const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address); + const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address); + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + addMemberProposal, + )).to.be.rejectedWith('BadOrigin'); + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + removeMemberProposal, + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm cannot set/clear Council prime member', async ({helper}) => { + const counselors = await initCouncil(donor, sudoer); + const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address); + const proposalForClear = await helper.council.membership.clearPrimeCall(); + + await expect(proposalFromAllCommittee(proposalForSet)).to.be.rejectedWith('BadOrigin'); + await expect(proposalFromAllCommittee(proposalForClear)).to.be.rejectedWith('BadOrigin'); + await clearCouncil(sudoer); + }); + + itSub('[Negative] TechComm member cannot set/clear Council prime member', async ({helper}) => { + const counselors = await initCouncil(donor, sudoer); + const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address); + const proposalForClear = await helper.council.membership.clearPrimeCall(); + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + proposalForSet, + )).to.be.rejectedWith('BadOrigin'); + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + proposalForClear, + )).to.be.rejectedWith('BadOrigin'); + await clearCouncil(sudoer); + }); + + itSub('[Negative] TechComm cannot add/remove a TechComm member', async ({helper}) => { + const newCommMember = helper.arrange.createEmptyAccount(); + const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address); + const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address); + + await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin'); + await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm member cannot add/remove a TechComm member', async ({helper}) => { + const newCommMember = helper.arrange.createEmptyAccount(); + const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address); + const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address); + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + addMemberProposal, + )).to.be.rejectedWith('BadOrigin'); + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + removeMemberProposal, + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm cannot remove a Fellowship member', async ({helper}) => { + const fellowship = await initFellowship(donor, sudoer); + + await expect(proposalFromAllCommittee(helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5))).to.be.rejectedWith('BadOrigin'); + await clearFellowship(sudoer); + }); + + itSub('[Negative] TechComm member cannot remove a Fellowship member', async ({helper}) => { + const fellowship = await initFellowship(donor, sudoer); + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5), + )).to.be.rejectedWith('BadOrigin'); + await clearFellowship(sudoer); + }); + + itSub('[Negative] TechComm member cannot fast-track Democracy proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0), + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm member cannot cancel Democracy proposals', async ({helper}) => { + const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n); + const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex; + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + helper.democracy.cancelProposalCall(proposalIndex), + )) + .to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm member cannot cancel ongoing Democracy referendums', async ({helper}) => { + await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper)); + const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); + const referendumIndex = startedEvent.referendumIndex; + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + helper.democracy.emergencyCancelCall(referendumIndex), + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm cannot blacklist Democracy proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); + + await expect(proposalFromAllCommittee(helper.democracy.blacklistCall(preimageHash))).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm member cannot blacklist Democracy proposals', async ({helper}) => { + const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); + await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + helper.democracy.blacklistCall(preimageHash), + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub.skip('[Negative] TechComm member cannot veto external Democracy proposals until the cool-off period pass', async () => { + + }); + + itSub('[Negative] TechComm member cannot cancel Fellowship referendums', async ({helper}) => { + const fellowship = await initFellowship(donor, sudoer); + const fellowshipProposer = fellowship[5][0]; + const proposal = dummyProposal(helper); + + const submitResult = await helper.fellowship.referenda.submit( + fellowshipProposer, + fellowshipPropositionOrigin, + proposal, + defaultEnactmentMoment, + ); + + const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; + + await expect(helper.technicalCommittee.collective.execute( + techcomms.andy, + helper.fellowship.referenda.cancelCall(referendumIndex), + )).to.be.rejectedWith('BadOrigin'); + }); + + itSub('[Negative] TechComm referendum cannot be closed until the voting threshold is met', async ({helper}) => { + const committeeSize = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length as any as number; + expect(committeeSize).is.greaterThan(1); + const proposeResult = await helper.technicalCommittee.collective.propose( + techcomms.andy, + dummyProposalCall(helper), + committeeSize, + ); + + const committeeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult); + const proposalIndex = committeeProposedEvent.proposalIndex; + const proposalHash = committeeProposedEvent.proposalHash; + + await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true); + + await expect(helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly'); + }); +}); --- /dev/null +++ b/js-packages/tests/governance/util.ts @@ -0,0 +1,224 @@ +import type {IKeyringPair} from '@polkadot/types/types'; +import {xxhashAsHex} from '@polkadot/util-crypto'; +import type {u32} from '@polkadot/types-codec'; +import {usingPlaygrounds, expect} from '../util/index.js'; +import {UniqueHelper} from '@unique/playgrounds/unique.js'; +import {DevUniqueHelper} from '@unique/playgrounds/unique.dev.js'; + +export const democracyLaunchPeriod = 35; +export const democracyVotingPeriod = 35; +export const councilMotionDuration = 35; +export const democracyEnactmentPeriod = 40; +export const democracyFastTrackVotingPeriod = 5; + +export const fellowshipRankLimit = 7; +export const fellowshipPropositionOrigin = 'FellowshipProposition'; +export const fellowshipPreparePeriod = 3; +export const fellowshipConfirmPeriod = 3; +export const fellowshipMinEnactPeriod = 1; + +export const defaultEnactmentMoment = {After: 0}; + +export const democracyTrackId = 10; +export const democracyTrackMinRank = 3; +const twox128 = (data: any) => xxhashAsHex(data, 128); +export interface ICounselors { + alex: IKeyringPair; + ildar: IKeyringPair; + charu: IKeyringPair; + filip: IKeyringPair; + irina: IKeyringPair; +} +export interface ITechComms { + greg: IKeyringPair; + andy: IKeyringPair; + constantine: IKeyringPair; +} + +export async function initCouncil(donor: IKeyringPair, superuser: IKeyringPair) { + let counselors: IKeyringPair[] = []; + + await usingPlaygrounds(async (helper) => { + const [alex, ildar, charu, filip, irina] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n, 10_000n, 10_000n], donor); + const sudo = helper.getSudo(); + { + const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as []; + if(members.length != 0) { + await clearCouncil(superuser); + } + } + const expectedMembers = [alex, ildar, charu, filip, irina]; + for(const member of expectedMembers) { + await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.addMember', [member.address]); + } + await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.setPrime', [alex.address]); + { + const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON(); + expect(members).to.containSubset(expectedMembers.map((x: IKeyringPair) => x.address)); + expect(members.length).to.be.equal(expectedMembers.length); + } + + counselors = [alex, ildar, charu, filip, irina]; + }); + return { + alex: counselors[0], + ildar: counselors[1], + charu: counselors[2], + filip: counselors[3], + irina: counselors[4], + }; +} + +export async function clearCouncil(superuser: IKeyringPair) { + await usingPlaygrounds(async (helper) => { + let members = (await helper.callRpc('api.query.councilMembership.members')).toJSON(); + if(members.length) { + const sudo = helper.getSudo(); + for(const address of members) { + await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.removeMember', [address]); + } + members = (await helper.callRpc('api.query.councilMembership.members')).toJSON(); + } + expect(members).to.be.deep.equal([]); + }); +} + + +export async function initTechComm(donor: IKeyringPair, superuser: IKeyringPair) { + let techcomms: IKeyringPair[] = []; + + await usingPlaygrounds(async (helper) => { + const [greg, andy, constantine] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n], donor); + const sudo = helper.getSudo(); + { + const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON() as []; + if(members.length != 0) { + await clearTechComm(superuser); + } + } + await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [greg.address]); + await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [andy.address]); + await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [constantine.address]); + await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.setPrime', [greg.address]); + { + const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON(); + expect(members).to.containSubset([greg.address, andy.address, constantine.address]); + expect(members.length).to.be.equal(3); + } + + techcomms = [greg, andy, constantine]; + }); + + return { + greg: techcomms[0], + andy: techcomms[1], + constantine: techcomms[2], + }; +} + +export async function clearTechComm(superuser: IKeyringPair) { + await usingPlaygrounds(async (helper) => { + let members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON(); + if(members.length) { + const sudo = helper.getSudo(); + for(const address of members) { + await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.removeMember', [address]); + } + members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON(); + } + expect(members).to.be.deep.equal([]); + }); +} + +export async function initFellowship(donor: IKeyringPair, sudoer: IKeyringPair) { + const numMembersInRank = 3; + const memberBalance = 5000n; + const members: IKeyringPair[][] = []; + + await usingPlaygrounds(async (helper) => { + const currentFellows = await helper.getApi().query.fellowshipCollective.members.keys(); + + if(currentFellows.length != 0) { + await clearFellowship(sudoer); + } + for(let i = 0; i < fellowshipRankLimit; i++) { + const rankMembers = await helper.arrange.createAccounts( + Array(numMembersInRank).fill(memberBalance), + donor, + ); + + for(const member of rankMembers) { + await helper.getSudo().fellowship.collective.addMember(sudoer, member.address); + + for(let rank = 0; rank < i; rank++) { + await helper.getSudo().fellowship.collective.promote(sudoer, member.address); + } + } + + members.push(rankMembers); + } + }); + + return members; +} + +export async function clearFellowship(sudoer: IKeyringPair) { + await usingPlaygrounds(async (helper) => { + const fellowship = (await helper.getApi().query.fellowshipCollective.members.keys()) + .map((key) => key.args[0].toString()); + for(const member of fellowship) { + await helper.getSudo().fellowship.collective.removeMember(sudoer, member, fellowshipRankLimit); + } + }); +} + +export async function clearFellowshipReferenda(sudoer: IKeyringPair) { + await usingPlaygrounds(async (helper) => { + const proposalsCount = (await helper.getApi().query.fellowshipReferenda.referendumCount()) as u32; + for(let i = 0; i < proposalsCount.toNumber(); i++) { + await helper.getSudo().fellowship.referenda.cancel(sudoer, i); + } + }); +} + +export async function hardResetFellowshipReferenda(sudoer: IKeyringPair) { + await usingPlaygrounds(async (helper) => { + const api = helper.getApi(); + const prefix = twox128('FellowshipReferenda'); + await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100))); + }); +} + +export async function hardResetDemocracy(sudoer: IKeyringPair) { + await usingPlaygrounds(async (helper) => { + const api = helper.getApi(); + const prefix = twox128('Democracy'); + await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100))); + }); +} + +export async function hardResetGovScheduler(sudoer: IKeyringPair) { + await usingPlaygrounds(async (helper) => { + const api = helper.getApi(); + const prefix = twox128('GovScheduler'); + await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 500))); + }); +} + +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); + } + } +} + +export function dummyProposalCall(helper: UniqueHelper) { + return helper.constructApiCall('api.tx.system.remark', ['dummy proposal' + (new Date()).getTime()]); +} + +export function dummyProposal(helper: UniqueHelper) { + return { + Inline: dummyProposalCall(helper).method.toHex(), + }; +} --- /dev/null +++ b/js-packages/tests/inflation.seqtest.ts @@ -0,0 +1,58 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, usingPlaygrounds} from './util/index.js'; + +// todo:playgrounds requires sudo, look into on the later stage +describe('integration test: Inflation', () => { + let superuser: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (_, privateKey) => { + superuser = await privateKey('//Alice'); + }); + }); + + itSub('First year inflation is 10%', async ({helper}) => { + // Make sure non-sudo can't start inflation + const [bob] = await helper.arrange.createAccounts([10n], superuser); + + await expect(helper.executeExtrinsic(bob, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/); + + // Make sure superuser can't start inflation without explicit sudo + await expect(helper.executeExtrinsic(superuser, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/); + + // Start inflation on relay block 1 (Alice is sudo) + const tx = helper.constructApiCall('api.tx.inflation.startInflation', [1]); + await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [tx])).to.not.be.rejected; + + const blockInterval = (helper.getApi().consts.inflation.inflationBlockInterval as any).toBigInt(); + const totalIssuanceStart = ((await helper.callRpc('api.query.inflation.startingYearTotalIssuance', [])) as any).toBigInt(); + const blockInflation = (await helper.callRpc('api.query.inflation.blockInflation', []) as any).toBigInt(); + + const YEAR = 5259600n; // 6-second block. Blocks in one year + // const YEAR = 2629800n; // 12-second block. Blocks in one year + + const totalExpectedInflation = totalIssuanceStart / 10n; + const totalActualInflation = blockInflation * YEAR / blockInterval; + + const tolerance = 0.00001; // Relative difference per year between theoretical and actual inflation + const expectedInflation = totalExpectedInflation / totalActualInflation - 1n; + + expect(Math.abs(Number(expectedInflation))).to.be.lessThanOrEqual(tolerance); + }); +}); --- /dev/null +++ b/js-packages/tests/limits.test.ts @@ -0,0 +1,474 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js'; + +describe('Number of tokens per address (NFT)', () => { + let alice: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + + itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 20}); + + for(let i = 0; i < 10; i++){ + await expect(collection.mintToken(alice)).to.be.not.rejected; + } + await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); + for(let i = 1; i < 11; i++) { + await expect(collection.burnToken(alice, i)).to.be.not.rejected; + } + await collection.burn(alice); + }); + + itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 1}); + + await collection.mintToken(alice); + await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); + + await collection.burnToken(alice, 1); + await expect(collection.burn(alice)).to.be.not.rejected; + }); +}); + +describe('Number of tokens per address (ReFungible)', () => { + let alice: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + + itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 20}); + + for(let i = 0; i < 10; i++){ + await expect(collection.mintToken(alice, 10n)).to.be.not.rejected; + } + await expect(collection.mintToken(alice, 10n)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); + for(let i = 1; i < 11; i++) { + await expect(collection.burnToken(alice, i, 10n)).to.be.not.rejected; + } + await collection.burn(alice); + }); + + itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 1}); + + await collection.mintToken(alice); + await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); + + await collection.burnToken(alice, 1); + await expect(collection.burn(alice)).to.be.not.rejected; + }); +}); + +// todo:playgrounds skipped ~ postponed +describe.skip('Sponsor timeout (NFT) (only for special chain limits test)', () => { + /*let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + alice = privateKeyWrapper('//Alice'); + bob = privateKeyWrapper('//Bob'); + charlie = privateKeyWrapper('//Charlie'); + }); + }); + + itSub.skip('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => { + const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); + await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7}); + const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); + await setCollectionSponsorExpectSuccess(collectionId, alice.address); + await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); + await transferExpectSuccess(collectionId, tokenId, alice, bob); + const aliceBalanceBefore = await getFreeBalance(alice); + + // check setting SponsorTimeout = 5, fail + await waitNewBlocks(5); + await transferExpectSuccess(collectionId, tokenId, bob, charlie); + const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); + + // check setting SponsorTimeout = 7, success + await waitNewBlocks(2); // 5 + 2 + await transferExpectSuccess(collectionId, tokenId, charlie, bob); + const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; + //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); + await destroyCollectionExpectSuccess(collectionId); + }); + + itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => { + + const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); + await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1}); + const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); + await setCollectionSponsorExpectSuccess(collectionId, alice.address); + await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); + await transferExpectSuccess(collectionId, tokenId, alice, bob); + const aliceBalanceBefore = await getFreeBalance(alice); + + // check setting SponsorTimeout = 1, fail + await waitNewBlocks(1); + await transferExpectSuccess(collectionId, tokenId, bob, charlie); + const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); + + // check setting SponsorTimeout = 5, success + await waitNewBlocks(4); + await transferExpectSuccess(collectionId, tokenId, charlie, bob); + const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; + //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); + await destroyCollectionExpectSuccess(collectionId); + }); +}); + +describe.skip('Sponsor timeout (Fungible) (only for special chain limits test)', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + alice = privateKeyWrapper('//Alice'); + bob = privateKeyWrapper('//Bob'); + charlie = privateKeyWrapper('//Charlie'); + }); + }); + + itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => { + const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); + await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7}); + const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible'); + await setCollectionSponsorExpectSuccess(collectionId, alice.address); + await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); + await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible'); + await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); + const aliceBalanceBefore = await getFreeBalance(alice); + + // check setting SponsorTimeout = 5, fail + await waitNewBlocks(5); + await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); + const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); + + // check setting SponsorTimeout = 7, success + await waitNewBlocks(2); // 5 + 2 + await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); + const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; + //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); + + await destroyCollectionExpectSuccess(collectionId); + }); + + itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => { + + const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); + await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1}); + const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible'); + await setCollectionSponsorExpectSuccess(collectionId, alice.address); + await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); + await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible'); + await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); + const aliceBalanceBefore = await getFreeBalance(alice); + + // check setting SponsorTimeout = 1, fail + await waitNewBlocks(1); + await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); + const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); + + // check setting SponsorTimeout = 5, success + await waitNewBlocks(4); + await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); + const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; + //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); + + await destroyCollectionExpectSuccess(collectionId); + }); +}); + +describe.skip('Sponsor timeout (ReFungible) (only for special chain limits test)', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + alice = privateKeyWrapper('//Alice'); + bob = privateKeyWrapper('//Bob'); + charlie = privateKeyWrapper('//Charlie'); + }); + }); + + itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => { + const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); + await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7}); + const tokenId = await createItemExpectSuccess(alice, collectionId, 'ReFungible'); + await setCollectionSponsorExpectSuccess(collectionId, alice.address); + await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); + await transferExpectSuccess(collectionId, tokenId, alice, bob, 100, 'ReFungible'); + const aliceBalanceBefore = await getFreeBalance(alice); + + // check setting SponsorTimeout = 5, fail + await waitNewBlocks(5); + await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible'); + const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); + + // check setting SponsorTimeout = 7, success + await waitNewBlocks(2); // 5 + 2 + await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible'); + const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; + //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); + await destroyCollectionExpectSuccess(collectionId); + }); + + itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => { + + const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); + await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1}); + const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); + await setCollectionSponsorExpectSuccess(collectionId, alice.address); + await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); + await transferExpectSuccess(collectionId, tokenId, alice, bob); + const aliceBalanceBefore = await getFreeBalance(alice); + + // check setting SponsorTimeout = 1, fail + await waitNewBlocks(1); + await transferExpectSuccess(collectionId, tokenId, bob, charlie); + const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); + + // check setting SponsorTimeout = 5, success + await waitNewBlocks(4); + await transferExpectSuccess(collectionId, tokenId, charlie, bob); + const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); + expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; + //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); + await destroyCollectionExpectSuccess(collectionId); + });*/ +}); + +describe('Collection zero limits (NFT)', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + }); + }); + + itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 0}); + + for(let i = 0; i < 10; i++){ + await collection.mintToken(alice); + } + await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); + }); + + itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setLimits(alice, {sponsorTransferTimeout: 0}); + const token = await collection.mintToken(alice); + + await collection.setSponsor(alice, alice.address); + await collection.confirmSponsorship(alice); + + await token.transfer(alice, {Substrate: bob.address}); + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); + + // check setting SponsorTimeout = 0, success with next block + await helper.wait.newBlocks(1); + await token.transfer(bob, {Substrate: charlie.address}); + const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address); + expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true; + }); +}); + +describe('Collection zero limits (Fungible)', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + }); + }); + + itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}); + await collection.setLimits(alice, {sponsorTransferTimeout: 0}); + await collection.mint(alice, 3n); + + await collection.setSponsor(alice, alice.address); + await collection.confirmSponsorship(alice); + + await collection.transfer(alice, {Substrate: bob.address}, 2n); + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); + + // check setting SponsorTimeout = 0, success with next block + await helper.wait.newBlocks(1); + await collection.transfer(bob, {Substrate: charlie.address}); + const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address); + expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true; + }); +}); + +describe('Collection zero limits (ReFungible)', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + }); + }); + + itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 0}); + for(let i = 0; i < 10; i++){ + await collection.mintToken(alice); + } + await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); + }); + + itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + await collection.setLimits(alice, {sponsorTransferTimeout: 0}); + const token = await collection.mintToken(alice, 3n); + + await collection.setSponsor(alice, alice.address); + await collection.confirmSponsorship(alice); + + await token.transfer(alice, {Substrate: bob.address}, 2n); + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); + + // check setting SponsorTimeout = 0, success with next block + await helper.wait.newBlocks(1); + await token.transfer(bob, {Substrate: charlie.address}); + const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address); + expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true; + }); +}); + +describe('Effective collection limits (NFT)', () => { + let alice: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + + itSub('Effective collection limits', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setLimits(alice, {ownerCanTransfer: true}); + + { + // Check that limits are undefined + const collectionInfo = await collection.getData(); + const limits = collectionInfo?.raw.limits; + expect(limits).to.be.any; + + expect(limits.accountTokenOwnershipLimit).to.be.null; + expect(limits.sponsoredDataSize).to.be.null; + expect(limits.sponsoredDataRateLimit).to.be.null; + expect(limits.tokenLimit).to.be.null; + expect(limits.sponsorTransferTimeout).to.be.null; + expect(limits.sponsorApproveTimeout).to.be.null; + expect(limits.ownerCanTransfer).to.be.true; + expect(limits.ownerCanDestroy).to.be.null; + expect(limits.transfersEnabled).to.be.null; + } + + { // Check that limits is undefined for non-existent collection + const limits = await helper.collection.getEffectiveLimits(999999); + expect(limits).to.be.null; + } + + { // Check that default values defined for collection limits + const limits = await collection.getEffectiveLimits(); + + expect(limits.accountTokenOwnershipLimit).to.be.eq(100000); + expect(limits.sponsoredDataSize).to.be.eq(2048); + expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null}); + expect(limits.tokenLimit).to.be.eq(4294967295); + expect(limits.sponsorTransferTimeout).to.be.eq(5); + expect(limits.sponsorApproveTimeout).to.be.eq(5); + expect(limits.ownerCanTransfer).to.be.true; + expect(limits.ownerCanDestroy).to.be.true; + expect(limits.transfersEnabled).to.be.true; + } + + { + // Check the values for collection limits + await collection.setLimits(alice, { + accountTokenOwnershipLimit: 99_999, + sponsoredDataSize: 1024, + tokenLimit: 123, + transfersEnabled: false, + }); + + const limits = await collection.getEffectiveLimits(); + + expect(limits.accountTokenOwnershipLimit).to.be.eq(99999); + expect(limits.sponsoredDataSize).to.be.eq(1024); + expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null}); + expect(limits.tokenLimit).to.be.eq(123); + expect(limits.sponsorTransferTimeout).to.be.eq(5); + expect(limits.sponsorApproveTimeout).to.be.eq(5); + expect(limits.ownerCanTransfer).to.be.true; + expect(limits.ownerCanDestroy).to.be.true; + expect(limits.transfersEnabled).to.be.false; + } + }); +}); --- /dev/null +++ b/js-packages/tests/maintenance.seqtest.ts @@ -0,0 +1,348 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {ApiPromise} from '@polkadot/api'; +import {expect, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js'; +import {itEth} from './eth/util/index.js'; +import {main as correctState} from './migrations/correctStateAfterMaintenance.js'; +import type {PalletBalancesIdAmount} from '@polkadot/types/lookup'; +import type {Vec} from '@polkadot/types-codec'; + +async function maintenanceEnabled(api: ApiPromise): Promise { + return (await api.query.maintenance.enabled()).toJSON() as boolean; +} + +describe('Integration Test: Maintenance Functionality', () => { + let superuser: IKeyringPair; + let donor: IKeyringPair; + let bob: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.Maintenance]); + superuser = await privateKey('//Alice'); + donor = await privateKey({url: import.meta.url}); + [bob] = await helper.arrange.createAccounts([10000n], donor); + + }); + }); + + describe('Maintenance Mode', () => { + before(async function() { + await usingPlaygrounds(async (helper) => { + if(await maintenanceEnabled(helper.getApi())) { + console.warn('\tMaintenance mode was left enabled BEFORE the test suite! Disabling it now.'); + await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled; + } + }); + }); + + itSub('Allows superuser to enable and disable maintenance mode - and disallows anyone else', async ({helper}) => { + // Make sure non-sudo can't enable maintenance mode + await expect(helper.executeExtrinsic(superuser, 'api.tx.maintenance.enable', []), 'on commoner enabling MM') + .to.be.rejectedWith(/BadOrigin/); + + // Set maintenance mode + 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; + + // Make sure non-sudo can't disable maintenance mode + await expect(helper.executeExtrinsic(bob, 'api.tx.maintenance.disable', []), 'on commoner disabling MM') + .to.be.rejectedWith(/BadOrigin/); + + // Disable maintenance mode + 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; + }); + + itSub('MM blocks unique pallet calls', async ({helper}) => { + // Can create an NFT collection before enabling the MM + const nftCollection = await helper.nft.mintCollection(bob, { + tokenPropertyPermissions: [{ + key: 'test', permission: { + collectionAdmin: true, + tokenOwner: true, + mutable: true, + }, + }], + }); + + // Can mint an NFT before enabling the MM + const nft = await nftCollection.mintToken(bob); + + // Can create an FT collection before enabling the MM + const ftCollection = await helper.ft.mintCollection(superuser); + + // Can mint an FT before enabling the MM + await expect(ftCollection.mint(superuser)).to.be.fulfilled; + + 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; + + // Unable to create a collection when the MM is enabled + await expect(helper.nft.mintCollection(superuser), 'cudo forbidden stuff') + .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/); + + // Unable to set token properties when the MM is enabled + await expect(nft.setProperties( + bob, + [{key: 'test', value: 'test-val'}], + )).to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/); + + // Unable to mint an NFT when the MM is enabled + await expect(nftCollection.mintToken(superuser)) + .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/); + + // Unable to mint an FT when the MM is enabled + await expect(ftCollection.mint(superuser)) + .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; + + // Can create a collection after disabling the MM + await expect(helper.nft.mintCollection(bob), 'MM is disabled, the collection should be created').to.be.fulfilled; + + // Can set token properties after disabling the MM + await nft.setProperties(bob, [{key: 'test', value: 'test-val'}]); + + // Can mint an NFT after disabling the MM + await nftCollection.mintToken(bob); + + // Can mint an FT after disabling the MM + await ftCollection.mint(superuser); + }); + + itSub.ifWithPallets('MM blocks unique pallet calls (Re-Fungible)', [Pallets.ReFungible], async ({helper}) => { + // Can create an RFT collection before enabling the MM + const rftCollection = await helper.rft.mintCollection(superuser); + + // Can mint an RFT before enabling the MM + await expect(rftCollection.mintToken(superuser)).to.be.fulfilled; + + 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; + + // Unable to mint an RFT when the MM is enabled + await expect(rftCollection.mintToken(superuser)) + .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; + + // Can mint an RFT after disabling the MM + await rftCollection.mintToken(superuser); + }); + + itSub('MM allows native token transfers and RPC calls', async ({helper}) => { + // We can use RPC before the MM is enabled + const totalCount = await helper.collection.getTotalCount(); + + // We can transfer funds before the MM is enabled + await expect(helper.balance.transferToSubstrate(superuser, bob.address, 2n)).to.be.fulfilled; + + 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; + + // RPCs work while in maintenance + expect(await helper.collection.getTotalCount()).to.be.deep.equal(totalCount); + + // We still able to transfer funds + await expect(helper.balance.transferToSubstrate(bob, superuser.address, 1n)).to.be.fulfilled; + + 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; + + // RPCs work after maintenance + expect(await helper.collection.getTotalCount()).to.be.deep.equal(totalCount); + + // Transfers work after maintenance + await expect(helper.balance.transferToSubstrate(bob, superuser.address, 1n)).to.be.fulfilled; + }); + + itSched.ifWithPallets('MM blocks scheduled calls and the scheduler itself', [Pallets.UniqueScheduler], async (scheduleKind, {helper}) => { + const collection = await helper.nft.mintCollection(bob); + + const nftBeforeMM = await collection.mintToken(bob); + const nftDuringMM = await collection.mintToken(bob); + const nftAfterMM = await collection.mintToken(bob); + + const [ + scheduledIdBeforeMM, + scheduledIdDuringMM, + scheduledIdBunkerThroughMM, + scheduledIdAttemptDuringMM, + scheduledIdAfterMM, + ] = scheduleKind == 'named' + ? helper.arrange.makeScheduledIds(5) + : new Array(5); + + const blocksToWait = 6; + + // Scheduling works before the maintenance + 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 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 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; + + await helper.wait.newBlocks(blocksToWait + 1); + // The owner should NOT change since the scheduled transaction should be rejected + 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(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 helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAfterMM}) + .nft.transferToken(bob, collection.collectionId, nftAfterMM.tokenId, {Substrate: superuser.address}); + + await helper.wait.newBlocks(blocksToWait + 1); + + expect(await nftAfterMM.getOwner()).to.be.deep.equal({Substrate: superuser.address}); + // The owner of the token scheduled for transaction *before* maintenance should now change *after* maintenance + expect(await nftDuringMM.getOwner()).to.be.deep.equal({Substrate: superuser.address}); + }); + + itEth('Disallows Ethereum transactions to execute while in maintenance', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); + + const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'B', 'C', ''); + + // Set maintenance mode + 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; + + const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + const tokenId = await contract.methods.nextTokenId().call(); + expect(tokenId).to.be.equal('1'); + + await expect(contract.methods.mintWithTokenURI(receiver, 'Test URI').send()) + .to.be.rejectedWith(/Returned error: unknown error/); + + await expect(contract.methods.ownerOf(tokenId).call()).rejectedWith(/token not found/); + + // Disable maintenance mode + 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; + }); + + itSub('Allows to enable and disable MM repeatedly', async ({helper}) => { + // Set maintenance mode + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []); + 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; + + // Disable maintenance mode + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []); + 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; + }); + + afterEach(async () => { + await usingPlaygrounds(async helper => { + if(helper.fetchMissingPalletNames([Pallets.Maintenance]).length != 0) return; + if(await maintenanceEnabled(helper.getApi())) { + console.warn('\tMaintenance mode was left enabled AFTER a test has finished! Be careful. Disabling it now.'); + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []); + } + expect(await maintenanceEnabled(helper.getApi()), 'Disastrous! Exited the test suite with maintenance mode on.').to.be.false; + }); + }); + }); + + describe('Integration Test: Maintenance mode & App Promo', () => { + let superuser: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.Maintenance]); + superuser = await privateKey('//Alice'); + }); + }); + + describe('Test AppPromo script for check state after Maintenance mode', () => { + before(async function () { + await usingPlaygrounds(async (helper) => { + if(await maintenanceEnabled(helper.getApi())) { + console.warn('\tMaintenance mode was left enabled BEFORE the test suite! Disabling it now.'); + await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled; + } + }); + }); + itSub('Can find and fix inconsistent state', async ({helper}) => { + const api = helper.getApi(); + + await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [api.tx.system.setStorage([ + // pendingUnstake(1 -> [superuser.address, 100UNQ]) + ['0x42b67acb8bd223c60d0c8f621ffefc0ae280fa2db99bd3827aac976de75af95f5153cb1f00942ff401000000', + '0x04d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000010632d5ec76b0500000000000000'], + // pendingUnstake(2 -> [superuser.address, 100UNQ]) + ['0x42b67acb8bd223c60d0c8f621ffefc0ae280fa2db99bd3827aac976de75af95f9eb2dcce60f37a2702000000', + '0x04d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000010632d5ec76b0500000000000000'], + // Balances.freezes(superuser.address -> freeze with app promo id and 200 UNQ ) + ['0xc2261276cc9d1f8598ea4b6a74b15c2fb1c0eb12e038e5c7f91e120ed4b7ebf1de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d', + '0x046170707374616b656170707374616b65000020c65abc8ed70a00000000000000'], + ])]); + + expect((await api.query.appPromotion.pendingUnstake(1)).toJSON()).to.be.deep.equal([[superuser.address, '0x00000000000000056bc75e2d63100000']]); + expect((await api.query.appPromotion.pendingUnstake(2)).toJSON()).to.be.deep.equal([[superuser.address, '0x00000000000000056bc75e2d63100000']]); + expect((await api.query.balances.freezes(superuser.address) as Vec) + .map(lock => ({id: lock.id.toUtf8(), amount: lock.amount.toBigInt()}))) + .to.be.deep.equal([{id: 'appstakeappstake', amount: 200000000000000000000n}]); + await correctState(); + + expect((await api.query.appPromotion.pendingUnstake(1)).toJSON()).to.be.deep.equal([]); + expect((await api.query.appPromotion.pendingUnstake(2)).toJSON()).to.be.deep.equal([]); + expect((await api.query.balances.freezes(superuser.address)).toJSON()).to.be.deep.equal([]); + + }); + + itSub('(!negative test!) Only works when Maintenance mode is disabled', async({helper}) => { + await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', [])).to.be.fulfilled; + await expect(correctState()).to.be.rejectedWith('The network is still in maintenance mode'); + await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled; + }); + }); + }); + + after(async () => { + await usingPlaygrounds(async(helper) => { + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []); + }); + }); +}); --- /dev/null +++ b/js-packages/tests/migrations/942057-appPromotion/README.md @@ -0,0 +1,42 @@ +# Update Procedure + +- Enable maintenance mode +- [Collect migration data using ChainQL](#stakers-data-loading) +- ❗️❗️❗️ Initiate the runtime upgrade only at this point ❗️❗️❗️ +- Wait for the upgrade to complete +- [Execute offchain migration](#execute-offchain-migration) +- Disable maintenance mode + +## Stakers Data Loading + +Set the environment variable (WS_RPC). For example, ws://localhost:9944. Execute the following command: + +```sh +chainql --tla-str=chainUrl= stakersParser.jsonnet > output.json +``` + +where `` - is the network address. + +Example for Opal: + +```sh +chainql --tla-str=chainUrl=wss://eu-ws-opal.unique.network:443 stakersParser.jsonnet > output.json +``` + +To install chainql, execute the following command: + +```sh +cargo install chainql +``` + +## Execute offchain migration + +To run, you need to set an environment variables: +- `SUPERUSER_SEED` – the sudo key seed. +- `WS_RPC` – the network address + +Run the migration by executing the following command: + +```sh +npx ts-node --esm executeMigration.ts +``` --- /dev/null +++ b/js-packages/tests/migrations/942057-appPromotion/collectData.ts @@ -0,0 +1,12 @@ +import {exec} from 'child_process'; +import path from 'path'; +import {dirname} from 'path'; +import {fileURLToPath} from 'url'; + +export const collectData = () => { + const dirName = dirname(fileURLToPath(import.meta.url)); + + const pathToScript = path.resolve(dirName, './stakersParser.jsonnet'); + const outputPath = path.resolve(dirName, './output.json'); + exec(`chainql --tla-str=chainUrl=ws://127.0.0.1:9944 ${pathToScript} > ${outputPath}`); +}; --- /dev/null +++ b/js-packages/tests/migrations/942057-appPromotion/executeMigration.ts @@ -0,0 +1,11 @@ +import {migrateLockedToFreeze} from './lockedToFreeze.js'; + + +const WS_RPC = process.env.WS_RPC || 'wss://ws-opal.unique.network:443'; +const SUPERUSER_SEED = process.env.SUPERUSER_SEED || ''; + +migrateLockedToFreeze({ + wsEndpoint: WS_RPC, + donorSeed: SUPERUSER_SEED, +}) + .catch(console.error); \ No newline at end of file --- /dev/null +++ b/js-packages/tests/migrations/942057-appPromotion/index.ts @@ -0,0 +1,12 @@ +import type {Migration} from '../../util/frankensteinMigrate.js'; +import {collectData} from './collectData.js'; +import {migrateLockedToFreeze} from './lockedToFreeze.js'; + +export const migration: Migration = { + async before() { + await collectData(); + }, + async after() { + await migrateLockedToFreeze(); + }, +}; \ No newline at end of file --- /dev/null +++ b/js-packages/tests/migrations/942057-appPromotion/lockedToFreeze.ts @@ -0,0 +1,259 @@ +// import { usingApi, privateKey, onlySign } from "./../../load/lib"; +import * as fs from 'fs'; +import {usingPlaygrounds} from '../../util/index.js'; +import path, {dirname} from 'path'; +import {isInteger, parse} from 'lossless-json'; +import {fileURLToPath} from 'url'; +import config from '../../config.js'; + + +const WS_ENDPOINT = config.substrateUrl; +const DONOR_SEED = '//Alice'; +const UPDATE_IF_VERSION = 942057; + +export function customNumberParser(value: any) { + return isInteger(value) ? BigInt(value) : parseFloat(value); +} + +export const migrateLockedToFreeze = async(options: { wsEndpoint: string; donorSeed: string } = { + wsEndpoint: WS_ENDPOINT, + donorSeed: DONOR_SEED, +}) => { + await usingPlaygrounds(async (helper, privateKey) => { + const api = helper.getApi(); + // 1. Check version equal 942057 or skip + console.log((api.consts.system.version as any).specVersion.toNumber()); + if((api.consts.system.version as any).specVersion.toNumber() != UPDATE_IF_VERSION) { + console.log("Version isn't 942057."); + return; + } + + // 2. Get sudo signer + const signer = await privateKey(options.donorSeed); + console.log('2. Getting sudo:', signer.address); + + // 3. Parse data to migrate + console.log('3. Parsing chainql results...'); + const dirName = dirname(fileURLToPath(import.meta.url)); + const parsingResult = parse(fs.readFileSync(path.resolve(dirName, 'output.json'), 'utf-8'), undefined, customNumberParser); + + const chainqlImportData = parsingResult as { + address: string; + balance: string; + account: { + fee_frozen: string, + free: string, + misc_frozen: string, + reserved: string, + }, + locks: { + amount: string, + id: string, + }[], + stakes: object, + unstakes: object, + }[]; + testChainqlData(chainqlImportData); + + const stakers = chainqlImportData.map((i) => i.address); + + // 3.1 Split into chunks by 100 + console.log('3.1 Splitting into chunks...'); + const stakersChunks = chunk(stakers, 100); + console.log('3.1 Done, total chunks:', stakersChunks.length); + + // 4. Get signer/sudo nonce + console.log('4. Getting sudo nonce...'); + const signerAccount = ( + await api.query.system.account(signer.address) + ).toJSON() as any; + + let nonce: number = signerAccount.nonce; + console.log('4. Sudo nonce is:', nonce); + + // 5. Only sign upgradeAccounts-transactions for each chunk + console.log('5. Signing transactions...'); + const signedTxs = []; + for(const chunk of stakersChunks) { + const tx = api.tx.sudo.sudo(api.tx.appPromotion.upgradeAccounts(chunk)); + const signed = tx.sign(signer, { + blockHash: api.genesisHash, + genesisHash: api.genesisHash, + runtimeVersion: api.runtimeVersion, + nonce: nonce++, + }); + signedTxs.push(signed); + } + + // 6. Send all signed transactions + console.log('6. Sending transactions...'); + const promises = signedTxs.map((tx) => api.rpc.author.submitAndWatchExtrinsic(tx)); + // 6.1 Wait all transactions settled + console.log('6.1 Waiting all transactions settled...'); + const res = await Promise.allSettled(promises); + + console.log('Wait 5 blocks for transactions to be included in a block...'); + await helper.wait.newBlocks(5); + // 6.2 Filter failed transactions + console.log('6.2 Getting failed transactions...'); + const failedTx = res.filter((r) => r.status == 'rejected') as PromiseRejectedResult[]; + console.log('6.2. total failedTxs:', failedTx.length); + + // 6.3 Log the reasons of failed tx + for(const tx of failedTx) { + console.log(tx.reason); + } + + // 7. Check balances for 10 blocks: + console.log('7. Check balances...'); + let blocksLeft = 10; + let notMigrated = stakers; + const suspiciousAccounts = []; + do { + console.log('blocks left:', blocksLeft); + const _notMigrated: string[] = []; + console.log('accounts to migrate...', notMigrated.length); + for(const accountToMigrate of notMigrated) { + let accountMigrated = true; + // 7.0 get data from chainql: + const oldAccount = chainqlImportData.find(acc => acc.address === accountToMigrate); + if(!oldAccount) { + console.log('Cannot find old account data for', accountMigrated); + accountMigrated = false; + _notMigrated.push(accountToMigrate); + continue; + } + + // 7.1 system.account + const balance = await api.query.system.account(accountToMigrate) as any; + // new balances + const free = balance.data.free; + const reserved = balance.data.reserved; + const frozen = balance.data.frozen; + // old balances + const oldFree = oldAccount.account.free; + const oldReserved = oldAccount.account.reserved; + const oldFrozen = oldAccount.account.fee_frozen; + // asserts new = old + if(oldFree.toString() !== free.toString()) { + console.log('Old free !== New free, which is probably not a problem', oldFree.toString(), free.toString()); + suspiciousAccounts.push(accountToMigrate); + } + if(oldFrozen.toString() !== frozen.toString()) { + console.log('Old frozen !== New frozen, which is probably not a problem', oldFrozen.toString(), frozen.toString()); + suspiciousAccounts.push(accountToMigrate); + } + if(oldReserved.toString() !== reserved.toString()) { + console.log('Old reserved !== New reserved, which is probably not a problem', oldReserved.toString(), reserved.toString()); + suspiciousAccounts.push(accountToMigrate); + } + + // 7.2 balances.locks: no id appstake + const locks = await helper.balance.getLocked(accountToMigrate); + const appPromoLocks = locks.filter(lock => lock.id === 'appstake'); + if(appPromoLocks.length !== 0) { + console.log('Account still has app-promo lock'); + accountMigrated = false; + } + + // 7.3 balances.freezes set... + let freezes = await api.query.balances.freezes(accountToMigrate) as any; + freezes = freezes.map((freez: any) => ({id: freez.id.toString(), amount: freez.amount.toString()})); + if(!freezes) { + console.log('Account does not have freezes'); + accountMigrated = false; + } else { + const oldAppPromoLocks = oldAccount.locks.filter(l => l.id === '0x6170707374616b65'); // get app promo locks + // should be only one freez for each account + if(freezes.length !== 1) { + console.log('freezes.length !== 1 and old appPromoLocks.length', freezes.length, oldAppPromoLocks.length); + accountMigrated = false; + } else { + const appPromoFreez = freezes[0]; + // freez-amount should be equal to migrated lock amount + if(appPromoFreez.amount.toString() !== oldAppPromoLocks[0].amount.toString()) { + console.log('freezes amount !== old appPromoLocks amount', appPromoFreez.amount.toString(), oldAppPromoLocks[0].amount.toString()); + accountMigrated = false; + } + // freez id should be correct + if(appPromoFreez.id !== '0x6170707374616b656170707374616b65') { + console.log('Got freez with incorrect id:', appPromoFreez.id); + accountMigrated = false; + } + } + } + + // 7.4 Stakes number the same + const stakesNumber = await helper.staking.getStakesNumber({Substrate: accountToMigrate}); + const oldStakesNumber = oldAccount.stakes ? Object.keys(oldAccount.stakes).length : 0; + if(stakesNumber.toString() !== oldStakesNumber.toString()) { + console.log('Old stakes number !== New stakes number', oldStakesNumber, stakesNumber); + accountMigrated = false; + } + + // 7.5 Total pendingUnstake + total staked = old locked + const pendingUnstakes = await helper.staking.getPendingUnstake({Substrate: accountToMigrate}); + const totalStaked = await helper.staking.getTotalStaked({Substrate: accountToMigrate}); + const totalBalanceInAppPromo = pendingUnstakes + totalStaked; + if(totalBalanceInAppPromo.toString() !== oldAccount.balance.toString()) { + console.log('totalBalanceInAppPromo !== old locked in app promo', totalBalanceInAppPromo.toString(), oldAccount.balance.toString()); + accountMigrated = false; + } + + // 8 Add to not-migrated + if(!accountMigrated) { + console.log('Add to not migrated:', accountToMigrate); + _notMigrated.push(accountToMigrate); + } + } + + blocksLeft--; + notMigrated = _notMigrated; + await helper.wait.newBlocks(1); + } while(blocksLeft > 0 && notMigrated.length !== 0); + + console.log('Not migrated accounts...', notMigrated.length); + if(suspiciousAccounts.length > 0) { + console.log('Saving suspicious accounts to suspicious.json:'); + fs.writeFileSync('./suspicious.json', JSON.stringify(suspiciousAccounts)); + } + if(notMigrated.length > 0) { + console.log('Saving not migrated list to failed.json:'); + notMigrated.forEach(console.log); + fs.writeFileSync('./failed.json', JSON.stringify(notMigrated)); + process.exit(1); + } else { + console.log('Migration success'); + } + }, options.wsEndpoint); +}; + +const chunk = (arr: T[], size: number) => + Array.from({length: Math.ceil(arr.length / size)}, (_: any, i: number) => + arr.slice(i * size, i * size + size)); + +const testChainqlData = (data: any) => { + const wrongData = []; + for(const account of data) { + try { + if(account.address == null) throw Error('no address in data'); + if(account.balance == null) throw Error('no balance in data'); + if(account.account == null) throw Error('no account in data'); + if(account.account.fee_frozen == null) throw Error('no account.fee_frozen in data'); + if(account.account.misc_frozen == null) throw Error('no account.misc_frozen in data'); + if(account.account.free == null) throw Error('no account.free in data'); + if(account.account.reserved == null) throw Error('no account.reserved in data'); + if(account.locks == null) throw Error('no locks in data'); + if(account.locks[0].amount == null) throw Error('no locks.amount in data'); + if(account.locks[0].id == null) throw Error('no locks.id in data'); + } catch (error) { + wrongData.push(account.address); + console.log((error as Error).message, account.address); + } + if(wrongData.length > 0) { + console.log(data); + throw Error('Chainql data not correct'); + } + } + console.log('Chainql data correct'); +}; --- /dev/null +++ b/js-packages/tests/migrations/942057-appPromotion/stakersParser.jsonnet @@ -0,0 +1,29 @@ +function(chainUrl) + + local + state = cql.chain(chainUrl).latest, + locked_balances = state.Balances.Locks._preloadKeys, + accountsRaw = state.System.Account._preloadKeys, + stakers = state.AppPromotion.Staked._preloadKeys, + unstakes = state.AppPromotion.PendingUnstake._preloadKeys, + locks = [ + { [k]: std.filter(function(l) l.id == '0x6170707374616b65', locked_balances[k]) } + for k in std.objectFields(locked_balances) + ], + unstakersData = [ + { [pair[0]]: { block: block, value: pair[1] } } + + for block in std.objectFields(unstakes) + for pair in unstakes[block] + ], + non_empty_locks = std.prune(locks) + ; + + std.map(function(a) { + address: std.objectFields(a)[0], + balance: a[std.objectFields(a)[0]][0].amount, + account: accountsRaw[std.objectFields(a)[0]].data, + locks: locked_balances[std.objectFields(a)[0]], + stakes: if std.objectHas(stakers, std.objectFields(a)[0]) then stakers[std.objectFields(a)[0]] else null, + unstakes: std.filterMap(function(b) std.objectHas(b, std.objectFields(a)[0]), function(c) std.objectValues(c)[0], unstakersData), + }, non_empty_locks) --- /dev/null +++ b/js-packages/tests/migrations/correctStateAfterMaintenance.ts @@ -0,0 +1,69 @@ +import config from '../config.js'; +import {usingPlaygrounds} from '../util/index.js'; +import type {u32} from '@polkadot/types-codec'; + +const WS_ENDPOINT = config.substrateUrl; +const DONOR_SEED = '//Alice'; + +export const main = async(options: { wsEndpoint: string; donorSeed: string } = { + wsEndpoint: WS_ENDPOINT, + donorSeed: DONOR_SEED, +}) => { + await usingPlaygrounds(async (helper, privateKey) => { + const api = helper.getApi(); + + if((await api.query.maintenance.enabled()).valueOf()) { + throw Error('The network is still in maintenance mode'); + } + + const pendingBlocks = ( + await api.query.appPromotion.pendingUnstake.entries() + ).map(([k, _v]) => + (k.args[0] as u32).toNumber()); + + const currentBlock = (await api.query.system.number() as u32).toNumber(); + + const filteredBlocks = pendingBlocks.filter(b => currentBlock > b); + + if(filteredBlocks.length != 0) { + console.log(`During maintenance mode, ${filteredBlocks.length} block(s) were not processed. Number(s): ${filteredBlocks}`); + } else { + console.log('Nothing to change'); + return; + } + + const skippedBlocks = chunk(filteredBlocks, 10); + + const signer = await privateKey(options.donorSeed); + + const txs = skippedBlocks.map((b) => + api.tx.sudo.sudo(api.tx.appPromotion.forceUnstake(b))); + + + const promises = txs.map((tx) => () => helper.signTransaction(signer, tx)); + + await Promise.allSettled(promises.map((p) => p())); + + const failedBlocks: number[] = []; + let isSuccess = true; + + for(const b of filteredBlocks) { + if(((await api.query.appPromotion.pendingUnstake(b)).toJSON() as any[]).length != 0) { + failedBlocks.push(b); + isSuccess = false; + } + } + + if(isSuccess) { + console.log('Done. %d block(s) were processed.', filteredBlocks.length); + } else { + throw new Error(`Something went wrong. Block(s) have not been processed: ${failedBlocks}`); + } + + + }, options.wsEndpoint); +}; + +const chunk = (arr: T[], size: number) => + Array.from({length: Math.ceil(arr.length / size)}, (_: any, i: number) => + arr.slice(i * size, i * size + size)); --- /dev/null +++ b/js-packages/tests/migrations/runCheckState.ts @@ -0,0 +1,10 @@ +import {main} from './correctStateAfterMaintenance.js'; + +main({ + wsEndpoint: process.env.WS_RPC!, + donorSeed: process.env.SUPERUSER_SEED!, +}).then(() => process.exit(0)) + .catch((e) => { + console.error(e); + process.exit(1); + }); --- /dev/null +++ b/js-packages/tests/nativeFungible.test.ts @@ -0,0 +1,221 @@ +// Copyright 2019-2023 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, usingPlaygrounds} from './util/index.js'; + +describe('Native fungible', () => { + let root: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + root = await privateKey('//Alice'); + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); + }); + }); + + itSub('destroy_collection()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.burn(alice)).to.be.rejectedWith('common.UnsupportedOperation'); + await expect(helper.executeExtrinsic(alice, 'api.tx.unique.destroyCollection', [0])).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('add_to_allow_list()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.addToAllowList(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('remove_from_allow_list()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.removeFromAllowList(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('change_collection_owner()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.changeOwner(alice, bob.address)).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('add_collection_admin()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.addAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('remove_collection_admin()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.removeAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('set_collection_sponsor()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.setSponsor(alice, bob.address)).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('confirm_sponsorship()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.confirmSponsorship(alice)).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('remove_collection_sponsor()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.removeSponsor(alice)).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('create_item()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.mint(alice, 100n, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('set_collection_properties()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.setProperties(alice, [{key: 'value'}])).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('delete_collection_properties()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.deleteProperties(alice, ['key'])).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('set_token_properties()', async ({helper}) => { + await expect(helper.executeExtrinsic( + alice, + 'api.tx.unique.setTokenProperties', + [0, 0, [{key: 'value'}]], + true, + )).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('delete_token_properties()', async ({helper}) => { + await expect(helper.executeExtrinsic( + alice, + 'api.tx.unique.deleteTokenProperties', + [0, 0, ['key']], + true, + )).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('set_transfers_enabled_flag()', async ({helper}) => { + await expect(helper.executeExtrinsic( + alice, + 'api.tx.unique.setTransfersEnabledFlag', + [0, true], + true, + )).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('burn_item()', async ({helper}) => { + await expect(helper.executeExtrinsic( + alice, + 'api.tx.unique.burnItem', + [0, 0, 100n], + true, + )).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('burn_from()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.burnTokens(alice, 100n)).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('transfer()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + const balanceAliceBefore = await helper.balance.getSubstrate(alice.address); + const balanceBobBefore = await helper.balance.getSubstrate(bob.address); + await collection.transfer(alice, {Substrate: bob.address}, 100n); + const balanceAliceAfter = await helper.balance.getSubstrate(alice.address); + const balanceBobAfter = await helper.balance.getSubstrate(bob.address); + expect(balanceAliceBefore - balanceAliceAfter > 100n).to.be.true; + expect(balanceBobAfter - balanceBobBefore === 100n).to.be.true; + }); + + itSub('transfer_from()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + const balanceAliceBefore = await helper.balance.getSubstrate(alice.address); + const balanceBobBefore = await helper.balance.getSubstrate(bob.address); + + await collection.transferFrom(alice, {Substrate: alice.address}, {Substrate: bob.address}, 100n); + + const balanceAliceAfter = await helper.balance.getSubstrate(alice.address); + const balanceBobAfter = await helper.balance.getSubstrate(bob.address); + expect(balanceAliceBefore - balanceAliceAfter > 100n).to.be.true; + expect(balanceBobAfter - balanceBobBefore === 100n).to.be.true; + + await expect(collection.transferFrom(alice, {Substrate: bob.address}, {Substrate: alice.address}, 100n)).to.be.rejectedWith('common.ApprovedValueTooLow'); + }); + + itSub('approve()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.approveTokens(alice, {Substrate: bob.address}, 100n)).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('approve_from()', async ({helper}) => { + await expect(helper.executeExtrinsic( + alice, + 'api.tx.unique.approveFrom', + [{Substrate: alice.address}, {Substrate: bob.address}, 0, 0, 100n], + true, + )).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('set_collection_limits()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.setLimits(alice, {accountTokenOwnershipLimit: 1})).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('set_collection_permissions()', async ({helper}) => { + const collection = helper.ft.getCollectionObject(0); + await expect(collection.setPermissions(alice, {access: 'AllowList'})).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('repartition()', async ({helper}) => { + await expect(helper.executeExtrinsic( + alice, + 'api.tx.unique.repartition', + [0, 0, 100n], + true, + )).to.be.rejectedWith('unique.RepartitionCalledOnNonRefungibleCollection'); + }); + + itSub('force_repair_collection()', async ({helper}) => { + await expect(helper.executeExtrinsic( + alice, + 'api.tx.unique.forceRepairCollection', + [0], + true, + )).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('force_repair_item()', async ({helper}) => { + await expect(helper.getSudo().executeExtrinsic( + root, + 'api.tx.unique.forceRepairItem', + [0, 0], + true, + )).to.be.rejectedWith('common.UnsupportedOperation'); + }); + + itSub('Nest into NFT token()', async ({helper}) => { + const nftCollection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const targetToken = await nftCollection.mintToken(alice); + + const collection = helper.ft.getCollectionObject(0); + await collection.transfer(alice, targetToken.nestingAccount(), 100n); + + await collection.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 100n); + }); +}); \ No newline at end of file --- /dev/null +++ b/js-packages/tests/nesting/collectionProperties.seqtest.ts @@ -0,0 +1,61 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js'; + +describe('Integration Test: Collection Properties with sudo', () => { + let superuser: IKeyringPair; + let alice: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + superuser = await privateKey('//Alice'); + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([100n], donor); + }); + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'ft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => { + before(async function() { + // eslint-disable-next-line require-await + await usingPlaygrounds(async helper => { + requirePalletsOrSkip(this, helper, testSuite.requiredPallets); + }); + }); + + itSub('Repairing an unbroken collection\'s properties preserves the consumed space', async({helper}) => { + const properties = [ + {key: 'sea-creatures', value: 'mermaids'}, + {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'}, + ]; + const collection = await helper[testSuite.mode].mintCollection(alice, {properties}); + + const newProperty = {key: 'space', value: ' '.repeat(4096)}; + await collection.setProperties(alice, [newProperty]); + const originalSpace = await collection.getPropertiesConsumedSpace(); + expect(originalSpace).to.be.equal(sizeOfProperty(properties[0]) + sizeOfProperty(properties[1]) + sizeOfProperty(newProperty)); + + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true); + const recomputedSpace = await collection.getPropertiesConsumedSpace(); + expect(recomputedSpace).to.be.equal(originalSpace); + }); + })); +}); --- /dev/null +++ b/js-packages/tests/nesting/collectionProperties.test.ts @@ -0,0 +1,326 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js'; + +describe('Integration Test: Collection Properties', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([200n, 10n], donor); + }); + }); + + itSub('Properties are initially empty', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice); + expect(await collection.getProperties()).to.be.empty; + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'ft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => { + before(async function() { + // eslint-disable-next-line require-await + await usingPlaygrounds(async helper => { + requirePalletsOrSkip(this, helper, testSuite.requiredPallets); + }); + }); + + itSub('Sets properties for a collection', async ({helper}) => { + const collection = await helper[testSuite.mode].mintCollection(alice); + + // As owner + await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}])).to.be.fulfilled; + + await collection.addAdmin(alice, {Substrate: bob.address}); + + // As administrator + await expect(collection.setProperties(bob, [{key: 'black_hole'}])).to.be.fulfilled; + + const properties = await collection.getProperties(); + expect(properties).to.include.deep.members([ + {key: 'electron', value: 'come bond'}, + {key: 'black_hole', value: ''}, + ]); + }); + + itSub('Check valid names for collection properties keys', async ({helper}) => { + const collection = await helper[testSuite.mode].mintCollection(alice); + + // alpha symbols + await expect(collection.setProperties(alice, [{key: 'answer'}])).to.be.fulfilled; + + // numeric symbols + await expect(collection.setProperties(alice, [{key: '451'}])).to.be.fulfilled; + + // underscore symbol + await expect(collection.setProperties(alice, [{key: 'black_hole'}])).to.be.fulfilled; + + // dash symbol + await expect(collection.setProperties(alice, [{key: '-'}])).to.be.fulfilled; + + // dot symbol + await expect(collection.setProperties(alice, [{key: 'once.in.a.long.long.while...', value: 'you get a little lost'}])).to.be.fulfilled; + + const properties = await collection.getProperties(); + expect(properties).to.include.deep.members([ + {key: 'answer', value: ''}, + {key: '451', value: ''}, + {key: 'black_hole', value: ''}, + {key: '-', value: ''}, + {key: 'once.in.a.long.long.while...', value: 'you get a little lost'}, + ]); + }); + + itSub('Changes properties of a collection', async ({helper}) => { + const collection = await helper[testSuite.mode].mintCollection(alice); + + await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: ''}])).to.be.fulfilled; + + // Mutate the properties + await expect(collection.setProperties(alice, [{key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled; + + const properties = await collection.getProperties(); + expect(properties).to.include.deep.members([ + {key: 'electron', value: 'come bond'}, + {key: 'black_hole', value: 'LIGO'}, + ]); + }); + + itSub('Deletes properties of a collection', async ({helper}) => { + const collection = await helper[testSuite.mode].mintCollection(alice); + + await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled; + + await expect(collection.deleteProperties(alice, ['electron'])).to.be.fulfilled; + + const properties = await collection.getProperties(['black_hole', 'electron']); + expect(properties).to.be.deep.equal([ + {key: 'black_hole', value: 'LIGO'}, + ]); + }); + + itSub('Allows modifying a collection property multiple times with the same size', async({helper}) => { + const propKey = 'tok-prop'; + + const collection = await helper[testSuite.mode].mintCollection(alice); + + const maxCollectionPropertiesSize = 40960; + + const propDataSize = 4096; + + let propDataChar = 'a'; + const makeNewPropData = () => { + propDataChar = String.fromCharCode(propDataChar.charCodeAt(0) + 1); + return `${propDataChar}`.repeat(propDataSize); + }; + + const property = {key: propKey, value: makeNewPropData()}; + await collection.setProperties(alice, [property]); + const originalSpace = await collection.getPropertiesConsumedSpace(); + expect(originalSpace).to.be.equal(sizeOfProperty(property)); + + const sameSizePropertiesPossibleNum = maxCollectionPropertiesSize / propDataSize; + + // It is possible to modify a property as many times as needed. + // It will not consume any additional space. + for(let i = 0; i < sameSizePropertiesPossibleNum + 1; i++) { + await collection.setProperties(alice, [{key: propKey, value: makeNewPropData()}]); + const consumedSpace = await collection.getPropertiesConsumedSpace(); + expect(consumedSpace).to.be.equal(originalSpace); + } + }); + + itSub('Adding then removing a collection property doesn\'t change the consumed space', async({helper}) => { + const propKey = 'tok-prop'; + + const collection = await helper[testSuite.mode].mintCollection(alice); + const originalSpace = await collection.getPropertiesConsumedSpace(); + + const propDataSize = 4096; + const propData = 'a'.repeat(propDataSize); + + const property = {key: propKey, value: propData}; + await collection.setProperties(alice, [property]); + let consumedSpace = await collection.getPropertiesConsumedSpace(); + expect(consumedSpace).to.be.equal(sizeOfProperty(property)); + + await collection.deleteProperties(alice, [propKey]); + consumedSpace = await collection.getPropertiesConsumedSpace(); + expect(consumedSpace).to.be.equal(originalSpace); + }); + + itSub('Modifying a collection property with different sizes correctly changes the consumed space', async({helper}) => { + const propKey = 'tok-prop'; + + const collection = await helper[testSuite.mode].mintCollection(alice); + const originalSpace = await collection.getPropertiesConsumedSpace(); + + const initProp = {key: propKey, value: 'a'.repeat(4096)}; + const biggerProp = {key: propKey, value: 'b'.repeat(5000)}; + const smallerProp = {key: propKey, value: 'c'.repeat(4000)}; + + let consumedSpace; + let expectedConsumedSpaceDiff; + + await collection.setProperties(alice, [initProp]); + consumedSpace = await collection.getPropertiesConsumedSpace(); + expectedConsumedSpaceDiff = sizeOfProperty(initProp) - originalSpace; + expect(consumedSpace).to.be.equal(originalSpace + expectedConsumedSpaceDiff); + + await collection.setProperties(alice, [biggerProp]); + consumedSpace = await collection.getPropertiesConsumedSpace(); + expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(initProp); + expect(consumedSpace).to.be.equal(sizeOfProperty(initProp) + expectedConsumedSpaceDiff); + + await collection.setProperties(alice, [smallerProp]); + consumedSpace = await collection.getPropertiesConsumedSpace(); + expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(smallerProp); + expect(consumedSpace).to.be.equal(sizeOfProperty(biggerProp) - expectedConsumedSpaceDiff); + }); + })); +}); + +describe('Negative Integration Test: Collection Properties', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([1000n, 100n], donor); + }); + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'ft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => { + before(async function() { + // eslint-disable-next-line require-await + await usingPlaygrounds(async helper => { + requirePalletsOrSkip(this, helper, testSuite.requiredPallets); + }); + }); + + itSub('Fails to set properties in a collection if not its onwer/administrator', async ({helper}) => { + const collection = await helper[testSuite.mode].mintCollection(alice); + + await expect(collection.setProperties(bob, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}])) + .to.be.rejectedWith(/common\.NoPermission/); + + expect(await collection.getProperties()).to.be.empty; + }); + + itSub('Fails to set properties that exceed the limits', async ({helper}) => { + const collection = await helper[testSuite.mode].mintCollection(alice); + + const spaceLimit = (helper.getApi().consts.unique.maxCollectionPropertiesSize as any).toNumber(); + + // Mute the general tx parsing error, too many bytes to process + { + console.error = () => {}; + await expect(collection.setProperties(alice, [ + {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}, + ])).to.be.rejected; + } + + expect(await collection.getProperties(['electron'])).to.be.empty; + + await expect(collection.setProperties(alice, [ + {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, + {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, + ])).to.be.rejectedWith(/common\.NoSpaceForProperty/); + + expect(await collection.getProperties(['electron', 'black_hole'])).to.be.empty; + }); + + itSub('Fails to set more properties than it is allowed', async ({helper}) => { + const collection = await helper[testSuite.mode].mintCollection(alice); + + const propertiesToBeSet = []; + for(let i = 0; i < 65; i++) { + propertiesToBeSet.push({ + key: 'electron_' + i, + value: Math.random() > 0.5 ? 'high' : 'low', + }); + } + + await expect(collection.setProperties(alice, propertiesToBeSet)). + to.be.rejectedWith(/common\.PropertyLimitReached/); + + expect(await collection.getProperties()).to.be.empty; + }); + + itSub('Fails to set properties with invalid names', async ({helper}) => { + const collection = await helper[testSuite.mode].mintCollection(alice); + + const invalidProperties = [ + [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}], + [{key: 'Mr/Sandman', value: 'Bring me a gene'}], + [{key: 'déjà vu', value: 'hmm...'}], + ]; + + for(let i = 0; i < invalidProperties.length; i++) { + await expect( + collection.setProperties(alice, invalidProperties[i]), + `on rejecting the new badly-named property #${i}`, + ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/); + } + + await expect( + collection.setProperties(alice, [{key: '', value: 'nothing must not exist'}]), + 'on rejecting an unnamed property', + ).to.be.rejectedWith(/common\.EmptyPropertyKey/); + + await expect( + collection.setProperties(alice, [{key: 'CRISPR-Cas9', value: 'rewriting nature!'}]), + 'on setting the correctly-but-still-badly-named property', + ).to.be.fulfilled; + + const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat(''); + + const properties = await collection.getProperties(keys); + expect(properties).to.be.deep.equal([ + {key: 'CRISPR-Cas9', value: 'rewriting nature!'}, + ]); + + for(let i = 0; i < invalidProperties.length; i++) { + await expect( + collection.deleteProperties(alice, invalidProperties[i].map(propertySet => propertySet.key)), + `on trying to delete the non-existent badly-named property #${i}`, + ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/); + } + }); + + itSub('Forbids to repair a collection if called with non-sudo', async({helper}) => { + const collection = await helper[testSuite.mode].mintCollection(alice, {properties: [ + {key: 'sea-creatures', value: 'mermaids'}, + {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'}, + ]}); + + await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true)) + .to.be.rejectedWith(/BadOrigin/); + }); + })); +}); --- /dev/null +++ b/js-packages/tests/nesting/graphs.test.ts @@ -0,0 +1,86 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, usingPlaygrounds} from '../util/index.js'; +import {UniqueHelper, UniqueNFTCollection, UniqueNFToken} from '@unique/playgrounds/unique.js'; + +/** + * ```dot + * 4 -> 3 -> 2 -> 1 + * 7 -> 6 -> 5 -> 2 + * 8 -> 5 + * ``` + */ +async function buildComplexObjectGraph(helper: UniqueHelper, sender: IKeyringPair): Promise<[UniqueNFTCollection,UniqueNFToken[]]> { + const collection = await helper.nft.mintCollection(sender, {permissions: {nesting: {tokenOwner: true}}}); + const tokens = await collection.mintMultipleTokens(sender, Array(8).fill({owner: {Substrate: sender.address}})); + + await tokens[7].nest(sender, tokens[4]); + await tokens[6].nest(sender, tokens[5]); + await tokens[5].nest(sender, tokens[4]); + await tokens[4].nest(sender, tokens[1]); + await tokens[3].nest(sender, tokens[2]); + await tokens[2].nest(sender, tokens[1]); + await tokens[1].nest(sender, tokens[0]); + + return [collection, tokens]; +} + +describe('Graphs', () => { + let alice: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([10n], donor); + }); + }); + + itSub('Ouroboros can\'t be created in a complex graph', async ({helper}) => { + const [collection, tokens] = await buildComplexObjectGraph(helper, alice); + + await collection.setPermissions(alice, {nesting: {collectionAdmin: false, tokenOwner: true}}); + + // [token owner] to self + await expect( + tokens[0].nest(alice, tokens[0]), + '[token owner] self-nesting is forbidden', + ).to.be.rejectedWith(/structure\.OuroborosDetected/); + // [token owner] to nested part of graph + await expect( + tokens[0].nest(alice, tokens[4]), + '[token owner] cannot nest the root node into an internal node', + ).to.be.rejectedWith(/structure\.OuroborosDetected/); + await expect( + tokens[1].transferFrom(alice, tokens[0].nestingAccount(), tokens[7].nestingAccount()), + '[token owner] cannot nest higher internal node into lower internal node', + ).to.be.rejectedWith(/structure\.OuroborosDetected/); + + await collection.setPermissions(alice, {nesting: {collectionAdmin: true, tokenOwner: false}}); + + // [collection owner] to self + await expect( + tokens[0].nest(alice, tokens[0]), + '[collection owner] self-nesting is forbidden', + ).to.be.rejectedWith(/structure\.OuroborosDetected/); + // [collection owner] to nested part of graph + await expect( + tokens[0].nest(alice, tokens[4]), + '[collection owner] cannot nest the root node into an internal node', + ).to.be.rejectedWith(/structure\.OuroborosDetected/); + }); +}); --- /dev/null +++ b/js-packages/tests/nesting/propertyPermissions.test.ts @@ -0,0 +1,198 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, Pallets, usingPlaygrounds, expect} from '../util/index.js'; +import {UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/unique.js'; + +describe('Integration Test: Access Rights to Token Properties', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); + }); + }); + + itSub('Reads access rights to properties of a collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice); + const propertyRights = (await helper.callRpc('api.query.common.collectionPropertyPermissions', [collection.collectionId])).toJSON(); + expect(propertyRights).to.be.empty; + }); + + async function testSetsAccessRightsToProperties(collection: UniqueNFTCollection | UniqueRFTCollection) { + await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true}}])) + .to.be.fulfilled; + + await collection.addAdmin(alice, {Substrate: bob.address}); + + await expect(collection.setTokenPropertyPermissions(bob, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}])) + .to.be.fulfilled; + + const propertyRights = await collection.getPropertyPermissions(['skullduggery', 'mindgame']); + expect(propertyRights).to.include.deep.members([ + {key: 'skullduggery', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}, + {key: 'mindgame', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}}, + ]); + } + + itSub('Sets access rights to properties of a collection (NFT)', async ({helper}) => { + await testSetsAccessRightsToProperties(await helper.nft.mintCollection(alice)); + }); + + itSub.ifWithPallets('Sets access rights to properties of a collection (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + await testSetsAccessRightsToProperties(await helper.rft.mintCollection(alice)); + }); + + async function testChangesAccessRightsToProperty(collection: UniqueNFTCollection | UniqueRFTCollection) { + await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}])) + .to.be.fulfilled; + + await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}])) + .to.be.fulfilled; + + const propertyRights = await collection.getPropertyPermissions(); + expect(propertyRights).to.be.deep.equal([ + {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}}, + ]); + } + + itSub('Changes access rights to properties of a NFT collection', async ({helper}) => { + await testChangesAccessRightsToProperty(await helper.nft.mintCollection(alice)); + }); + + itSub.ifWithPallets('Changes access rights to properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => { + await testChangesAccessRightsToProperty(await helper.rft.mintCollection(alice)); + }); +}); + +describe('Negative Integration Test: Access Rights to Token Properties', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); + }); + }); + + async function testPreventsFromSettingAccessRightsNotAdminOrOwner(collection: UniqueNFTCollection | UniqueRFTCollection) { + await expect(collection.setTokenPropertyPermissions(bob, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}])) + .to.be.rejectedWith(/common\.NoPermission/); + + const propertyRights = await collection.getPropertyPermissions(['skullduggery']); + expect(propertyRights).to.be.empty; + } + + itSub('Prevents from setting access rights to properties of a NFT collection if not an onwer/admin', async ({helper}) => { + await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.nft.mintCollection(alice)); + }); + + itSub.ifWithPallets('Prevents from setting access rights to properties of a ReFungible collection if not an onwer/admin', [Pallets.ReFungible], async ({helper}) => { + await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.rft.mintCollection(alice)); + }); + + async function testPreventFromAddingTooManyPossibleProperties(collection: UniqueNFTCollection | UniqueRFTCollection) { + const constitution = []; + for(let i = 0; i < 65; i++) { + constitution.push({ + key: 'property_' + i, + permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {}, + }); + } + + await expect(collection.setTokenPropertyPermissions(alice, constitution)) + .to.be.rejectedWith(/common\.PropertyLimitReached/); + + const propertyRights = await collection.getPropertyPermissions(); + expect(propertyRights).to.be.empty; + } + + itSub('Prevents from adding too many possible properties (NFT)', async ({helper}) => { + await testPreventFromAddingTooManyPossibleProperties(await helper.nft.mintCollection(alice)); + }); + + itSub.ifWithPallets('Prevents from adding too many possible properties (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + await testPreventFromAddingTooManyPossibleProperties(await helper.rft.mintCollection(alice)); + }); + + async function testPreventAccessRightsModifiedIfConstant(collection: UniqueNFTCollection | UniqueRFTCollection) { + await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}])) + .to.be.fulfilled; + + await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {collectionAdmin: true}}])) + .to.be.rejectedWith(/common\.NoPermission/); + + const propertyRights = await collection.getPropertyPermissions(['skullduggery']); + expect(propertyRights).to.deep.equal([ + {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}}, + ]); + } + + itSub('Prevents access rights to be modified if constant (NFT)', async ({helper}) => { + await testPreventAccessRightsModifiedIfConstant(await helper.nft.mintCollection(alice)); + }); + + itSub.ifWithPallets('Prevents access rights to be modified if constant (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + await testPreventAccessRightsModifiedIfConstant(await helper.rft.mintCollection(alice)); + }); + + async function testPreventsAddingPropertiesWithInvalidNames(collection: UniqueNFTCollection | UniqueRFTCollection) { + const invalidProperties = [ + [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}], + [{key: 'G#4', permission: {tokenOwner: true}}], + [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}], + ]; + + for(let i = 0; i < invalidProperties.length; i++) { + await expect( + collection.setTokenPropertyPermissions(alice, invalidProperties[i]), + `on setting the new badly-named property #${i}`, + ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/); + } + + await expect( + collection.setTokenPropertyPermissions(alice, [{key: '', permission: {}}]), + 'on rejecting an unnamed property', + ).to.be.rejectedWith(/common\.EmptyPropertyKey/); + + const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string + await expect( + collection.setTokenPropertyPermissions(alice, [ + {key: correctKey, permission: {collectionAdmin: true}}, + ]), + 'on setting the correctly-but-still-badly-named property', + ).to.be.fulfilled; + + const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat(''); + + const propertyRights = await collection.getPropertyPermissions(keys); + expect(propertyRights).to.be.deep.equal([ + {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}}, + ]); + } + + itSub('Prevents adding properties with invalid names (NFT)', async ({helper}) => { + await testPreventsAddingPropertiesWithInvalidNames(await helper.nft.mintCollection(alice)); + }); + + itSub.ifWithPallets('Prevents adding properties with invalid names (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + await testPreventsAddingPropertiesWithInvalidNames(await helper.rft.mintCollection(alice)); + }); +}); --- /dev/null +++ b/js-packages/tests/nesting/tokenProperties.seqtest.ts @@ -0,0 +1,71 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js'; + +describe('Integration Test: Token Properties with sudo', () => { + let superuser: IKeyringPair; + let alice: IKeyringPair; // collection owner + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + superuser = await privateKey('//Alice'); + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([100n], donor); + }); + }); + + [ + {mode: 'nft' as const, pieces: undefined, requiredPallets: []} as const, + {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]} as const, + ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => { + before(async function() { + // eslint-disable-next-line require-await + await usingPlaygrounds(async helper => { + requirePalletsOrSkip(this, helper, testSuite.requiredPallets); + }); + }); + + itSub('force_repair_item preserves valid consumed space', async({helper}) => { + const propKey = 'tok-prop'; + + const collection = await helper[testSuite.mode].mintCollection(alice, { + tokenPropertyPermissions: [ + { + key: propKey, + permission: {mutable: true, tokenOwner: true}, + }, + ], + }); + const token = await ( + testSuite.pieces + ? collection.mintToken(alice, testSuite.pieces as any) + : collection.mintToken(alice) + ); + + const prop = {key: propKey, value: 'a'.repeat(4096)}; + + await token.setProperties(alice, [prop]); + const originalSpace = await token.getTokenPropertiesConsumedSpace(); + expect(originalSpace).to.be.equal(sizeOfProperty(prop)); + + await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true); + const recomputedSpace = await token.getTokenPropertiesConsumedSpace(); + expect(recomputedSpace).to.be.equal(originalSpace); + }); + })); +}); --- /dev/null +++ b/js-packages/tests/nesting/tokenProperties.test.ts @@ -0,0 +1,816 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect, sizeOfProperty} from '../util/index.js'; +import {UniqueHelper, UniqueNFToken, UniqueRFToken} from '@unique/playgrounds/unique.js'; + +describe('Integration Test: Token Properties', () => { + let alice: IKeyringPair; // collection owner + let bob: IKeyringPair; // collection admin + let charlie: IKeyringPair; // token owner + + let permissions: {permission: any, signers: IKeyringPair[]}[]; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 100n, 100n], donor); + }); + + permissions = [ + {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]}, + {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]}, + {permission: {mutable: true, tokenOwner: true}, signers: [charlie]}, + {permission: {mutable: false, tokenOwner: true}, signers: [charlie]}, + {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]}, + {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]}, + ]; + }); + + async function mintCollectionWithAllPermissionsAndToken(helper: UniqueHelper, mode: 'NFT' | 'RFT'): Promise<[UniqueNFToken | UniqueRFToken, bigint]> { + const collection = await (mode == 'NFT' ? helper.nft : helper.rft).mintCollection(alice, { + tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) => + signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))), + }); + return mode == 'NFT' ? [await collection.mintToken(alice), 1n] : [await collection.mintToken(alice, 100n as any), 100n]; + } + + async function testReadsYetEmptyProperties(token: UniqueNFToken | UniqueRFToken) { + const properties = await token.getProperties(); + expect(properties).to.be.empty; + + const tokenData = await token.getData(); + expect(tokenData!.properties).to.be.empty; + } + + itSub('Reads yet empty properties of a token (NFT)', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice); + const token = await collection.mintToken(alice); + await testReadsYetEmptyProperties(token); + }); + + itSub.ifWithPallets('Reads yet empty properties of a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice); + const token = await collection.mintToken(alice); + await testReadsYetEmptyProperties(token); + }); + + async function testAssignPropertiesAccordingToPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { + await token.collection.addAdmin(alice, {Substrate: bob.address}); + await token.transfer(alice, {Substrate: charlie.address}, pieces); + + const propertyKeys: string[] = []; + let i = 0; + for(const permission of permissions) { + i++; + let j = 0; + for(const signer of permission.signers) { + j++; + const key = i + '_' + signer.address; + propertyKeys.push(key); + + await expect( + token.setProperties(signer, [{key: key, value: 'Serotonin increase'}]), + `on adding property #${i} by signer #${j}`, + ).to.be.fulfilled; + } + } + + const properties = await token.getProperties(propertyKeys); + const tokenData = await token.getData(); + for(let i = 0; i < properties.length; i++) { + expect(properties[i].value).to.be.equal('Serotonin increase'); + expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase'); + } + } + + itSub('Assigns properties to a token according to permissions (NFT)', async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); + await testAssignPropertiesAccordingToPermissions(token, amount); + }); + + itSub.ifWithPallets('Assigns properties to a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); + await testAssignPropertiesAccordingToPermissions(token, amount); + }); + + async function testChangesPropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { + await token.collection.addAdmin(alice, {Substrate: bob.address}); + await token.transfer(alice, {Substrate: charlie.address}, pieces); + + const propertyKeys: string[] = []; + let i = 0; + for(const permission of permissions) { + i++; + if(!permission.permission.mutable) continue; + + let j = 0; + for(const signer of permission.signers) { + j++; + const key = i + '_' + signer.address; + propertyKeys.push(key); + + await expect( + token.setProperties(signer, [{key, value: 'Serotonin increase'}]), + `on adding property #${i} by signer #${j}`, + ).to.be.fulfilled; + + await expect( + token.setProperties(signer, [{key, value: 'Serotonin stable'}]), + `on changing property #${i} by signer #${j}`, + ).to.be.fulfilled; + } + } + + const properties = await token.getProperties(propertyKeys); + const tokenData = await token.getData(); + for(let i = 0; i < properties.length; i++) { + expect(properties[i].value).to.be.equal('Serotonin stable'); + expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable'); + } + } + + itSub('Changes properties of a token according to permissions (NFT)', async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); + await testChangesPropertiesAccordingPermission(token, amount); + }); + + itSub.ifWithPallets('Changes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); + await testChangesPropertiesAccordingPermission(token, amount); + }); + + async function testDeletePropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { + await token.collection.addAdmin(alice, {Substrate: bob.address}); + await token.transfer(alice, {Substrate: charlie.address}, pieces); + + const propertyKeys: string[] = []; + let i = 0; + + for(const permission of permissions) { + i++; + if(!permission.permission.mutable) continue; + + let j = 0; + for(const signer of permission.signers) { + j++; + const key = i + '_' + signer.address; + propertyKeys.push(key); + + await expect( + token.setProperties(signer, [{key, value: 'Serotonin increase'}]), + `on adding property #${i} by signer #${j}`, + ).to.be.fulfilled; + + await expect( + token.deleteProperties(signer, [key]), + `on deleting property #${i} by signer #${j}`, + ).to.be.fulfilled; + } + } + + expect(await token.getProperties(propertyKeys)).to.be.empty; + expect((await token.getData())!.properties).to.be.empty; + } + + itSub('Deletes properties of a token according to permissions (NFT)', async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); + await testDeletePropertiesAccordingPermission(token, amount); + }); + + itSub.ifWithPallets('Deletes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); + await testDeletePropertiesAccordingPermission(token, amount); + }); + + itSub('Assigns properties to a nested token according to permissions', async ({helper}) => { + const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const collectionB = await helper.nft.mintCollection(alice, { + tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) => + signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))), + }); + const targetToken = await collectionA.mintToken(alice); + const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount()); + + await collectionB.addAdmin(alice, {Substrate: bob.address}); + await targetToken.transfer(alice, {Substrate: charlie.address}); + + const propertyKeys: string[] = []; + let i = 0; + for(const permission of permissions) { + i++; + let j = 0; + for(const signer of permission.signers) { + j++; + const key = i + '_' + signer.address; + propertyKeys.push(key); + + await expect( + nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), + `on adding property #${i} by signer #${j}`, + ).to.be.fulfilled; + } + } + + const properties = await nestedToken.getProperties(propertyKeys); + const tokenData = await nestedToken.getData(); + for(let i = 0; i < properties.length; i++) { + expect(properties[i].value).to.be.equal('Serotonin increase'); + expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase'); + } + expect(await targetToken.getProperties()).to.be.empty; + }); + + itSub('Changes properties of a nested token according to permissions', async ({helper}) => { + const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const collectionB = await helper.nft.mintCollection(alice, { + tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) => + signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))), + }); + const targetToken = await collectionA.mintToken(alice); + const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount()); + + await collectionB.addAdmin(alice, {Substrate: bob.address}); + await targetToken.transfer(alice, {Substrate: charlie.address}); + + const propertyKeys: string[] = []; + let i = 0; + for(const permission of permissions) { + i++; + if(!permission.permission.mutable) continue; + + let j = 0; + for(const signer of permission.signers) { + j++; + const key = i + '_' + signer.address; + propertyKeys.push(key); + + await expect( + nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), + `on adding property #${i} by signer #${j}`, + ).to.be.fulfilled; + + await expect( + nestedToken.setProperties(signer, [{key, value: 'Serotonin stable'}]), + `on changing property #${i} by signer #${j}`, + ).to.be.fulfilled; + } + } + + const properties = await nestedToken.getProperties(propertyKeys); + const tokenData = await nestedToken.getData(); + for(let i = 0; i < properties.length; i++) { + expect(properties[i].value).to.be.equal('Serotonin stable'); + expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable'); + } + expect(await targetToken.getProperties()).to.be.empty; + }); + + itSub('Deletes properties of a nested token according to permissions', async ({helper}) => { + const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const collectionB = await helper.nft.mintCollection(alice, { + tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) => + signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))), + }); + const targetToken = await collectionA.mintToken(alice); + const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount()); + + await collectionB.addAdmin(alice, {Substrate: bob.address}); + await targetToken.transfer(alice, {Substrate: charlie.address}); + + const propertyKeys: string[] = []; + let i = 0; + for(const permission of permissions) { + i++; + if(!permission.permission.mutable) continue; + + let j = 0; + for(const signer of permission.signers) { + j++; + const key = i + '_' + signer.address; + propertyKeys.push(key); + + await expect( + nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), + `on adding property #${i} by signer #${j}`, + ).to.be.fulfilled; + + await expect( + nestedToken.deleteProperties(signer, [key]), + `on deleting property #${i} by signer #${j}`, + ).to.be.fulfilled; + } + } + + expect(await nestedToken.getProperties(propertyKeys)).to.be.empty; + expect((await nestedToken.getData())!.properties).to.be.empty; + expect(await targetToken.getProperties()).to.be.empty; + }); + + [ + {mode: 'nft' as const, storage: 'nonfungible' as const, pieces: undefined, requiredPallets: []} as const, + {mode: 'rft' as const, storage: 'refungible' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]} as const, + ].map(testCase => + itSub.ifWithPallets(`Allows modifying a token property multiple times with the same size (${testCase.mode})`, testCase.requiredPallets, async({helper}) => { + const propKey = 'tok-prop'; + + const collection = await helper[testCase.mode].mintCollection(alice, { + tokenPropertyPermissions: [ + { + key: propKey, + permission: {mutable: true, tokenOwner: true}, + }, + ], + }); + + const maxTokenPropertiesSize = 32768; + + const propDataSize = 4096; + + let propDataChar = 'a'; + const makeNewPropData = () => { + propDataChar = String.fromCharCode(propDataChar.charCodeAt(0) + 1); + return `${propDataChar}`.repeat(propDataSize); + }; + + const token = await ( + testCase.pieces + ? collection.mintToken(alice, testCase.pieces as any) + : collection.mintToken(alice) + ); + + const property = {key: propKey, value: makeNewPropData()}; + await token.setProperties(alice, [property]); + const originalSpace = await token.getTokenPropertiesConsumedSpace(); + expect(originalSpace).to.be.equal(sizeOfProperty(property)); + + const sameSizePropertiesPossibleNum = maxTokenPropertiesSize / propDataSize; + + // It is possible to modify a property as many times as needed. + // It will not consume any additional space. + for(let i = 0; i < sameSizePropertiesPossibleNum + 1; i++) { + await token.setProperties(alice, [{key: propKey, value: makeNewPropData()}]); + const consumedSpace = await token.getTokenPropertiesConsumedSpace(); + expect(consumedSpace).to.be.equal(originalSpace); + } + })); + + [ + {mode: 'nft' as const, pieces: undefined, requiredPallets: []}, + {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itSub.ifWithPallets(`Adding then removing a token property doesn't change the consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => { + const propKey = 'tok-prop'; + + const collection = await helper[testCase.mode].mintCollection(alice, { + tokenPropertyPermissions: [ + { + key: propKey, + permission: {mutable: true, tokenOwner: true}, + }, + ], + }); + const token = await ( + testCase.pieces + ? collection.mintToken(alice, testCase.pieces as any) + : collection.mintToken(alice) + ); + const originalSpace = await token.getTokenPropertiesConsumedSpace(); + + const propDataSize = 4096; + const propData = 'a'.repeat(propDataSize); + + const property = {key: propKey, value: propData}; + await token.setProperties(alice, [property]); + let consumedSpace = await token.getTokenPropertiesConsumedSpace(); + expect(consumedSpace).to.be.equal(sizeOfProperty(property)); + + await token.deleteProperties(alice, [propKey]); + consumedSpace = await token.getTokenPropertiesConsumedSpace(); + expect(consumedSpace).to.be.equal(originalSpace); + })); + + [ + {mode: 'nft' as const, pieces: undefined, requiredPallets: []}, + {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itSub.ifWithPallets(`Modifying a token property with different sizes correctly changes the consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => { + const propKey = 'tok-prop'; + + const collection = await helper[testCase.mode].mintCollection(alice, { + tokenPropertyPermissions: [ + { + key: propKey, + permission: {mutable: true, tokenOwner: true}, + }, + ], + }); + const token = await ( + testCase.pieces + ? collection.mintToken(alice, testCase.pieces as any) + : collection.mintToken(alice) + ); + const originalSpace = await token.getTokenPropertiesConsumedSpace(); + + const initProp = {key: propKey, value: 'a'.repeat(4096)}; + const biggerProp = {key: propKey, value: 'b'.repeat(5000)}; + const smallerProp = {key: propKey, value: 'c'.repeat(4000)}; + + let consumedSpace; + let expectedConsumedSpaceDiff; + + await token.setProperties(alice, [initProp]); + consumedSpace = await token.getTokenPropertiesConsumedSpace(); + expectedConsumedSpaceDiff = sizeOfProperty(initProp) - originalSpace; + expect(consumedSpace).to.be.equal(originalSpace + expectedConsumedSpaceDiff); + + await token.setProperties(alice, [biggerProp]); + consumedSpace = await token.getTokenPropertiesConsumedSpace(); + expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(initProp); + expect(consumedSpace).to.be.equal(sizeOfProperty(initProp) + expectedConsumedSpaceDiff); + + await token.setProperties(alice, [smallerProp]); + consumedSpace = await token.getTokenPropertiesConsumedSpace(); + expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(smallerProp); + expect(consumedSpace).to.be.equal(sizeOfProperty(biggerProp) - expectedConsumedSpaceDiff); + })); + + itSub('Set sponsored properties', async({helper}) => { + const collection = await helper.nft.mintCollection(alice, {tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: true}}]}); + + await collection.setSponsor(alice, alice.address); + await collection.confirmSponsorship(alice); + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await collection.addToAllowList(alice, {Substrate: bob.address}); + await collection.setLimits(alice, {sponsoredDataRateLimit: {blocks: 30}}); + + const token = await collection.mintToken(alice, {Substrate: bob.address}); + + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); + const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); + + await token.setProperties(bob, [{key: 'k', value: 'val'}]); + + const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); + const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); + + expect(bobBalanceAfter).to.be.equal(bobBalanceBefore); + expect(aliceBalanceBefore > aliceBalanceAfter).to.be.true; + }); +}); + +describe('Negative Integration Test: Token Properties', () => { + let alice: IKeyringPair; // collection owner + let bob: IKeyringPair; // collection admin + let charlie: IKeyringPair; // token owner + + let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[]; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + let dave: IKeyringPair; + [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor); + + // todo:playgrounds probably separate these tests later + constitution = [ + {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie}, + {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie}, + {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice}, + {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice}, + {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave}, + {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave}, + ]; + }); + }); + + [ + {mode: 'nft' as const, requiredPallets: [Pallets.NFT]}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itSub.ifWithPallets(`Forbids adding/deleting properties of a token if token doesn't exist (${testCase.mode.toLocaleUpperCase})`, testCase.requiredPallets, async({helper}) => { + const collection = await helper[testCase.mode].mintCollection(alice, { + tokenPropertyPermissions: constitution.slice(0, 1).map(({permission}) => ({key: '1', permission})), + }); + const nonExistentToken = collection.getTokenObject(1); + + await expect( + nonExistentToken.setProperties(alice, [{key: '1', value: 'Serotonin increase'}]), + 'on expecting failure whilst adding a property by alice', + ).to.be.rejectedWith(/common\.TokenNotFound/); + + await expect( + nonExistentToken.deleteProperties(alice, ['1']), + 'on expecting failure whilst deleting a property by alice', + ).to.be.rejectedWith(/common\.TokenNotFound/); + })); + + async function mintCollectionWithAllPermissionsAndToken(helper: UniqueHelper, mode: 'NFT' | 'RFT'): Promise<[UniqueNFToken | UniqueRFToken, bigint]> { + const collection = await (mode == 'NFT' ? helper.nft : helper.rft).mintCollection(alice, { + tokenPropertyPermissions: constitution.map(({permission}, i) => ({key: `${i+1}`, permission})), + }); + return mode == 'NFT' ? [await collection.mintToken(alice), 1n] : [await collection.mintToken(alice, 100n as any), 100n]; + } + + async function getConsumedSpace(api: any, collectionId: number, tokenId: number, mode: 'NFT' | 'RFT'): Promise { + return (await (mode == 'NFT' ? api.query.nonfungible : api.query.refungible).tokenProperties(collectionId, tokenId)).toJSON().consumedSpace; + } + + async function prepare(token: UniqueNFToken | UniqueRFToken, pieces: bigint): Promise { + await token.collection.addAdmin(alice, {Substrate: bob.address}); + await token.transfer(alice, {Substrate: charlie.address}, pieces); + + let i = 0; + for(const passage of constitution) { + i++; + const signer = passage.signers[0]; + await expect( + token.setProperties(signer, [{key: `${i}`, value: 'Serotonin increase'}]), + `on adding property ${i} by ${signer.address}`, + ).to.be.fulfilled; + } + + const originalSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); + return originalSpace; + } + + async function testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { + const originalSpace = await prepare(token, pieces); + + let i = 0; + for(const forbiddance of constitution) { + i++; + if(!forbiddance.permission.mutable) continue; + + await expect( + token.setProperties(forbiddance.sinner, [{key: `${i}`, value: 'Serotonin down'}]), + `on failing to change property ${i} by the malefactor`, + ).to.be.rejectedWith(/common\.NoPermission/); + + await expect( + token.deleteProperties(forbiddance.sinner, [`${i}`]), + `on failing to delete property ${i} by the malefactor`, + ).to.be.rejectedWith(/common\.NoPermission/); + } + + const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); + expect(consumedSpace).to.be.equal(originalSpace); + } + + itSub('Forbids changing/deleting properties of a token if the user is outside of permissions (NFT)', async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); + await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, amount); + }); + + itSub.ifWithPallets('Forbids changing/deleting properties of a token if the user is outside of permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); + await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, amount); + }); + + async function testForbidsChangingDeletingPropertiesIfPropertyImmutable(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { + const originalSpace = await prepare(token, pieces); + + let i = 0; + for(const permission of constitution) { + i++; + if(permission.permission.mutable) continue; + + await expect( + token.setProperties(permission.signers[0], [{key: `${i}`, value: 'Serotonin down'}]), + `on failing to change property ${i} by signer #0`, + ).to.be.rejectedWith(/common\.NoPermission/); + + await expect( + token.deleteProperties(permission.signers[0], [i.toString()]), + `on failing to delete property ${i} by signer #0`, + ).to.be.rejectedWith(/common\.NoPermission/); + } + + const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); + expect(consumedSpace).to.be.equal(originalSpace); + } + + itSub('Forbids changing/deleting properties of a token if the property is permanent (immutable) (NFT)', async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); + await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, amount); + }); + + itSub.ifWithPallets('Forbids changing/deleting properties of a token if the property is permanent (immutable) (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); + await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, amount); + }); + + async function testForbidsAddingPropertiesIfPropertyNotDeclared(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { + const originalSpace = await prepare(token, pieces); + + await expect( + token.setProperties(alice, [{key: 'non-existent', value: 'I exist!'}]), + 'on failing to add a previously non-existent property', + ).to.be.rejectedWith(/common\.NoPermission/); + + await expect( + token.collection.setTokenPropertyPermissions(alice, [{key: 'now-existent', permission: {}}]), + 'on setting a new non-permitted property', + ).to.be.fulfilled; + + await expect( + token.setProperties(alice, [{key: 'now-existent', value: 'I exist!'}]), + 'on failing to add a property forbidden by the \'None\' permission', + ).to.be.rejectedWith(/common\.NoPermission/); + + expect(await token.getProperties(['non-existent', 'now-existent'])).to.be.empty; + + const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); + expect(consumedSpace).to.be.equal(originalSpace); + } + + itSub('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (NFT)', async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); + await testForbidsAddingPropertiesIfPropertyNotDeclared(token, amount); + }); + + itSub.ifWithPallets('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); + await testForbidsAddingPropertiesIfPropertyNotDeclared(token, amount); + }); + + async function testForbidsAddingTooLargeProperties(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { + const originalSpace = await prepare(token, pieces); + + await expect( + token.collection.setTokenPropertyPermissions(alice, [ + {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, + {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}}, + ]), + 'on setting new permissions for properties', + ).to.be.fulfilled; + + // Mute the general tx parsing error + { + console.error = () => {}; + await expect(token.setProperties(alice, [{key: 'a_holy_book', value: 'word '.repeat(6554)}])) + .to.be.rejected; + } + + await expect(token.setProperties(alice, [ + {key: 'a_holy_book', value: 'word '.repeat(3277)}, + {key: 'young_years', value: 'neverending'.repeat(1490)}, + ])).to.be.rejectedWith(/common\.NoSpaceForProperty/); + + expect(await token.getProperties(['a_holy_book', 'young_years'])).to.be.empty; + const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); + expect(consumedSpace).to.be.equal(originalSpace); + } + + itSub('Forbids adding too large properties to a token (NFT)', async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); + await testForbidsAddingTooLargeProperties(token, amount); + }); + + itSub.ifWithPallets('Forbids adding too large properties to a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => { + const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); + await testForbidsAddingTooLargeProperties(token, amount); + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itSub.ifWithPallets(`Forbids adding too many propeties to a token (${testCase.mode})`, testCase.requiredPallets, async({helper}) => { + const collection = await helper[testCase.mode].mintCollection(alice); + const maxPropertiesPerItem = 64; + + for(let i = 0; i < maxPropertiesPerItem; i++) { + await collection.setTokenPropertyPermissions(alice, [{ + key: `${i+1}`, + permission: {mutable: true, tokenOwner: true, collectionAdmin: true}, + }]); + } + + await expect(collection.setTokenPropertyPermissions(alice, [{ + key: `${maxPropertiesPerItem}-th`, + permission: {mutable: true, tokenOwner: true, collectionAdmin: true}, + }])).to.be.rejectedWith(/common\.PropertyLimitReached/); + })); + + [ + {mode: 'nft' as const, pieces: undefined, requiredPallets: []}, + {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itSub.ifWithPallets(`Forbids force_repair_item from non-sudo (${testCase.mode})`, testCase.requiredPallets, async({helper}) => { + const propKey = 'tok-prop'; + + const collection = await helper[testCase.mode].mintCollection(alice, { + tokenPropertyPermissions: [ + { + key: propKey, + permission: {mutable: true, tokenOwner: true}, + }, + ], + }); + const token = await ( + testCase.pieces + ? collection.mintToken(alice, testCase.pieces as any) + : collection.mintToken(alice) + ); + + const propDataSize = 4096; + const propData = 'a'.repeat(propDataSize); + await token.setProperties(alice, [{key: propKey, value: propData}]); + + await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true)) + .to.be.rejectedWith(/BadOrigin/); + })); +}); + +describe('ReFungible token properties permissions tests', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + async function prepare(helper: UniqueHelper): Promise { + const collection = await helper.rft.mintCollection(alice); + const token = await collection.mintToken(alice, 100n); + + await collection.addAdmin(alice, {Substrate: bob.address}); + await collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable: true, tokenOwner: true}}]); + + return token; + } + + itSub('Forbids adding token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) => { + const token = await prepare(helper); + + await token.transfer(alice, {Substrate: charlie.address}, 33n); + + await expect(token.setProperties(alice, [ + {key: 'fractals', value: 'multiverse'}, + ])).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Forbids mutating token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) => { + const token = await prepare(helper); + + await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, tokenOwner: true}}])) + .to.be.fulfilled; + + await expect(token.setProperties(alice, [ + {key: 'fractals', value: 'multiverse'}, + ])).to.be.fulfilled; + + await token.transfer(alice, {Substrate: charlie.address}, 33n); + + await expect(token.setProperties(alice, [ + {key: 'fractals', value: 'want to rule the world'}, + ])).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Forbids deleting token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) => { + const token = await prepare(helper); + + await expect(token.setProperties(alice, [ + {key: 'fractals', value: 'one headline - why believe it'}, + ])).to.be.fulfilled; + + await token.transfer(alice, {Substrate: charlie.address}, 33n); + + await expect(token.deleteProperties(alice, ['fractals'])). + to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Allows token property mutation with collectionOwner==true when admin doesn\'t have all pieces', async ({helper}) => { + const token = await prepare(helper); + + await token.transfer(alice, {Substrate: charlie.address}, 33n); + + await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, collectionAdmin: true}}])) + .to.be.fulfilled; + + await expect(token.setProperties(alice, [ + {key: 'fractals', value: 'multiverse'}, + ])).to.be.fulfilled; + }); +}); --- /dev/null +++ b/js-packages/tests/nesting/unnest.test.ts @@ -0,0 +1,325 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, Pallets, usingPlaygrounds} from '../util/index.js'; +import {CrossAccountId, UniqueFTCollection, UniqueNFToken, UniqueRFToken} from '@unique/playgrounds/unique.js'; + +describe('Integration Test: Unnesting', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 50n, 50n], donor); + }); + }); + + itSub('NFT: allows the owner to successfully unnest a token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const targetToken = await collection.mintToken(alice); + + // Create a nested token + const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount()); + + // Unnest + await expect(nestedToken.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}), 'while unnesting').to.be.fulfilled; + expect(await nestedToken.getOwner()).to.be.deep.equal({Substrate: alice.address}); + + // Nest and burn + await nestedToken.nest(alice, targetToken); + await expect(nestedToken.burnFrom(alice, targetToken.nestingAccount()), 'while burning').to.be.fulfilled; + await expect(nestedToken.getOwner()).to.be.rejected; + }); + + itSub('NativeFungible: allows the owner to successfully unnest a token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const targetToken = await collection.mintToken(alice); + + const collectionFT = helper.ft.getCollectionObject(0); + + // Nest + await collectionFT.transfer(alice, targetToken.nestingAccount(), 10n); + // Unnest + await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled; + + expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(1n); + }); + + itSub('Fungible: allows the owner to successfully unnest a token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const targetToken = await collection.mintToken(alice); + + const collectionFT = await helper.ft.mintCollection(alice); + + // Nest and unnest + await collectionFT.mint(alice, 10n, targetToken.nestingAccount()); + await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled; + expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(9n); + expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(1n); + + // Nest and burn + await collectionFT.transfer(alice, targetToken.nestingAccount(), 5n); + await expect(collectionFT.burnTokensFrom(alice, targetToken.nestingAccount(), 6n), 'while burning').to.be.fulfilled; + expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(4n); + expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(0n); + expect(await targetToken.getChildren()).to.be.length(0); + }); + + itSub.ifWithPallets('ReFungible: allows the owner to successfully unnest a token', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const targetToken = await collection.mintToken(alice); + + const collectionRFT = await helper.rft.mintCollection(alice); + + // Nest and unnest + const token = await collectionRFT.mintToken(alice, 10n, targetToken.nestingAccount()); + await expect(token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(9n); + expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(1n); + + // Nest and burn + await token.transfer(alice, targetToken.nestingAccount(), 5n); + await expect(token.burnFrom(alice, targetToken.nestingAccount(), 6n), 'while burning').to.be.fulfilled; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(4n); + expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n); + expect(await targetToken.getChildren()).to.be.length(0); + }); + + async function checkNestedAmountState({ + expectedBalance, + childrenShouldPresent, + nested, + targetNft, + }: { + expectedBalance: bigint, + childrenShouldPresent: boolean, + nested: UniqueFTCollection | UniqueRFToken, + targetNft: UniqueNFToken, + }) { + const balance = await nested.getBalance(targetNft.nestingAccount()); + expect(balance).to.be.equal(expectedBalance); + + const children = await targetNft.getChildren(); + + if(childrenShouldPresent) { + expect(children[0]).to.be.deep.equal({ + collectionId: nested.collectionId, + tokenId: (nested instanceof UniqueFTCollection) ? 0 : nested.tokenId, + }); + } else { + expect(children.length).to.be.equal(0); + } + } + + function ownerOrAdminUnnestCases(modes: ('ft' | 'nft' | 'rft')[]): { + mode: 'ft' | 'nft' | 'rft', + sender: string, + op: 'transfer' | 'burn', + requiredPallets: Pallets[], + }[] { + const senders = ['owner', 'admin']; + const ops = ['transfer', 'burn']; + + const cases = []; + for(const mode of modes) { + const requiredPallets = (mode === 'rft') + ? [Pallets.ReFungible] + : []; + + for(const sender of senders) { + for(const op of ops) { + cases.push({ + mode: mode as 'ft' | 'nft' | 'rft', + sender, + op: op as 'transfer' | 'burn', + requiredPallets, + }); + } + } + } + + return cases; + } + + ownerOrAdminUnnestCases(['ft', 'rft']).map(testCase => + itSub.ifWithPallets(`[${testCase.mode}]: allows a collection ${testCase.sender} to ${testCase.op} nested token`, testCase.requiredPallets, async({helper}) => { + const owner = alice; + const admin = bob; + + const unnester = (testCase.sender === 'owner') + ? owner + : admin; + + const collectionNFT = await helper.nft.mintCollection(owner); + await collectionNFT.setPermissions(owner, {nesting: {tokenOwner: true}}); + + const collectionNested = await helper[testCase.mode as 'ft' | 'rft'].mintCollection(owner, { + limits: { + ownerCanTransfer: true, + }, + }); + await collectionNested.addAdmin(owner, {Substrate: admin.address}); + + const targetNft = await collectionNFT.mintToken(owner, {Substrate: charlie.address}); + + let nested: UniqueFTCollection | UniqueRFToken; + const totalAmount = 5n; + const firstUnnestAmount = 2n; + const restUnnestAmount = totalAmount - firstUnnestAmount; + + if(collectionNested instanceof UniqueFTCollection) { + await collectionNested.mint(owner, totalAmount, {Substrate: charlie.address}); + nested = collectionNested; + } else { + nested = await collectionNested.mintToken(owner, totalAmount, {Substrate: charlie.address}); + } + + // transfer/burn `amount` of nested assets by `unnester`. + const doOperationAndCheck = async ({ + amount, + shouldBeNestedAfterOp, + }: { + amount: bigint, + shouldBeNestedAfterOp: boolean, + }) => { + const nestedBalanceBeforeOp = await nested.getBalance(targetNft.nestingAccount()); + + if(testCase.op === 'transfer') { + const bobBalanceBeforeOp = await nested.getBalance({Substrate: bob.address}); + + await nested.transferFrom(unnester, targetNft.nestingAccount(), {Substrate: bob.address}, amount); + expect(await nested.getBalance({Substrate: bob.address})).to.be.equal(bobBalanceBeforeOp + amount); + } else { + if(nested instanceof UniqueFTCollection) { + await nested.burnTokensFrom(unnester, targetNft.nestingAccount(), amount); + } else { + await nested.burnFrom(unnester, targetNft.nestingAccount(), amount); + } + } + + await checkNestedAmountState({ + expectedBalance: nestedBalanceBeforeOp - amount, + childrenShouldPresent: shouldBeNestedAfterOp, + nested, + targetNft, + }); + }; + + // Initial setup: nest (fungibles/rft parts). + // Check NFT's balance of nested assets and NFT's children. + await nested.transfer(charlie, targetNft.nestingAccount(), totalAmount); + await checkNestedAmountState({ + expectedBalance: totalAmount, + childrenShouldPresent: true, + nested, + targetNft, + }); + + // Transfer/burn only a part of nested assets. + // Check that NFT's balance of the nested assets correctly decreased and NFT's children are not changed. + await doOperationAndCheck({ + amount: firstUnnestAmount, + shouldBeNestedAfterOp: true, + }); + + // Transfer/burn all remaining nested assets. + // Check that NFT's balance of the nested assets is 0 and NFT has no more children. + await doOperationAndCheck({ + amount: restUnnestAmount, + shouldBeNestedAfterOp: false, + }); + })); + + ownerOrAdminUnnestCases(['nft']).map(testCase => + itSub(`[nft]: allows a collection ${testCase.sender} to ${testCase.op} nested token`, async ({helper}) => { + const owner = alice; + const admin = bob; + + const unnester = (testCase.sender === 'owner') + ? owner + : admin; + + const collectionNFT = await helper.nft.mintCollection(owner); + await collectionNFT.setPermissions(owner, {nesting: {tokenOwner: true}}); + + const collectionNested = await helper.nft.mintCollection(owner, { + limits: { + ownerCanTransfer: true, + }, + }); + await collectionNested.addAdmin(owner, {Substrate: admin.address}); + + const targetNft = await collectionNFT.mintToken(owner, {Substrate: charlie.address}); + const nested = await collectionNested.mintToken(owner, {Substrate: charlie.address}); + + await nested.transfer(charlie, targetNft.nestingAccount()); + expect(await targetNft.getChildren()).to.be.deep.equal([{ + collectionId: nested.collectionId, + tokenId: nested.tokenId, + }]); + + if(testCase.op === 'transfer') { + await nested.transferFrom(unnester, targetNft.nestingAccount(), {Substrate: bob.address}); + } else { + await nested.burnFrom(unnester, targetNft.nestingAccount()); + } + + expect((await targetNft.getChildren()).length).to.be.equal(0); + })); +}); + +describe('Negative Test: Unnesting', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); + }); + }); + + itSub('Disallows a non-owner to unnest/burn a token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const targetToken = await collection.mintToken(alice); + + // Create a nested token + const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount()); + + // Try to unnest + await expect(nestedToken.unnest(bob, targetToken, {Substrate: alice.address})).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); + + // Try to burn + await expect(nestedToken.burnFrom(bob, targetToken.nestingAccount())).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); + }); + + // todo another test for creating excessive depth matryoshka with Ethereum? + + // Recursive nesting + itSub('Prevents Ouroboros creation', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const targetToken = await collection.mintToken(alice); + + // Fail to create a nested token ouroboros + const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount()); + await expect(targetToken.nest(alice, nestedToken)).to.be.rejectedWith(/^structure\.OuroborosDetected$/); + }); +}); --- /dev/null +++ b/js-packages/tests/nextSponsoring.test.ts @@ -0,0 +1,97 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, Pallets, usingPlaygrounds} from './util/index.js'; + +const SPONSORING_TIMEOUT = 5; + +describe('Integration Test getNextSponsored(collection_id, owner, item_id):', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); + }); + }); + + itSub('NFT', async ({helper}) => { + // Non-existing collection + expect(await helper.collection.getTokenNextSponsored(0, 0, {Substrate: alice.address})).to.be.null; + + const collection = await helper.nft.mintCollection(alice, {}); + const token = await collection.mintToken(alice); + + // Check with Disabled sponsoring state + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null; + + // Check with Unconfirmed sponsoring state + await collection.setSponsor(alice, bob.address); + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null; + + // Check with Confirmed sponsoring state + await collection.confirmSponsorship(bob); + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.equal(0); + + // Check after transfer + await token.transfer(alice, {Substrate: bob.address}); + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT); + + // Non-existing token + expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null; + }); + + itSub('Fungible', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}); + await collection.mint(alice, 10n); + + // Check with Disabled sponsoring state + expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null; + + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + + // Check with Confirmed sponsoring state + expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.equal(0); + + // Check after transfer + await collection.transfer(alice, {Substrate: bob.address}); + expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT); + }); + + itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + const token = await collection.mintToken(alice, 10n); + + // Check with Disabled sponsoring state + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null; + + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + + // Check with Confirmed sponsoring state + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.equal(0); + + // Check after transfer + await token.transfer(alice, {Substrate: bob.address}); + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT); + + // Non-existing token + expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null; + }); +}); --- a/js-packages/tests/package.json +++ b/js-packages/tests/package.json @@ -20,13 +20,12 @@ "typescript": "^5.1.6" }, "scripts": { - "setup": "ts-node --esm ./src/util/globalSetup.ts", - "setIdentities": "ts-node --esm ./src/util/identitySetter.ts", - "checkRelayIdentities": "ts-node --esm ./src/util/relayIdentitiesChecker.ts", - "frankenstein": "ts-node --esm ./src/util/frankenstein.ts", + "setup": "ts-node --esm ./util/globalSetup.ts", + "setIdentities": "ts-node --esm ./util/identitySetter.ts", + "checkRelayIdentities": "ts-node --esm ./util/relayIdentitiesChecker.ts", "_test": "yarn setup && mocha --timeout 9999999 --loader=ts-node/esm.mjs", "_testParallel": "yarn setup && mocha --timeout 9999999 --parallel --loader=ts-node/esm.mjs", - "test": "yarn _test './src/**/*.*test.ts'", + "test": "yarn _test './**/*.*test.ts'", "load": "yarn _test './**/*.load.ts'" } } --- /dev/null +++ b/js-packages/tests/pallet-presence.test.ts @@ -0,0 +1,130 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {itSub, usingPlaygrounds, expect} from './util/index.js'; + +// Pallets that must always be present +const requiredPallets = [ + 'balances', + 'balancesadapter', + 'common', + 'timestamp', + 'transactionpayment', + 'treasury', + 'statetriemigration', + 'structure', + 'system', + 'utility', + 'vesting', + 'parachainsystem', + 'parachaininfo', + 'evm', + 'evmcodersubstrate', + 'evmcontracthelpers', + 'evmmigration', + 'evmtransactionpayment', + 'ethereum', + 'fungible', + 'xcmpqueue', + 'polkadotxcm', + 'cumulusxcm', + 'dmpqueue', + 'inflation', + 'unique', + 'nonfungible', + 'charging', + 'configuration', + 'tokens', + 'xtokens', + 'maintenance', +]; + +// Pallets that depend on consensus and governance configuration +const consensusPallets = [ + 'sudo', + 'aura', + 'auraext', +]; + +describe('Pallet presence', () => { + before(async () => { + await usingPlaygrounds(async helper => { + const runtimeVersion = await helper.callRpc('api.rpc.state.getRuntimeVersion', []); + const chain = runtimeVersion.specName; + + const refungible = 'refungible'; + const foreignAssets = 'foreignassets'; + const appPromotion = 'apppromotion'; + const collatorSelection = ['authorship', 'session', 'collatorselection']; + const preimage = ['preimage']; + const governance = [ + 'council', + 'councilmembership', + 'democracy', + 'fellowshipcollective', + 'fellowshipreferenda', + 'origins', + 'scheduler', + 'technicalcommittee', + 'technicalcommitteemembership', + 'identity', + ]; + const testUtils = 'testutils'; + + if(chain.eq('opal')) { + requiredPallets.push( + refungible, + foreignAssets, + appPromotion, + testUtils, + ...collatorSelection, + ...preimage, + ...governance, + ); + } else if(chain.eq('quartz') || chain.eq('sapphire')) { + requiredPallets.push( + refungible, + appPromotion, + foreignAssets, + ...collatorSelection, + ...preimage, + ...governance, + ); + } else if(chain.eq('unique')) { + // Insert Unique additional pallets here + requiredPallets.push( + refungible, + foreignAssets, + appPromotion, + ...preimage, + ...governance, + ); + } + }); + }); + + itSub('Required pallets are present', ({helper}) => { + expect(helper.fetchAllPalletNames()).to.contain.members([...requiredPallets].sort()); + }); + + itSub('Governance and consensus pallets are present', ({helper}) => { + expect(helper.fetchAllPalletNames()).to.contain.members([...consensusPallets].sort()); + }); + + itSub('No extra pallets are included', ({helper}) => { + expect(helper.fetchAllPalletNames().sort()).to.be.deep.equal([...requiredPallets, ...consensusPallets].sort()); + }); +}); --- /dev/null +++ b/js-packages/tests/performance.seq.test.ts @@ -0,0 +1,125 @@ +// Copyright 2019-2023 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {ApiPromise} from '@polkadot/api'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, usingPlaygrounds} from './util/index.js'; +import type {ICrossAccountId, IProperty} from '@unique/playgrounds/types.js'; +import {UniqueHelper} from '@unique/playgrounds/unique.js'; + +describe('Performace tests', () => { + let alice: IKeyringPair; + const MAX_TOKENS_TO_MINT = 120; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([100_000n], donor); + }); + }); + + itSub('NFT tokens minting', async ({helper}) => { + const propertyKey = 'prop-a'; + const collection = await helper.nft.mintCollection(alice, { + name: 'test properties', + description: 'test properties collection', + tokenPrefix: 'TPC', + tokenPropertyPermissions: [ + {key: propertyKey, permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}, + ], + }); + + const step = 1_000; + const sizeOfKey = sizeOfEncodedStr(propertyKey); + let currentSize = step; + let startCount = 0; + let minterFunc = tryMintUnsafeRPC; + try { + startCount = await tryMintUnsafeRPC(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address}); + } + catch (e) { + startCount = await tryMintExplicit(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address}); + minterFunc = tryMintExplicit; + } + + expect(startCount).to.be.equal(MAX_TOKENS_TO_MINT); + + while(currentSize <= 32_000) { + const property = {key: propertyKey, value: 'A'.repeat(currentSize - sizeOfKey - sizeOfInt(currentSize))}; + const tokens = await minterFunc(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address}, property); + expect(tokens).to.be.equal(MAX_TOKENS_TO_MINT); + + currentSize += step; + await helper.wait.newBlocks(2); + } + }); +}); + + +const dryRun = async (api: ApiPromise, signer: IKeyringPair, tx: any) => { + const signed = await tx.signAsync(signer); + const dryRun = await api.rpc.system.dryRun(signed.toHex()); + return dryRun.isOk && dryRun.asOk.isOk; +}; + +const getTokens = (tokensCount: number, owner: ICrossAccountId, property?: IProperty) => (new Array(tokensCount)).fill(0).map(() => { + const token = {owner} as {owner: ICrossAccountId, properties?: IProperty[]}; + if(property) token.properties = [property]; + return token; +}); + +const tryMintUnsafeRPC = async (helper: UniqueHelper, signer: IKeyringPair, tokensCount: number, collectionId: number, owner: ICrossAccountId, property?: IProperty): Promise => { + if(tokensCount < 10) console.log('try mint', tokensCount, 'tokens'); + const tokens = getTokens(tokensCount, owner, property); + const tx = helper.constructApiCall('api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]); + if(!(await dryRun(helper.getApi(), signer, tx))) { + if(tokensCount < 2) return 0; + return await tryMintUnsafeRPC(helper, signer, tokensCount - 1, collectionId, owner, property); + } + await helper.executeExtrinsic(signer, 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]); + return tokensCount; +}; + +const tryMintExplicit = async (helper: UniqueHelper, signer: IKeyringPair, tokensCount: number, collectionId: number, owner: ICrossAccountId, property?: IProperty): Promise => { + const tokens = getTokens(tokensCount, owner, property); + try { + await helper.executeExtrinsic(signer, 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]); + } + catch (e) { + if(tokensCount < 2) return 0; + return await tryMintExplicit(helper, signer, tokensCount - 1, collectionId, owner, property); + } + return tokensCount; +}; + +function sizeOfInt(i: number) { + if(i < 0 || i > 0xffffffff) throw new Error('out of range'); + if(i < 0b11_1111) { + return 1; + } else if(i < 0b11_1111_1111_1111) { + return 2; + } else if(i < 0b11_1111_1111_1111_1111_1111_1111_1111) { + return 4; + } else { + return 5; + } +} + +const UTF8_ENCODER = new TextEncoder(); +function sizeOfEncodedStr(v: string) { + const encoded = UTF8_ENCODER.encode(v); + return sizeOfInt(encoded.length) + encoded.length; +} --- /dev/null +++ b/js-packages/tests/refungible.test.ts @@ -0,0 +1,132 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from './util/index.js'; + +const MAX_REFUNGIBLE_PIECES = 1_000_000_000_000_000_000_000n; + +describe('integration test: Refungible functionality:', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); + }); + }); + + itSub('Create refungible collection and token', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + + const itemCountBefore = await collection.getLastTokenId(); + const token = await collection.mintToken(alice, 100n); + + const itemCountAfter = await collection.getLastTokenId(); + + // What to expect + expect(token?.tokenId).to.be.gte(itemCountBefore); + expect(itemCountAfter).to.be.equal(itemCountBefore + 1); + expect(itemCountAfter.toString()).to.be.equal(token?.tokenId.toString()); + }); + + itSub('Checking RPC methods when interacting with maximum allowed values (MAX_REFUNGIBLE_PIECES)', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + + const token = await collection.mintToken(alice, MAX_REFUNGIBLE_PIECES); + + expect(await collection.getTokenBalance(token.tokenId, {Substrate: alice.address})).to.be.equal(MAX_REFUNGIBLE_PIECES); + + await collection.transferToken(alice, token.tokenId, {Substrate: bob.address}, MAX_REFUNGIBLE_PIECES); + expect(await collection.getTokenBalance(token.tokenId, {Substrate: bob.address})).to.be.equal(MAX_REFUNGIBLE_PIECES); + expect(await token.getTotalPieces()).to.be.equal(MAX_REFUNGIBLE_PIECES); + + await expect(collection.mintToken(alice, MAX_REFUNGIBLE_PIECES + 1n)) + .to.eventually.be.rejectedWith(/refungible\.WrongRefungiblePieces/); + }); + + itSub('RPC method tokenOwners for refungible collection and token', async ({helper}) => { + const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; + const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => ({Substrate: keyring.address})); + + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + + const token = await collection.mintToken(alice, 10_000n); + + await token.transfer(alice, {Substrate: bob.address}, 1000n); + await token.transfer(alice, ethAcc, 900n); + + for(let i = 0; i < 7; i++) { + await token.transfer(alice, facelessCrowd[i], 50n * BigInt(i + 1)); + } + + const owners = await token.getTop10Owners(); + + // What to expect + expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]); + expect(owners.length).to.be.equal(10); + + const [eleven] = await helper.arrange.createAccounts([0n], donor); + expect(await token.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true; + expect((await token.getTop10Owners()).length).to.be.equal(10); + }); + + itSub('Create multiple tokens', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + // TODO: fix mintMultipleTokens + // await collection.mintMultipleTokens(alice, [ + // {owner: {Substrate: alice.address}, pieces: 1n}, + // {owner: {Substrate: alice.address}, pieces: 2n}, + // {owner: {Substrate: alice.address}, pieces: 100n}, + // ]); + await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, [ + {pieces: 1n}, + {pieces: 2n}, + {pieces: 100n}, + ]); + const lastTokenId = await collection.getLastTokenId(); + expect(lastTokenId).to.be.equal(3); + expect(await collection.getTokenBalance(lastTokenId, {Substrate: alice.address})).to.be.equal(100n); + }); + + itSub('Set allowance for token', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); + + expect(await token.approve(alice, {Substrate: bob.address}, 60n)).to.be.true; + expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n); + + expect(await token.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(80n); + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(20n); + expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n); + }); + + itSub('Create new collection with properties', async ({helper}) => { + const properties = [{key: 'key1', value: 'val1'}]; + const tokenPropertyPermissions = [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}]; + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test', properties, tokenPropertyPermissions}); + const info = await collection.getData(); + expect(info?.raw.properties).to.be.deep.equal(properties); + expect(info?.raw.tokenPropertyPermissions).to.be.deep.equal(tokenPropertyPermissions); + }); +}); --- /dev/null +++ b/js-packages/tests/removeCollectionAdmin.test.ts @@ -0,0 +1,113 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from './util/index.js'; +import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js'; + +describe('Integration Test removeCollectionAdmin(collection_id, account_id):', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); + }); + }); + + itSub('Remove collection admin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-1', tokenPrefix: 'RCA'}); + const collectionInfo = await collection.getData(); + expect(collectionInfo?.raw.owner.toString()).to.be.deep.eq(alice.address); + // first - add collection admin Bob + await collection.addAdmin(alice, {Substrate: bob.address}); + + const adminListAfterAddAdmin = await collection.getAdmins(); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); + + // then remove bob from admins of collection + await collection.removeAdmin(alice, {Substrate: bob.address}); + + const adminListAfterRemoveAdmin = await collection.getAdmins(); + expect(adminListAfterRemoveAdmin).not.to.be.deep.contains({Substrate: bob.address}); + }); + + itSub('Remove admin from collection that has no admins', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-2', tokenPrefix: 'RCA'}); + + const adminListBeforeAddAdmin = await collection.getAdmins(); + expect(adminListBeforeAddAdmin).to.have.lengthOf(0); + + await expect(collection.removeAdmin(alice, {Substrate: alice.address})).to.be.rejectedWith('common.UserIsNotCollectionAdmin'); + }); +}); + +describe('Negative Integration Test removeCollectionAdmin(collection_id, account_id):', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); + }); + }); + + itSub('Can\'t remove collection admin from not existing collection', async ({helper}) => { + const collectionId = NON_EXISTENT_COLLECTION_ID; + + await expect(helper.collection.removeAdmin(alice, collectionId, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('Can\'t remove collection admin from deleted collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-2', tokenPrefix: 'RCA'}); + + expect(await collection.burn(alice)).to.be.true; + + await expect(helper.collection.removeAdmin(alice, collection.collectionId, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('Regular user can\'t remove collection admin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-3', tokenPrefix: 'RCA'}); + + await collection.addAdmin(alice, {Substrate: bob.address}); + + await expect(collection.removeAdmin(charlie, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('Admin can\'t remove collection admin.', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-4', tokenPrefix: 'RCA'}); + + await collection.addAdmin(alice, {Substrate: bob.address}); + await collection.addAdmin(alice, {Substrate: charlie.address}); + + const adminListAfterAddAdmin = await collection.getAdmins(); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: charlie.address}); + + await expect(collection.removeAdmin(charlie, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.NoPermission/); + + const adminListAfterRemoveAdmin = await collection.getAdmins(); + expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: bob.address}); + expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: charlie.address}); + }); +}); --- /dev/null +++ b/js-packages/tests/removeCollectionSponsor.test.ts @@ -0,0 +1,126 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from './util/index.js'; +import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js'; + +describe('integration test: ext. removeCollectionSponsor():', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); + }); + }); + + itSub('Removing NFT collection sponsor stops sponsorship', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-1', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + await collection.removeSponsor(alice); + + // Find unused address + const [zeroBalance] = await helper.arrange.createAccounts([0n], donor); + + // Mint token for unused address + const token = await collection.mintToken(alice, {Substrate: zeroBalance.address}); + + // Transfer this tokens from unused address to Alice - should fail + const sponsorBalanceBefore = await helper.balance.getSubstrate(bob.address); + await expect(token.transfer(zeroBalance, {Substrate: alice.address})) + .to.be.rejectedWith('Inability to pay some fees'); + const sponsorBalanceAfter = await helper.balance.getSubstrate(bob.address); + + expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore); + }); + + itSub('Remove a sponsor after it was already removed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-2', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + await expect(collection.removeSponsor(alice)).to.not.be.rejected; + await expect(collection.removeSponsor(alice)).to.not.be.rejected; + }); + + itSub('Remove sponsor in a collection that never had the sponsor set', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-3', tokenPrefix: 'RCS'}); + await expect(collection.removeSponsor(alice)).to.not.be.rejected; + }); + + itSub('Remove sponsor for a collection that had the sponsor set, but not confirmed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-4', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await expect(collection.removeSponsor(alice)).to.not.be.rejected; + }); + + itSub('Remove a sponsor from a collection with collection admin permissions', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-1', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.addAdmin(alice, {Substrate: charlie.address}); + await expect(collection.removeSponsor(charlie)).not.to.be.rejected; + }); +}); + +describe('(!negative test!) integration test: ext. removeCollectionSponsor():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); + }); + }); + + itSub('(!negative test!) Remove sponsor for a collection that never existed', async ({helper}) => { + const collectionId = NON_EXISTENT_COLLECTION_ID; + await expect(helper.collection.removeSponsor(alice, collectionId)).to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('(!negative test!) Remove sponsor for a collection by regular user', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-2', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await expect(collection.removeSponsor(charlie)).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('(!negative test!) Remove sponsor in a destroyed collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-3', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.burn(alice); + await expect(collection.removeSponsor(alice)).to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('Set - remove - confirm: fails', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-4', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.removeSponsor(alice); + await expect(collection.confirmSponsorship(bob)).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); + }); + + itSub('Set - confirm - remove - confirm: Sponsor cannot come back', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-5', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + await collection.removeSponsor(alice); + await expect(collection.confirmSponsorship(bob)).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); + }); +}); --- /dev/null +++ b/js-packages/tests/rpc.test.ts @@ -0,0 +1,68 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, itSub, expect} from './util/index.js'; +import {ICrossAccountId} from '@unique/playgrounds/types.js'; + +describe('integration test: RPC methods', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); + }); + }); + + itSub('returns None for fungible collection', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'RPC-1', tokenPrefix: 'RPC'}); + const owner = (await helper.callRpc('api.rpc.unique.tokenOwner', [collection.collectionId, 0])).toJSON() as any; + expect(owner).to.be.null; + }); + + itSub('RPC method tokenOwners for fungible collection and token', async ({helper}) => { + // Set-up a few token owners of all stripes + const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; + const facelessCrowd = (await helper.arrange.createAccounts([0n, 0n, 0n, 0n, 0n, 0n, 0n], donor)) + .map(i => ({Substrate: i.address})); + + const collection = await helper.ft.mintCollection(alice, {name: 'RPC-2', tokenPrefix: 'RPC'}); + // mint some maximum (u128) amounts of tokens possible + await collection.mint(alice, (1n << 128n) - 1n); + + await collection.transfer(alice, {Substrate: bob.address}, 1000n); + await collection.transfer(alice, ethAcc, 900n); + + for(let i = 0; i < facelessCrowd.length; i++) { + await collection.transfer(alice, facelessCrowd[i], 1n); + } + // Set-up over + + const owners = await helper.callRpc('api.rpc.unique.tokenOwners', [collection.collectionId, 0]); + const ids = owners.toHuman() as ICrossAccountId[]; + + expect(ids).to.have.deep.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]); + expect(owners.length == 10).to.be.true; + + // Make sure only 10 results are returned with this RPC + const [eleven] = await helper.arrange.createAccounts([0n], donor); + expect(await collection.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true; + expect((await helper.callRpc('api.rpc.unique.tokenOwners', [collection.collectionId, 0])).length).to.be.equal(10); + }); +}); --- /dev/null +++ b/js-packages/tests/scheduler.seqtest.ts @@ -0,0 +1,796 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {expect, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js'; + +describe('Scheduling token and balance transfers', () => { + let superuser: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.UniqueScheduler]); + + superuser = await privateKey('//Alice'); + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + + await helper.testUtils.enable(Pallets.TestUtils); + }); + }); + + beforeEach(async () => { + await usingPlaygrounds(async (helper) => { + await helper.wait.noScheduledTasks(); + }); + }); + + itSched('Can delay a transfer of an owned token', async (scheduleKind, {helper}) => { + const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); + const token = await collection.mintToken(alice); + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; + const blocksBeforeExecution = 4; + 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}); + + await helper.wait.forParachainBlockNumber(executionBlock); + + expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); + }); + + itSched('Can transfer funds periodically', async (scheduleKind, {helper}) => { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; + const waitForBlocks = 1; + + const amount = 1n * helper.balance.getOneTokenNominal(); + const periodic = { + period: 2, + repetitions: 2, + }; + + const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address); + + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) + .balance.transferToSubstrate(alice, bob.address, amount); + const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; + + await helper.wait.forParachainBlockNumber(executionBlock); + + const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address); + expect(bobsBalanceAfterFirst) + .to.be.equal( + bobsBalanceBefore + 1n * amount, + '#1 Balance of the recipient should be increased by 1 * amount', + ); + + await helper.wait.forParachainBlockNumber(executionBlock + periodic.period); + + const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address); + expect(bobsBalanceAfterSecond) + .to.be.equal( + bobsBalanceBefore + 2n * amount, + '#2 Balance of the recipient should be increased by 2 * amount', + ); + }); + + itSub('Can cancel a scheduled operation which has not yet taken effect', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); + const token = await collection.mintToken(alice); + + const scheduledId = helper.arrange.makeScheduledId(); + const waitForBlocks = 4; + + expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.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); + + await helper.wait.forParachainBlockNumber(executionBlock); + + expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address}); + }); + + itSub('Can cancel a periodic operation (transfer of funds)', async ({helper}) => { + const waitForBlocks = 1; + const periodic = { + period: 3, + repetitions: 2, + }; + + const scheduledId = helper.arrange.makeScheduledId(); + + const amount = 1n * helper.balance.getOneTokenNominal(); + + const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address); + + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) + .balance.transferToSubstrate(alice, bob.address, amount); + const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; + + await helper.wait.forParachainBlockNumber(executionBlock); + + const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address); + + expect(bobsBalanceAfterFirst) + .to.be.equal( + bobsBalanceBefore + 1n * amount, + '#1 Balance of the recipient should be increased by 1 * amount', + ); + + await helper.scheduler.cancelScheduled(alice, scheduledId); + await helper.wait.forParachainBlockNumber(executionBlock + periodic.period); + + const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address); + expect(bobsBalanceAfterSecond) + .to.be.equal( + bobsBalanceAfterFirst, + '#2 Balance of the recipient should not be changed', + ); + }); + + itSched('scheduler will not insert more tasks than allowed', async (scheduleKind, {helper}) => { + const maxScheduledPerBlock = 50; + let fillScheduledIds = new Array(maxScheduledPerBlock); + let extraScheduledId = undefined; + + if(scheduleKind == 'named') { + const scheduledIds = helper.arrange.makeScheduledIds(maxScheduledPerBlock + 1); + fillScheduledIds = scheduledIds.slice(0, maxScheduledPerBlock); + extraScheduledId = scheduledIds[maxScheduledPerBlock]; + } + + // Since the dev node has Instant Seal, + // we need a larger gap between the current block and the target one. + // + // We will schedule `maxScheduledPerBlock` transaction into the target block, + // so we need at least `maxScheduledPerBlock`-wide gap. + // We add some additional blocks to this gap to mitigate possible PolkadotJS delays. + const waitForBlocks = await helper.arrange.isDevNode() ? maxScheduledPerBlock + 5 : 5; + + const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks; + + const amount = 1n * helper.balance.getOneTokenNominal(); + + const balanceBefore = await helper.balance.getSubstrate(bob.address); + + // Fill the target block + for(let i = 0; i < maxScheduledPerBlock; i++) { + await helper.scheduler.scheduleAt(executionBlock, {scheduledId: fillScheduledIds[i]}) + .balance.transferToSubstrate(superuser, bob.address, amount); + } + + // Try to schedule a task into a full block + await expect(helper.scheduler.scheduleAt(executionBlock, {scheduledId: extraScheduledId}) + .balance.transferToSubstrate(superuser, bob.address, amount)) + .to.be.rejectedWith(/scheduler\.AgendaIsExhausted/); + + await helper.wait.forParachainBlockNumber(executionBlock); + + const balanceAfter = await helper.balance.getSubstrate(bob.address); + + expect(balanceAfter > balanceBefore).to.be.true; + + const diff = balanceAfter - balanceBefore; + expect(diff).to.be.equal(amount * BigInt(maxScheduledPerBlock)); + }); + + itSched.ifWithPallets('Scheduled tasks are transactional', [Pallets.TestUtils], async (scheduleKind, {helper}) => { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; + const waitForBlocks = 4; + + const initTestVal = 42; + const changedTestVal = 111; + + await helper.testUtils.setTestValue(alice, initTestVal); + + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId}) + .testUtils.setTestValueAndRollback(alice, changedTestVal); + const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; + + await helper.wait.forParachainBlockNumber(executionBlock); + + const testVal = await helper.testUtils.testValue(); + expect(testVal, 'The test value should NOT be commited') + .to.be.equal(initTestVal); + }); + + itSched.ifWithPallets('Scheduled tasks should take correct fees', [Pallets.TestUtils], async function(scheduleKind, {helper}) { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; + const waitForBlocks = 4; + const periodic = { + period: 2, + repetitions: 2, + }; + + const dummyTx = helper.constructApiCall('api.tx.testUtils.justTakeFee', []); + const scheduledLen = dummyTx.callIndex.length; + + const expectedScheduledFee = (await helper.getPaymentInfo(alice, dummyTx, scheduledLen)) + .partialFee.toBigInt(); + + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) + .testUtils.justTakeFee(alice); + const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; + + const aliceInitBalance = await helper.balance.getSubstrate(alice.address); + let diff; + + await helper.wait.forParachainBlockNumber(executionBlock); + + const aliceBalanceAfterFirst = await helper.balance.getSubstrate(alice.address); + expect( + aliceBalanceAfterFirst < aliceInitBalance, + '[after execution #1] Scheduled task should take a fee', + ).to.be.true; + + diff = aliceInitBalance - aliceBalanceAfterFirst; + expect(diff).to.be.equal( + expectedScheduledFee, + 'Scheduled task should take the right amount of fees', + ); + + await helper.wait.forParachainBlockNumber(executionBlock + periodic.period); + + const aliceBalanceAfterSecond = await helper.balance.getSubstrate(alice.address); + expect( + aliceBalanceAfterSecond < aliceBalanceAfterFirst, + '[after execution #2] Scheduled task should take a fee', + ).to.be.true; + + diff = aliceBalanceAfterFirst - aliceBalanceAfterSecond; + expect(diff).to.be.equal( + expectedScheduledFee, + 'Scheduled task should take the right amount of fees', + ); + }); + + // Check if we can cancel a scheduled periodic operation + // in the same block in which it is running + itSub.ifWithPallets('Can cancel the periodic sheduled tx when the tx is running', [Pallets.TestUtils], async ({helper}) => { + const currentBlockNumber = await helper.chain.getLatestBlockNumber(); + const blocksBeforeExecution = 10; + const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution; + + const [ + scheduledId, + scheduledCancelId, + ] = helper.arrange.makeScheduledIds(2); + + const periodic = { + period: 5, + repetitions: 5, + }; + + const initTestVal = 0; + const incTestVal = initTestVal + 1; + const finalTestVal = initTestVal + 2; + + await helper.testUtils.setTestValue(alice, initTestVal); + + await helper.scheduler.scheduleAt(firstExecutionBlockNumber, {scheduledId, periodic}) + .testUtils.incTestValue(alice); + + // Cancel the inc tx after 2 executions + // *in the same block* in which the second execution is scheduled + await helper.scheduler.scheduleAt( + firstExecutionBlockNumber + periodic.period, + {scheduledId: scheduledCancelId}, + ).scheduler.cancelScheduled(alice, scheduledId); + + await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber); + + // execution #0 + expect(await helper.testUtils.testValue()) + .to.be.equal(incTestVal); + + await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period); + + // execution #1 + expect(await helper.testUtils.testValue()) + .to.be.equal(finalTestVal); + + for(let i = 1; i < periodic.repetitions; i++) { + await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period * (i + 1)); + expect(await helper.testUtils.testValue()) + .to.be.equal(finalTestVal); + } + }); + + itSub.ifWithPallets('A scheduled operation can cancel itself', [Pallets.TestUtils], async ({helper}) => { + const scheduledId = helper.arrange.makeScheduledId(); + const waitForBlocks = 4; + const periodic = { + period: 2, + repetitions: 5, + }; + + const initTestVal = 0; + const maxTestVal = 2; + + await helper.testUtils.setTestValue(alice, initTestVal); + + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) + .testUtils.selfCancelingInc(alice, scheduledId, maxTestVal); + const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; + + await helper.wait.forParachainBlockNumber(executionBlock); + + // execution #0 + expect(await helper.testUtils.testValue()) + .to.be.equal(initTestVal + 1); + + await helper.wait.forParachainBlockNumber(executionBlock + periodic.period); + + // execution #1 + expect(await helper.testUtils.testValue()) + .to.be.equal(initTestVal + 2); + + await helper.wait.forParachainBlockNumber(executionBlock + 2 * periodic.period); + + // + expect(await helper.testUtils.testValue()) + .to.be.equal(initTestVal + 2); + }); + + itSub('Root can cancel any scheduled operation', async ({helper}) => { + const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'}); + const token = await collection.mintToken(bob); + + const scheduledId = helper.arrange.makeScheduledId(); + const waitForBlocks = 4; + + 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); + + await helper.wait.forParachainBlockNumber(executionBlock); + + expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); + }); + + itSched('Root can set prioritized scheduled operation', async (scheduleKind, {helper}) => { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; + const waitForBlocks = 4; + + const amount = 42n * helper.balance.getOneTokenNominal(); + + const balanceBefore = await helper.balance.getSubstrate(charlie.address); + + await helper.getSudo() + .scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42}) + .balance.forceTransferToSubstrate(superuser, bob.address, charlie.address, amount); + const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; + + await helper.wait.forParachainBlockNumber(executionBlock); + + const balanceAfter = await helper.balance.getSubstrate(charlie.address); + + expect(balanceAfter > balanceBefore).to.be.true; + + const diff = balanceAfter - balanceBefore; + expect(diff).to.be.equal(amount); + }); + + itSub("Root can change scheduled operation's priority", async ({helper}) => { + const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'}); + const token = await collection.mintToken(bob); + + const scheduledId = helper.arrange.makeScheduledId(); + const waitForBlocks = 6; + + 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; + await helper.getSudo().scheduler.changePriority(superuser, scheduledId, priority); + + const priorityChanged = await helper.wait.expectEvent(waitForBlocks, Event.UniqueScheduler.PriorityChanged); + + const [blockNumber, index] = priorityChanged.task(); + expect(blockNumber).to.be.equal(executionBlock); + expect(index).to.be.equal(0); + + expect(priorityChanged.priority().toString()).to.be.equal(priority.toString()); + }); + + itSub('Prioritized operations execute in valid order', async ({helper}) => { + const [ + scheduledFirstId, + scheduledSecondId, + ] = helper.arrange.makeScheduledIds(2); + + const currentBlockNumber = await helper.chain.getLatestBlockNumber(); + const blocksBeforeExecution = 6; + const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution; + + const prioHigh = 0; + const prioLow = 255; + + const periodic = { + period: 6, + repetitions: 2, + }; + + const amount = 1n * helper.balance.getOneTokenNominal(); + + // Scheduler a task with a lower priority first, then with a higher priority + await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, { + scheduledId: scheduledFirstId, + priority: prioLow, + periodic, + }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount); + + await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, { + scheduledId: scheduledSecondId, + priority: prioHigh, + periodic, + }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount); + + const capture = await helper.arrange.captureEvents('scheduler', 'Dispatched'); + + await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber); + + // Flip priorities + await helper.getSudo().scheduler.changePriority(superuser, scheduledFirstId, prioHigh); + await helper.getSudo().scheduler.changePriority(superuser, scheduledSecondId, prioLow); + + await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period); + + const dispatchEvents = capture.extractCapturedEvents(); + expect(dispatchEvents.length).to.be.equal(4); + + const dispatchedIds = dispatchEvents.map(r => r.event.data[1].toString()); + + const firstExecuctionIds = [dispatchedIds[0], dispatchedIds[1]]; + const secondExecuctionIds = [dispatchedIds[2], dispatchedIds[3]]; + + expect(firstExecuctionIds[0]).to.be.equal(scheduledSecondId); + expect(firstExecuctionIds[1]).to.be.equal(scheduledFirstId); + + expect(secondExecuctionIds[0]).to.be.equal(scheduledFirstId); + expect(secondExecuctionIds[1]).to.be.equal(scheduledSecondId); + }); + + itSched('Periodic operations always can be rescheduled', async (scheduleKind, {helper}) => { + const maxScheduledPerBlock = 50; + const numFilledBlocks = 3; + const ids = helper.arrange.makeScheduledIds(numFilledBlocks * maxScheduledPerBlock + 1); + const periodicId = scheduleKind == 'named' ? ids[0] : undefined; + const fillIds = ids.slice(1); + + const fillScheduleFn = scheduleKind == 'named' ? 'scheduleNamed' : 'schedule'; + + const initTestVal = 0; + const firstExecTestVal = 1; + const secondExecTestVal = 2; + await helper.testUtils.setTestValue(alice, initTestVal); + + const currentBlockNumber = await helper.chain.getLatestBlockNumber(); + const blocksBeforeExecution = 8; + const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution; + + const period = 5; + + const periodic = { + period, + repetitions: 2, + }; + + // Fill `numFilledBlocks` blocks beginning from the block in which the second execution should occur + const txs = []; + for(let offset = 0; offset < numFilledBlocks; offset ++) { + for(let i = 0; i < maxScheduledPerBlock; i++) { + + const scheduledTx = helper.constructApiCall('api.tx.balances.transfer', [bob.address, 1n]); + + const when = firstExecutionBlockNumber + period + offset; + const mandatoryArgs = [when, null, null, scheduledTx]; + const scheduleArgs = scheduleKind == 'named' + ? [fillIds[i + offset * maxScheduledPerBlock], ...mandatoryArgs] + : mandatoryArgs; + + const tx = helper.constructApiCall(`api.tx.scheduler.${fillScheduleFn}`, scheduleArgs); + + txs.push(tx); + } + } + await helper.executeExtrinsic(alice, 'api.tx.testUtils.batchAll', [txs], true); + + await helper.scheduler.scheduleAt(firstExecutionBlockNumber, { + scheduledId: periodicId, + periodic, + }).testUtils.incTestValue(alice); + + await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber); + expect(await helper.testUtils.testValue()).to.be.equal(firstExecTestVal); + + await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + period + numFilledBlocks); + + // The periodic operation should be postponed by `numFilledBlocks` + for(let i = 0; i < numFilledBlocks; i++) { + expect(await helper.testUtils.testValue(firstExecutionBlockNumber + period + i)).to.be.equal(firstExecTestVal); + } + + // After the `numFilledBlocks` the periodic operation will eventually be executed + expect(await helper.testUtils.testValue()).to.be.equal(secondExecTestVal); + }); + + itSched('scheduled operations does not change nonce', async (scheduleKind, {helper}) => { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; + const blocksBeforeExecution = 4; + + await helper.scheduler + .scheduleAfter(blocksBeforeExecution, {scheduledId}) + .balance.transferToSubstrate(alice, bob.address, 1n); + const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1; + + const initNonce = await helper.chain.getNonce(alice.address); + + await helper.wait.forParachainBlockNumber(executionBlock); + + const finalNonce = await helper.chain.getNonce(alice.address); + + expect(initNonce).to.be.equal(finalNonce); + }); +}); + +describe('Negative Test: Scheduling', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.UniqueScheduler]); + + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + + await helper.testUtils.enable(Pallets.TestUtils); + }); + }); + + itSub("Can't overwrite a scheduled ID", async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); + const token = await collection.mintToken(alice); + + const scheduledId = helper.arrange.makeScheduledId(); + const waitForBlocks = 4; + + 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}); + await expect(scheduled.balance.transferToSubstrate(alice, bob.address, 1n * helper.balance.getOneTokenNominal())) + .to.be.rejectedWith(/scheduler\.FailedToSchedule/); + + const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address); + + await helper.wait.forParachainBlockNumber(executionBlock); + + const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address); + + expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); + expect(bobsBalanceBefore).to.be.equal(bobsBalanceAfter); + }); + + itSub("Can't cancel an operation which is not scheduled", async ({helper}) => { + const scheduledId = helper.arrange.makeScheduledId(); + await expect(helper.scheduler.cancelScheduled(alice, scheduledId)) + .to.be.rejectedWith(/scheduler\.NotFound/); + }); + + itSub("Can't cancel a non-owned scheduled operation", async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); + const token = await collection.mintToken(alice); + + const scheduledId = helper.arrange.makeScheduledId(); + const waitForBlocks = 4; + + 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)) + .to.be.rejectedWith(/BadOrigin/); + + await helper.wait.forParachainBlockNumber(executionBlock); + + expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); + }); + + itSched("Regular user can't set prioritized scheduled operation", async (scheduleKind, {helper}) => { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; + const waitForBlocks = 4; + + const amount = 42n * helper.balance.getOneTokenNominal(); + + const balanceBefore = await helper.balance.getSubstrate(bob.address); + + const scheduled = helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42}); + + await expect(scheduled.balance.transferToSubstrate(alice, bob.address, amount)) + .to.be.rejectedWith(/BadOrigin/); + + const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; + + await helper.wait.forParachainBlockNumber(executionBlock); + + const balanceAfter = await helper.balance.getSubstrate(bob.address); + + expect(balanceAfter).to.be.equal(balanceBefore); + }); + + itSub("Regular user can't change scheduled operation's priority", async ({helper}) => { + const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'}); + const token = await collection.mintToken(bob); + + const scheduledId = helper.arrange.makeScheduledId(); + const waitForBlocks = 4; + + 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)) + .to.be.rejectedWith(/BadOrigin/); + + await helper.wait.expectEvent(waitForBlocks, Event.UniqueScheduler.PriorityChanged); + }); +}); + +// Implementation of the functionality tested here was postponed/shelved +describe.skip('Sponsoring scheduling', () => { + // let alice: IKeyringPair; + // let bob: IKeyringPair; + + // before(async() => { + // await usingApi(async (_, privateKey) => { + // alice = privateKey('//Alice'); + // bob = privateKey('//Bob'); + // }); + // }); + + it('Can sponsor scheduling a transaction', async () => { + // const collectionId = await createCollectionExpectSuccess(); + // await setCollectionSponsorExpectSuccess(collectionId, bob.address); + // await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); + + // await usingApi(async api => { + // const scheduledId = await makeScheduledId(); + // const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address); + + // const bobBalanceBefore = await getFreeBalance(bob); + // const waitForBlocks = 4; + // // no need to wait to check, fees must be deducted on scheduling, immediately + // await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 0, waitForBlocks, scheduledId); + // const bobBalanceAfter = await getFreeBalance(bob); + // // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true; + // expect(bobBalanceAfter < bobBalanceBefore).to.be.true; + // // wait for sequentiality matters + // await waitNewBlocks(waitForBlocks - 1); + // }); + }); + + it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => { + // await usingApi(async (api, privateKey) => { + // // Find an empty, unused account + // const zeroBalance = await findUnusedAddress(api, privateKey); + + // const collectionId = await createCollectionExpectSuccess(); + + // // Add zeroBalance address to allow list + // await enablePublicMintingExpectSuccess(alice, collectionId); + // await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address); + + // // Grace zeroBalance with money, enough to cover future transactions + // const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE); + // await submitTransactionAsync(alice, balanceTx); + + // // Mint a fresh NFT + // const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT'); + // const scheduledId = await makeScheduledId(); + + // // Schedule transfer of the NFT a few blocks ahead + // const waitForBlocks = 5; + // await scheduleTransferExpectSuccess(api, collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, scheduledId); + + // // Get rid of the account's funds before the scheduled transaction takes place + // const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n); + // const events = await submitTransactionAsync(zeroBalance, balanceTx2); + // expect(getGenericResult(events).success).to.be.true; + // /*const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved? + // const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any); + // const events = await submitTransactionAsync(alice, sudoTx); + // expect(getGenericResult(events).success).to.be.true;*/ + + // // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions + // await waitNewBlocks(waitForBlocks - 3); + + // expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address)); + // }); + }); + + it('Sponsor going bankrupt does not impact a scheduled transaction', async () => { + // const collectionId = await createCollectionExpectSuccess(); + + // await usingApi(async (api, privateKey) => { + // const zeroBalance = await findUnusedAddress(api, privateKey); + // const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE); + // await submitTransactionAsync(alice, balanceTx); + + // await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address); + // await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance); + + // const scheduledId = await makeScheduledId(); + // const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address); + + // const waitForBlocks = 5; + // await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, scheduledId); + + // const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); + // const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any); + // const events = await submitTransactionAsync(alice, sudoTx); + // expect(getGenericResult(events).success).to.be.true; + + // // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions + // await waitNewBlocks(waitForBlocks - 3); + + // expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address)); + // }); + }); + + it('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => { + // const collectionId = await createCollectionExpectSuccess(); + // await setCollectionSponsorExpectSuccess(collectionId, bob.address); + // await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); + + // await usingApi(async (api, privateKey) => { + // const zeroBalance = await findUnusedAddress(api, privateKey); + + // await enablePublicMintingExpectSuccess(alice, collectionId); + // await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address); + + // const bobBalanceBefore = await getFreeBalance(bob); + + // const createData = {nft: {const_data: [], variable_data: []}}; + // const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any); + // const scheduledId = await makeScheduledId(); + + // /*const badTransaction = async function () { + // await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice); + // }; + // await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/ + + // await expect(scheduleAfter(api, creationTx, zeroBalance, 3, scheduledId, 1, 3)).to.be.rejectedWith(/Inability to pay some fees/); + + // expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore); + // }); + }); +}); --- /dev/null +++ b/js-packages/tests/setCollectionLimits.test.ts @@ -0,0 +1,201 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from './util/index.js'; +import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js'; + +const accountTokenOwnershipLimit = 0; +const sponsoredDataSize = 0; +const sponsorTransferTimeout = 1; +const tokenLimit = 10; + +describe('setCollectionLimits positive', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); + }); + }); + + itSub('execute setCollectionLimits with predefined params', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-1', tokenPrefix: 'SCL'}); + + await collection.setLimits( + alice, + { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, + sponsorTransferTimeout, + ownerCanTransfer: true, + ownerCanDestroy: true, + }, + ); + + // get collection limits defined previously + const collectionInfo = await collection.getEffectiveLimits(); + + expect(collectionInfo.accountTokenOwnershipLimit).to.be.equal(accountTokenOwnershipLimit); + expect(collectionInfo.sponsoredDataSize).to.be.equal(sponsoredDataSize); + expect(collectionInfo.tokenLimit).to.be.equal(tokenLimit); + expect(collectionInfo.sponsorTransferTimeout).to.be.equal(sponsorTransferTimeout); + expect(collectionInfo.ownerCanTransfer).to.be.true; + expect(collectionInfo.ownerCanDestroy).to.be.true; + }); + + itSub('Set the same token limit twice', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-2', tokenPrefix: 'SCL'}); + + const collectionLimits = { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, + sponsorTransferTimeout, + ownerCanTransfer: true, + ownerCanDestroy: true, + }; + + await collection.setLimits(alice, collectionLimits); + + const collectionInfo1 = await collection.getEffectiveLimits(); + + expect(collectionInfo1.tokenLimit).to.be.equal(tokenLimit); + + await collection.setLimits(alice, collectionLimits); + const collectionInfo2 = await collection.getEffectiveLimits(); + expect(collectionInfo2.tokenLimit).to.be.equal(tokenLimit); + }); + + itSub('execute setCollectionLimits from admin collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-3', tokenPrefix: 'SCL'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + + const collectionLimits = { + accountTokenOwnershipLimit, + sponsoredDataSize, + // sponsoredMintSize, + tokenLimit, + }; + + await expect(collection.setLimits(alice, collectionLimits)).to.not.be.rejected; + }); +}); + +describe('setCollectionLimits negative', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); + }); + }); + + itSub('execute setCollectionLimits for not exists collection', async ({helper}) => { + const nonExistentCollectionId = NON_EXISTENT_COLLECTION_ID; + await expect(helper.collection.setLimits( + alice, + nonExistentCollectionId, + { + accountTokenOwnershipLimit, + sponsoredDataSize, + // sponsoredMintSize, + tokenLimit, + }, + )).to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('execute setCollectionLimits from user who is not owner of this collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-1', tokenPrefix: 'SCL'}); + + await expect(collection.setLimits(bob, { + accountTokenOwnershipLimit, + sponsoredDataSize, + // sponsoredMintSize, + tokenLimit, + })).to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('fails when trying to enable OwnerCanTransfer after it was disabled', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-2', tokenPrefix: 'SCL'}); + + await collection.setLimits(alice, { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, + sponsorTransferTimeout, + ownerCanTransfer: false, + ownerCanDestroy: true, + }); + + await expect(collection.setLimits(alice, { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, + sponsorTransferTimeout, + ownerCanTransfer: true, + ownerCanDestroy: true, + })).to.be.rejectedWith(/common\.OwnerPermissionsCantBeReverted/); + }); + + itSub('fails when trying to enable OwnerCanDestroy after it was disabled', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-3', tokenPrefix: 'SCL'}); + + await collection.setLimits(alice, { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, + sponsorTransferTimeout, + ownerCanTransfer: true, + ownerCanDestroy: false, + }); + + await expect(collection.setLimits(alice, { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, + sponsorTransferTimeout, + ownerCanTransfer: true, + ownerCanDestroy: true, + })).to.be.rejectedWith(/common\.OwnerPermissionsCantBeReverted/); + }); + + itSub('Setting the higher token limit fails', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-4', tokenPrefix: 'SCL'}); + + const collectionLimits = { + accountTokenOwnershipLimit: accountTokenOwnershipLimit, + sponsoredMintSize: sponsoredDataSize, + tokenLimit: tokenLimit, + sponsorTransferTimeout, + ownerCanTransfer: true, + ownerCanDestroy: true, + }; + + // The first time + await collection.setLimits(alice, collectionLimits); + + // The second time - higher token limit + collectionLimits.tokenLimit += 1; + await expect(collection.setLimits(alice, collectionLimits)).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/); + }); +}); --- /dev/null +++ b/js-packages/tests/setCollectionSponsor.test.ts @@ -0,0 +1,120 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect, Pallets} from './util/index.js'; +import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js'; + +describe('integration test: ext. setCollectionSponsor():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); + }); + }); + + itSub('Set NFT collection sponsor', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-1-NFT', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: bob.address, + }); + }); + + itSub('Set Fungible collection sponsor', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'SetCollectionSponsor-1-FT', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: bob.address, + }); + }); + + itSub.ifWithPallets('Set ReFungible collection sponsor', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'SetCollectionSponsor-1-RFT', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: bob.address, + }); + }); + + itSub('Set the same sponsor repeatedly', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-2', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: bob.address, + }); + }); + + itSub('Replace collection sponsor', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-3', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + await expect(collection.setSponsor(alice, charlie.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: charlie.address, + }); + }); + + itSub('Collection admin add sponsor', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-4', tokenPrefix: 'SCS'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + await expect(collection.setSponsor(bob, charlie.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: charlie.address, + }); + }); +}); + +describe('(!negative test!) integration test: ext. setCollectionSponsor():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([10n, 5n], donor); + }); + }); + + itSub('(!negative test!) Add sponsor with a non-owner', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-Neg-1', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(bob, bob.address)) + .to.be.rejectedWith(/common\.NoPermission/); + }); + + itSub('(!negative test!) Add sponsor to a collection that never existed', async ({helper}) => { + const collectionId = NON_EXISTENT_COLLECTION_ID; + await expect(helper.collection.setSponsor(alice, collectionId, bob.address)) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('(!negative test!) Add sponsor to a collection that was destroyed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-Neg-2', tokenPrefix: 'SCS'}); + await collection.burn(alice); + await expect(collection.setSponsor(alice, bob.address)) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); +}); --- /dev/null +++ b/js-packages/tests/setPermissions.test.ts @@ -0,0 +1,108 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from './util/index.js'; +import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js'; + +describe('Integration Test: Set Permissions', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); + }); + }); + + itSub('can all be enabled twice', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-1', tokenPrefix: 'SP'}); + expect((await collection.getData())?.raw.permissions.access).to.not.equal('AllowList'); + + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}}); + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}}); + + const permissions = (await collection.getData())?.raw.permissions; + expect(permissions).to.be.deep.equal({ + access: 'AllowList', + mintMode: true, + nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}, + }); + }); + + itSub('can all be disabled twice', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-2', tokenPrefix: 'SP'}); + expect((await collection.getData())?.raw.permissions.access).to.equal('Normal'); + + await collection.setPermissions(alice, {access: 'AllowList', nesting: {collectionAdmin: false, tokenOwner: true, restricted: [1, 2]}}); + expect((await collection.getData())?.raw.permissions).to.be.deep.equal({ + access: 'AllowList', + mintMode: false, + nesting: {collectionAdmin: false, tokenOwner: true, restricted: [1, 2]}, + }); + + await collection.setPermissions(alice, {access: 'Normal', mintMode: false, nesting: {}}); + await collection.setPermissions(alice, {access: 'Normal', mintMode: false, nesting: {}}); + expect((await collection.getData())?.raw.permissions).to.be.deep.equal({ + access: 'Normal', + mintMode: false, + nesting: {collectionAdmin: false, tokenOwner: false, restricted: null}, + }); + }); + + itSub('collection admin can set permissions', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-2', tokenPrefix: 'SP'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + await collection.setPermissions(bob, {access: 'AllowList', mintMode: true}); + + expect((await collection.getData())?.raw.permissions.access).to.equal('AllowList'); + expect((await collection.getData())?.raw.permissions.mintMode).to.equal(true); + }); +}); + +describe('Negative Integration Test: Set Permissions', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); + }); + }); + + itSub('fails on not existing collection', async ({helper}) => { + const collectionId = NON_EXISTENT_COLLECTION_ID; + await expect(helper.collection.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('fails on removed collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-Neg-1', tokenPrefix: 'SP'}); + await collection.burn(alice); + + await expect(collection.setPermissions(alice, {access: 'AllowList', mintMode: true})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('fails when non-owner tries to set permissions', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-Neg-2', tokenPrefix: 'SP'}); + + await expect(collection.setPermissions(bob, {access: 'AllowList', mintMode: true})) + .to.be.rejectedWith(/common\.NoPermission/); + }); +}); --- a/js-packages/tests/src/addCollectionAdmin.test.ts +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from './util/index.js'; -import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js'; - -describe('Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => { - let donor: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itSub('Add collection admin.', async ({helper}) => { - const [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - - const collection = await helper.collection.getData(collectionId); - expect(collection!.normalizedOwner!).to.be.equal(helper.address.normalizeSubstrate(alice.address)); - - await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address}); - - const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); - }); -}); - -describe('Negative Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => { - let donor: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itSub("Not owner can't add collection admin.", async ({helper}) => { - const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - - const collection = await helper.collection.getData(collectionId); - expect(collection?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address)); - - await expect(helper.collection.addAdmin(bob, collectionId, {Substrate: charlie.address})).to.be.rejectedWith(/common\.NoPermission/); - await expect(helper.collection.addAdmin(bob, collectionId, {Substrate: bob.address})).to.be.rejectedWith(/common\.NoPermission/); - - const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId); - expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: charlie.address}); - expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: bob.address}); - }); - - itSub("Admin can't add collection admin.", async ({helper}) => { - const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - - await collection.addAdmin(alice, {Substrate: bob.address}); - - const adminListAfterAddAdmin = await collection.getAdmins(); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); - - await expect(collection.addAdmin(bob, {Substrate: charlie.address})).to.be.rejectedWith(/common\.NoPermission/); - - const adminListAfterAddNewAdmin = await collection.getAdmins(); - expect(adminListAfterAddNewAdmin).to.be.deep.contains({Substrate: bob.address}); - expect(adminListAfterAddNewAdmin).to.be.not.deep.contains({Substrate: charlie.address}); - }); - - itSub("Can't add collection admin of not existing collection.", async ({helper}) => { - const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - const collectionId = NON_EXISTENT_COLLECTION_ID; - - await expect(helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address})).to.be.rejectedWith(/common\.CollectionNotFound/); - - // Verifying that nothing bad happened (network is live, new collections can be created, etc.) - await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - }); - - itSub("Can't add an admin to a destroyed collection.", async ({helper}) => { - const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - - await collection.burn(alice); - await expect(collection.addAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.CollectionNotFound/); - - // Verifying that nothing bad happened (network is live, new collections can be created, etc.) - await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - }); - - itSub('Add an admin to a collection that has reached the maximum number of admins limit', async ({helper}) => { - const [alice, ...accounts] = await helper.arrange.createAccounts([10n, 0n, 0n, 0n, 0n, 0n, 0n, 0n], donor); - const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - - const chainAdminLimit = (helper.getApi().consts.common.collectionAdminsLimit as any).toNumber(); - expect(chainAdminLimit).to.be.equal(5); - - for(let i = 0; i < chainAdminLimit; i++) { - await collection.addAdmin(alice, {Substrate: accounts[i].address}); - const adminListAfterAddAdmin = await collection.getAdmins(); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: accounts[i].address}); - } - - await expect(collection.addAdmin(alice, {Substrate: accounts[chainAdminLimit].address})).to.be.rejectedWith(/common\.CollectionAdminCountExceeded/); - }); -}); --- a/js-packages/tests/src/adminTransferAndBurn.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, itSub} from './util/index.js'; - -describe('Integration Test: ownerCanTransfer allows admins to use only transferFrom/burnFrom:', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - }); - }); - - itSub('admin transfers other user\'s token', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}); - await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); - const limits = await helper.collection.getEffectiveLimits(collectionId); - expect(limits.ownerCanTransfer).to.be.true; - - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); - await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})).to.be.rejected; - - await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address}); - const newTokenOwner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(newTokenOwner).to.be.deep.equal({Substrate: charlie.address}); - }); - - itSub('admin burns other user\'s token', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}); - - await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); - const limits = await helper.collection.getEffectiveLimits(collectionId); - expect(limits.ownerCanTransfer).to.be.true; - - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); - - await expect(helper.nft.burnToken(alice, collectionId, tokenId)).to.be.rejected; - - await helper.nft.burnToken(bob, collectionId, tokenId); - const token = await helper.nft.getToken(collectionId, tokenId); - expect(token).to.be.null; - }); -}); --- a/js-packages/tests/src/allowLists.test.ts +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, itSub} from './util/index.js'; -import type {ICollectionPermissions} from '@unique/playgrounds/src/types.js'; -import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js'; - -describe('Integration Test ext. Allow list tests', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor); - }); - }); - - describe('Positive', () => { - itSub('Owner can add address to allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - // allow list does not need to be enabled to add someone in advance - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - const allowList = await helper.nft.getAllowList(collectionId); - expect(allowList).to.deep.contain({Substrate: bob.address}); - }); - - itSub('Admin can add address to allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address}); - - // allow list does not need to be enabled to add someone in advance - await helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address}); - const allowList = await helper.nft.getAllowList(collectionId); - expect(allowList).to.deep.contain({Substrate: charlie.address}); - }); - - itSub('If address is already added to allow list, nothing happens', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - const allowList = await helper.nft.getAllowList(collectionId); - expect(allowList).to.deep.contain({Substrate: bob.address}); - }); - }); - - describe('Negative', () => { - itSub('Nobody can add address to allow list of non-existing collection', async ({helper}) => { - const collectionId = NON_EXISTENT_COLLECTION_ID; - await expect(helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('Nobody can add address to allow list of destroyed collection', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.collection.burn(alice, collectionId); - await expect(helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('Non-privileged user cannot add address to allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await expect(helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address})) - .to.be.rejectedWith(/common\.NoPermission/); - }); - }); -}); - -describe('Integration Test ext. Remove from Allow List', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor); - }); - }); - - describe('Positive', () => { - itSub('Owner can remove address from allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - - await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); - - const allowList = await helper.nft.getAllowList(collectionId); - - expect(allowList).to.not.deep.contain({Substrate: bob.address}); - }); - - itSub('Admin can remove address from allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addAdmin(alice, collectionId, {Substrate: charlie.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - await helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: bob.address}); - - const allowList = await helper.nft.getAllowList(collectionId); - expect(allowList).to.not.deep.contain({Substrate: bob.address}); - }); - - itSub('If address is already removed from allow list, nothing happens', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); - const allowListBefore = await helper.nft.getAllowList(collectionId); - expect(allowListBefore).to.not.deep.contain({Substrate: bob.address}); - - await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}); - - const allowListAfter = await helper.nft.getAllowList(collectionId); - expect(allowListAfter).to.not.deep.contain({Substrate: bob.address}); - }); - }); - - describe('Negative', () => { - itSub('Non-privileged user cannot remove address from allow list', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - await expect(helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: charlie.address})) - .to.be.rejectedWith(/common\.NoPermission/); - - const allowList = await helper.nft.getAllowList(collectionId); - expect(allowList).to.deep.contain({Substrate: charlie.address}); - }); - - itSub('Nobody can remove address from allow list of non-existing collection', async ({helper}) => { - const collectionId = NON_EXISTENT_COLLECTION_ID; - await expect(helper.collection.removeFromAllowList(bob, collectionId, {Substrate: charlie.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('Nobody can remove address from allow list of deleted collection', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}); - await helper.collection.burn(alice, collectionId); - - await expect(helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - }); -}); - -describe('Integration Test ext. Transfer if included in Allow List', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor); - }); - }); - - describe('Positive', () => { - itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transfer.', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner).to.be.deep.equal({Substrate: charlie.address}); - }); - - itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transferFrom.', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - - await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner).to.be.deep.equal({Substrate: charlie.address}); - }); - - itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - - await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner).to.be.deep.equal({Substrate: charlie.address}); - }); - - itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transferFrom', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - - await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner).to.be.deep.equal({Substrate: charlie.address}); - }); - }); - - describe('Negative', () => { - itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred from a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - - await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) - .to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); - - itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred from a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address}); - - await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) - .to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); - - itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred to a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - - await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) - .to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); - - itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred to a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address}); - await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address}); - - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address}); - - await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})) - .to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); - - itSub('If Public Access mode is set to AllowList, tokens can\'t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await expect(helper.nft.burnToken(bob, collectionId, tokenId)) - .to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('If Public Access mode is set to AllowList, token transfers can\'t be Approved by a non-allowlisted address (see Approve method)', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'}); - await expect(helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.AddressNotInAllowlist/); - }); - }); -}); - -describe('Integration Test ext. Mint if included in Allow List', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - const permissionSet: ICollectionPermissions[] = [ - {access: 'Normal', mintMode: false}, - {access: 'Normal', mintMode: true}, - {access: 'AllowList', mintMode: false}, - {access: 'AllowList', mintMode: true}, - ]; - - const testPermissionSuite = (permissions: ICollectionPermissions) => { - const allowlistedMintingShouldFail = !permissions.mintMode!; - - const appropriateRejectionMessage = permissions.mintMode! ? /common\.AddressNotInAllowlist/ : /common\.PublicMintingNotAllowed/; - - const allowlistedMintingTest = () => itSub( - `With the condtions above, tokens can${allowlistedMintingShouldFail ? '\'t' : ''} be created by allow-listed addresses`, - async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {}); - await collection.setPermissions(alice, permissions); - await collection.addToAllowList(alice, {Substrate: bob.address}); - - if(allowlistedMintingShouldFail) - await expect(collection.mintToken(bob, {Substrate: bob.address})).to.be.rejectedWith(appropriateRejectionMessage); - else - await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected; - }, - ); - - - describe(`Public Access Mode = ${permissions.access}, Mint Mode = ${permissions.mintMode}`, () => { - describe('Positive', () => { - itSub('With the condtions above, tokens can be created by owner', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {}); - await collection.setPermissions(alice, permissions); - await expect(collection.mintToken(alice, {Substrate: alice.address})).to.not.be.rejected; - }); - - itSub('With the condtions above, tokens can be created by admin', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {}); - await collection.setPermissions(alice, permissions); - await collection.addAdmin(alice, {Substrate: bob.address}); - await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected; - }); - - if(!allowlistedMintingShouldFail) allowlistedMintingTest(); - }); - - describe('Negative', () => { - itSub('With the condtions above, tokens can\'t be created by non-priviliged non-allow-listed address', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.setPermissions(alice, permissions); - await expect(collection.mintToken(bob, {Substrate: bob.address})) - .to.be.rejectedWith(appropriateRejectionMessage); - }); - - if(allowlistedMintingShouldFail) allowlistedMintingTest(); - }); - }); - }; - - for(const permissions of permissionSet) { - testPermissionSuite(permissions); - } -}); --- a/js-packages/tests/src/apiConsts.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {ApiPromise} from '@polkadot/api'; -import {usingPlaygrounds, itSub, expect, COLLECTION_HELPER, CONTRACT_HELPER} from './util/index.js'; - - -const MAX_COLLECTION_DESCRIPTION_LENGTH = 256n; -const MAX_COLLECTION_NAME_LENGTH = 64n; -const COLLECTION_ADMINS_LIMIT = 5n; -const MAX_COLLECTION_PROPERTIES_SIZE = 40960n; -const MAX_TOKEN_PREFIX_LENGTH = 16n; -const MAX_PROPERTY_KEY_LENGTH = 256n; -const MAX_PROPERTY_VALUE_LENGTH = 32768n; -const MAX_PROPERTIES_PER_ITEM = 64n; -const MAX_TOKEN_PROPERTIES_SIZE = 32768n; -const NESTING_BUDGET = 5n; - -const DEFAULT_COLLETCTION_LIMIT = { - accountTokenOwnershipLimit: '1,000,000', - sponsoredDataSize: '2,048', - sponsoredDataRateLimit: 'SponsoringDisabled', - tokenLimit: '4,294,967,295', - sponsorTransferTimeout: '5', - sponsorApproveTimeout: '5', - ownerCanTransfer: false, - ownerCanDestroy: true, - transfersEnabled: true, -}; - -describe('integration test: API UNIQUE consts', () => { - let api: ApiPromise; - - before(async () => { - await usingPlaygrounds(async (helper) => { - api = await helper.getApi(); - }); - }); - - itSub('DEFAULT_NFT_COLLECTION_LIMITS', () => { - expect(api.consts.unique.nftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT); - }); - - itSub('DEFAULT_RFT_COLLECTION_LIMITS', () => { - expect(api.consts.unique.rftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT); - }); - - itSub('DEFAULT_FT_COLLECTION_LIMITS', () => { - expect(api.consts.unique.ftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT); - }); - - itSub('MAX_COLLECTION_NAME_LENGTH', () => { - checkConst(api.consts.unique.maxCollectionNameLength, MAX_COLLECTION_NAME_LENGTH); - }); - - itSub('MAX_COLLECTION_DESCRIPTION_LENGTH', () => { - checkConst(api.consts.unique.maxCollectionDescriptionLength, MAX_COLLECTION_DESCRIPTION_LENGTH); - }); - - itSub('MAX_COLLECTION_PROPERTIES_SIZE', () => { - checkConst(api.consts.unique.maxCollectionPropertiesSize, MAX_COLLECTION_PROPERTIES_SIZE); - }); - - itSub('MAX_TOKEN_PREFIX_LENGTH', () => { - checkConst(api.consts.unique.maxTokenPrefixLength, MAX_TOKEN_PREFIX_LENGTH); - }); - - itSub('MAX_PROPERTY_KEY_LENGTH', () => { - checkConst(api.consts.unique.maxPropertyKeyLength, MAX_PROPERTY_KEY_LENGTH); - }); - - itSub('MAX_PROPERTY_VALUE_LENGTH', () => { - checkConst(api.consts.unique.maxPropertyValueLength, MAX_PROPERTY_VALUE_LENGTH); - }); - - itSub('MAX_PROPERTIES_PER_ITEM', () => { - checkConst(api.consts.unique.maxPropertiesPerItem, MAX_PROPERTIES_PER_ITEM); - }); - - itSub('NESTING_BUDGET', () => { - checkConst(api.consts.unique.nestingBudget, NESTING_BUDGET); - }); - - itSub('MAX_TOKEN_PROPERTIES_SIZE', () => { - checkConst(api.consts.unique.maxTokenPropertiesSize, MAX_TOKEN_PROPERTIES_SIZE); - }); - - itSub('COLLECTION_ADMINS_LIMIT', () => { - checkConst(api.consts.unique.collectionAdminsLimit, COLLECTION_ADMINS_LIMIT); - }); - - itSub('HELPERS_CONTRACT_ADDRESS', () => { - expect(api.consts.evmContractHelpers.contractAddress.toString().toLowerCase()).to.be.equal(CONTRACT_HELPER.toLowerCase()); - }); - - itSub('EVM_COLLECTION_HELPERS_ADDRESS', () => { - expect(api.consts.common.contractAddress.toString().toLowerCase()).to.be.equal(COLLECTION_HELPER.toLowerCase()); - }); -}); - -function checkConst(constValue: any, expectedValue: T) { - expect(constValue.toBigInt()).equal(expectedValue); -} \ No newline at end of file --- a/js-packages/tests/src/approve.test.ts +++ /dev/null @@ -1,658 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, Pallets, usingPlaygrounds} from './util/index.js'; -import {CrossAccountId} from '@unique/playgrounds/src/unique.js'; - - - -[ - {method: 'approveToken', account: (account: IKeyringPair) => CrossAccountId.fromKeyring(account).toICrossAccountId()}, - {method: 'approveTokenFromEth', account: (account: IKeyringPair) => CrossAccountId.fromKeyring(account).toEthereum().toICrossAccountId()}, -].map(testCase => { - describe(`Integration Test ${testCase.method}(spender, collection_id, item_id, amount):`, () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('[nft] Execute the extrinsic and check approvedList', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)}); - await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; - }); - - itSub('[fungible] Execute the extrinsic and check approvedList', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice)); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); - const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); - expect(amount).to.be.equal(1n); - }); - - itSub.ifWithPallets('[refungible] Execute the extrinsic and check approvedList', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n}); - await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); - const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); - expect(amount).to.be.equal(1n); - }); - - itSub('[nft] Remove approval by using 0 amount', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const collectionId = collection.collectionId; - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)}); - await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; - await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false; - }); - - itSub('[fungible] Remove approval by using 0 amount', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice)); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); - const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); - expect(amountBefore).to.be.equal(1n); - - await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); - const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); - expect(amountAfter).to.be.equal(0n); - }); - - itSub.ifWithPallets('[refungible] Remove approval by using 0 amount', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n}); - await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); - const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); - expect(amountBefore).to.be.equal(1n); - - await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); - const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice)); - expect(amountAfter).to.be.equal(0n); - }); - - itSub('can`t be called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); - const result = (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: charlie.address}); - await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned'); - }); - }); - - describe(`[${testCase.method}] Normal user can approve other users to transfer:`, () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('NFT', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); - await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.true; - }); - - itSub('Fungible up to an approved amount', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob)); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); - const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, testCase.account(bob)); - expect(amount).to.be.equal(1n); - }); - - itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n}); - await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 100n); - const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, testCase.account(bob)); - expect(amount).to.be.equal(100n); - }); - }); - - describe(`[${testCase.method}] Approved users can transferFrom up to approved amount:`, () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('NFT', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); - await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); - await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner).to.be.deep.equal({Substrate: alice.address}); - }); - - itSub('Fungible up to an approved amount', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob)); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); - const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n); - const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - expect(after - before).to.be.equal(1n); - }); - - itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n}); - await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); - const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n); - const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - expect(after - before).to.be.equal(1n); - }); - }); - - describe(`[${testCase.method}] Approved users cannot use transferFrom to repeat transfers if approved amount was already transferred:`, () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('NFT', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); - await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); - await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner).to.be.deep.equal({Substrate: alice.address}); - const transferTokenFromTx = () => helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}); - await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow'); - }); - - itSub('Fungible up to an approved amount', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob)); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); - const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n); - const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - expect(after - before).to.be.equal(1n); - - const transferTokenFromTx = () => helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n); - await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow'); - }); - - itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n}); - await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 100n); - const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 100n); - const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - expect(after - before).to.be.equal(100n); - const transferTokenFromTx = () => helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 100n); - await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow'); - }); - }); - - describe(`[${testCase.method}] Approved amount decreases by the transferred amount:`, () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - let dave: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor); - }); - }); - - itSub('If a user B is approved to transfer 10 Fungible tokens from user A, they can transfer 2 tokens to user C, which will result in decreasing approval from 10 to 8. Then user B can transfer 8 tokens to user D.', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice)); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 10n); - - const charlieBefore = await helper.ft.getBalance(collectionId, {Substrate: charlie.address}); - await helper.ft.transferTokenFrom(bob, collectionId, tokenId, testCase.account(alice), {Substrate: charlie.address}, 2n); - const charlieAfter = await helper.ft.getBalance(collectionId, {Substrate: charlie.address}); - expect(charlieAfter - charlieBefore).to.be.equal(2n); - - const daveBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); - await helper.ft.transferTokenFrom(bob, collectionId, tokenId, testCase.account(alice), {Substrate: dave.address}, 8n); - const daveAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); - expect(daveAfter - daveBefore).to.be.equal(8n); - }); - }); - - describe(`[${testCase.method}] User may clear the approvals to approving for 0 amount:`, () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('NFT', async ({helper}) => { - const owner = testCase.account(alice); - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: owner}); - - await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; - - await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false; - - const transferTokenFromTx = helper.nft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: bob.address}); - await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow'); - }); - - itSub('Fungible', async ({helper}) => { - const owner = testCase.account(alice); - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, 10n, owner); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); - const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner); - expect(amountBefore).to.be.equal(1n); - - await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); - const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner); - expect(amountAfter).to.be.equal(0n); - - const transferTokenFromTx = helper.ft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: charlie.address}, 1n); - await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow'); - }); - - itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => { - const owner = testCase.account(alice); - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner, pieces: 100n}); - await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}); - const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner); - expect(amountBefore).to.be.equal(1n); - - await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n); - const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner); - expect(amountAfter).to.be.equal(0n); - - const transferTokenFromTx = helper.rft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: charlie.address}, 1n); - await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow'); - }); - }); - - describe(`[${testCase.method}] User cannot approve for the amount greater than they own:`, () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('1 for NFT', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); - const result = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 2n); - await expect(result).to.be.rejectedWith('nonfungible.NonfungibleItemsHaveNoAmount'); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.false; - }); - - itSub('Fungible', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice)); - const tokenId = await helper.ft.getLastTokenId(collectionId); - const result = (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 11n); - await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned'); - }); - - itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n}); - const result = (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 101n); - await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned'); - }); - }); - - describe(`[${testCase.method}] Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:`, () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('cannot be called by collection admin on non-owned item', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)}); - await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); - const approveTx = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}); - await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); - }); - }); - - describe(`[${testCase.method}] Negative Integration Test approve(spender, collection_id, item_id, amount):`, () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - const NONEXISTENT_COLLECTION = (2 ** 32) - 1; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('[nft] Approve for a collection that does not exist', async ({helper}) => { - const approveTx = (helper.nft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address}); - await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); - }); - - itSub('[fungible] Approve for a collection that does not exist', async ({helper}) => { - const approveTx = (helper.ft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address}); - await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); - }); - - itSub.ifWithPallets('[refungible] Approve for a collection that does not exist', [Pallets.ReFungible], async ({helper}) => { - const approveTx = (helper.rft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address}); - await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); - }); - - itSub('[nft] Approve for a collection that was destroyed', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.burn(alice, collectionId); - const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address}); - await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); - }); - - itSub('[fungible] Approve for a collection that was destroyed', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.ft.burn(alice, collectionId); - const approveTx = (helper.ft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address}); - await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); - }); - - itSub.ifWithPallets('[refungible] Approve for a collection that was destroyed', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.rft.burn(alice, collectionId); - const approveTx = (helper.rft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address}); - await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound'); - }); - - itSub('[nft] Approve transfer of a token that does not exist', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, 2, {Substrate: bob.address}); - await expect(approveTx).to.be.rejectedWith('common.TokenNotFound'); - }); - - itSub.ifWithPallets('[refungible] Approve transfer of a token that does not exist', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const approveTx = (helper.rft as any)[testCase.method](alice, collectionId, 2, {Substrate: bob.address}); - await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); // TODO: why the error is not common.TokenNotFound - }); - - itSub('[nft] Approve using the address that does not own the approved token', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)}); - const approveTx = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}); - await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); - }); - - itSub('[fungible] Approve using the address that does not own the approved token', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice)); - const tokenId = await helper.ft.getLastTokenId(collectionId); - const approveTx = (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}); - await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); - }); - - itSub.ifWithPallets('[refungible] Approve using the address that does not own the approved token', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n}); - const approveTx = (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}); - await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); - }); - - itSub.ifWithPallets('should fail if approved more ReFungibles than owned', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); - await helper.rft.transferToken(alice, collectionId, tokenId, testCase.account(bob), 100n); - await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 100n); - - const approveTx = (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 101n); - await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); - }); - - itSub('should fail if approved more Fungibles than owned', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.ft.mintTokens(alice, collectionId, 10n, alice.address); - const tokenId = await helper.ft.getLastTokenId(collectionId); - - await helper.ft.transferToken(alice, collectionId, tokenId, testCase.account(bob), 10n); - await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 10n); - const approveTx = (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 11n); - await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); - }); - - itSub('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)}); - await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: false}); - - const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: charlie.address}); - await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); - }); - }); -}); - -describe('Normal user can approve other users to be wallet operator:', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('[nft] Enable and disable approval', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - - const checkBeforeApproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); - expect(checkBeforeApproval).to.be.false; - - await helper.nft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, true); - const checkAfterApproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); - expect(checkAfterApproval).to.be.true; - - await helper.nft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, false); - const checkAfterDisapproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); - expect(checkAfterDisapproval).to.be.false; - }); - - itSub.ifWithPallets('[rft] Enable and disable approval', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - - const checkBeforeApproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); - expect(checkBeforeApproval).to.be.false; - - await helper.rft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, true); - const checkAfterApproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); - expect(checkAfterApproval).to.be.true; - - await helper.rft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, false); - const checkAfterDisapproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address}); - expect(checkAfterDisapproval).to.be.false; - }); -}); - -describe('Administrator and collection owner do not need approval in order to execute TransferFrom (with owner_can_transfer_flag = true):', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - let dave: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor); - }); - }); - - itSub('NFT', async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: charlie.address}); - - await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}); - const owner1 = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner1).to.be.deep.equal({Substrate: dave.address}); - - await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); - await helper.nft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}); - const owner2 = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner2).to.be.deep.equal({Substrate: alice.address}); - }); - - itSub('Fungible up to an approved amount', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); - await helper.ft.mintTokens(alice, collectionId, 10n, charlie.address); - const tokenId = await helper.ft.getLastTokenId(collectionId); - - const daveBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); - await helper.ft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n); - const daveBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); - expect(daveBalanceAfter - daveBalanceBefore).to.be.equal(1n); - - await helper.collection.addAdmin(alice ,collectionId, {Substrate: bob.address}); - - const aliceBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - await helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n); - const aliceBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - expect(aliceBalanceAfter - aliceBalanceBefore).to.be.equal(1n); - }); - - itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: charlie.address, pieces: 100n}); - - const daveBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address}); - await helper.rft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n); - const daveAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address}); - expect(daveAfter - daveBefore).to.be.equal(1n); - - await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); - - const aliceBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - await helper.rft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n); - const aliceAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - expect(aliceAfter - aliceBefore).to.be.equal(1n); - }); -}); - -describe('Repeated approvals add up', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - let dave: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor); - }); - }); - - itSub.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. Fungible', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {}); - await collection.mint(alice, 10n); - await collection.approveTokens(alice, {Substrate: bob.address}, 1n); - await collection.approveTokens(alice, {Substrate: charlie.address}, 1n); - // const allowances1 = await getAllowance(collectionId, 0, Alice.address, Bob.address); - // const allowances2 = await getAllowance(collectionId, 0, Alice.address, Charlie.address); - // expect(allowances1 + allowances2).to.be.eq(2n); - }); - - itSub.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. ReFungible', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {}); - const token = await collection.mintToken(alice, 10n); - await token.approve(alice, {Substrate: bob.address}, 1n); - await token.approve(alice, {Substrate: charlie.address}, 1n); - // const allowances1 = await getAllowance(collectionId, itemId, Alice.address, Bob.address); - // const allowances2 = await getAllowance(collectionId, itemId, Alice.address, Charlie.address); - // expect(allowances1 + allowances2).to.be.eq(2n); - }); - - // Canceled by changing approve logic - itSub.skip('Cannot approve for more than total user\'s amount (owned: 10, approval 1: 5 - should succeed, approval 2: 6 - should fail). Fungible', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {}); - await collection.mint(alice, 10n, {Substrate: dave.address}); - await collection.approveTokens(dave, {Substrate: bob.address}, 5n); - await expect(collection.approveTokens(dave, {Substrate: charlie.address}, 6n)) - .to.be.rejectedWith('this test would fail (since it is skipped), replace this expecting message with what would have been received'); - }); - - // Canceled by changing approve logic - itSub.skip('Cannot approve for more than total user\'s amount (owned: 100, approval 1: 50 - should succeed, approval 2: 51 - should fail). ReFungible', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {}); - const token = await collection.mintToken(alice, 100n, {Substrate: dave.address}); - await token.approve(dave, {Substrate: bob.address}, 50n); - await expect(token.approve(dave, {Substrate: charlie.address}, 51n)) - .to.be.rejectedWith('this test would fail (since it is skipped), replace this expecting message with what would have been received'); - }); -}); --- a/js-packages/tests/src/burnItem.test.ts +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, usingPlaygrounds} from './util/index.js'; - - -describe('integration test: ext. burnItem():', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([100n], donor); - }); - }); - - itSub('Burn item in NFT collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice); - const token = await collection.mintToken(alice); - - await token.burn(alice); - expect(await token.doesExist()).to.be.false; - }); - - itSub('Burn item in Fungible collection', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {}, 10); - await collection.mint(alice, 10n); - - await collection.burnTokens(alice, 1n); - expect(await collection.getBalance({Substrate: alice.address})).to.eq(9n); - }); -}); - -describe('integration test: ext. burnItem() with admin permissions:', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('Burn item in NFT collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice); - await collection.setLimits(alice, {ownerCanTransfer: true}); - await collection.addAdmin(alice, {Substrate: bob.address}); - const token = await collection.mintToken(alice); - - await token.burnFrom(bob, {Substrate: alice.address}); - expect(await token.doesExist()).to.be.false; - }); - - itSub('Burn item in Fungible collection', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {}, 0); - await collection.setLimits(alice, {ownerCanTransfer: true}); - await collection.addAdmin(alice, {Substrate: bob.address}); - await collection.mint(alice, 10n); - - await collection.burnTokensFrom(bob, {Substrate: alice.address}, 1n); - expect(await collection.getBalance({Substrate: alice.address})).to.eq(9n); - }); -}); - -describe('Negative integration test: ext. burnItem():', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('Burn a token that was never created', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice); - await expect(collection.burnToken(alice, 10)).to.be.rejectedWith('common.TokenNotFound'); - }); - - itSub('Burn a token using the address that does not own it', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice); - const token = await collection.mintToken(alice); - - await expect(token.burn(bob)).to.be.rejectedWith('common.NoPermission'); - }); - - itSub('Transfer a burned token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice); - const token = await collection.mintToken(alice); - await token.burn(alice); - - await expect(token.transfer(alice, {Substrate: bob.address})).to.be.rejectedWith('common.TokenNotFound'); - }); - - itSub('Burn more than owned in Fungible collection', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {}, 0); - await collection.mint(alice, 10n); - - await expect(collection.burnTokens(alice, 11n)).to.be.rejectedWith('common.TokenValueTooLow'); - expect(await collection.getBalance({Substrate: alice.address})).to.eq(10n); - }); - - itSub('Zero burn NFT', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'Coll', description: 'Desc', tokenPrefix: 'T'}); - const tokenAlice = await collection.mintToken(alice, {Substrate: alice.address}); - const tokenBob = await collection.mintToken(alice, {Substrate: bob.address}); - - // 1. Zero burn of own tokens allowed: - await helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, tokenAlice.tokenId, 0]); - // 2. Zero burn of non-owned tokens not allowed: - await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, tokenBob.tokenId, 0])).to.be.rejectedWith('common.NoPermission'); - // 3. Zero burn of non-existing tokens not allowed: - await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, 9999, 0])).to.be.rejectedWith('common.TokenNotFound'); - expect(await tokenAlice.doesExist()).to.be.true; - expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: alice.address}); - expect(await tokenBob.getOwner()).to.deep.eq({Substrate: bob.address}); - // 4. Storage is not corrupted: - await tokenAlice.transfer(alice, {Substrate: bob.address}); - await tokenBob.transfer(bob, {Substrate: alice.address}); - expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: bob.address}); - expect(await tokenBob.getOwner()).to.deep.eq({Substrate: alice.address}); - }); - - itSub('zero burnFrom NFT', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'Zero', description: 'Zero transfer', tokenPrefix: 'TF'}); - const notApprovedNft = await collection.mintToken(alice, {Substrate: bob.address}); - const approvedNft = await collection.mintToken(alice, {Substrate: bob.address}); - await approvedNft.approve(bob, {Substrate: alice.address}); - - // 1. Zero burnFrom of non-existing tokens not allowed: - await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, 9999, 0])).to.be.rejectedWith('common.ApprovedValueTooLow'); - // 2. Zero burnFrom of not approved tokens not allowed: - await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, notApprovedNft.tokenId, 0])).to.be.rejectedWith('common.ApprovedValueTooLow'); - // 3. Zero burnFrom of approved tokens allowed: - await helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, approvedNft.tokenId, 0]); - - // 4.1 approvedNft still approved: - expect(await approvedNft.isApproved({Substrate: alice.address})).to.be.true; - // 4.2 bob is still the owner: - expect(await approvedNft.getOwner()).to.deep.eq({Substrate: bob.address}); - expect(await notApprovedNft.getOwner()).to.deep.eq({Substrate: bob.address}); - // 4.3 Alice can burn approved nft: - await approvedNft.burnFrom(alice, {Substrate: bob.address}); - expect(await approvedNft.doesExist()).to.be.false; - }); -}); --- a/js-packages/tests/src/change-collection-owner.test.ts +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, itSub} from './util/index.js'; -import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js'; - -describe('Integration Test changeCollectionOwner(collection_id, new_owner):', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('Changing owner changes owner address', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const beforeChanging = await helper.collection.getData(collection.collectionId); - expect(beforeChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address)); - - await collection.changeOwner(alice, bob.address); - const afterChanging = await helper.collection.getData(collection.collectionId); - expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address)); - }); -}); - -describe('Integration Test changeCollectionOwner(collection_id, new_owner) special checks for exOwner:', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('Changing the owner of the collection is not allowed for the former owner', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - - await collection.changeOwner(alice, bob.address); - - const changeOwnerTx = () => collection.changeOwner(alice, alice.address); - await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/); - - const afterChanging = await helper.collection.getData(collection.collectionId); - expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address)); - }); - - itSub('New collectionOwner has access to sponsorship management operations in the collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.changeOwner(alice, bob.address); - - const afterChanging = await helper.collection.getData(collection.collectionId); - expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address)); - - await collection.setSponsor(bob, charlie.address); - await collection.confirmSponsorship(charlie); - await collection.removeSponsor(bob); - const limits = { - accountTokenOwnershipLimit: 1, - tokenLimit: 1, - sponsorTransferTimeout: 1, - ownerCanDestroy: true, - ownerCanTransfer: true, - }; - - await collection.setLimits(bob, limits); - const gotLimits = await collection.getEffectiveLimits(); - expect(gotLimits).to.be.deep.contains(limits); - - await collection.setPermissions(bob, {access: 'AllowList', mintMode: true}); - - await collection.burn(bob); - const collectionData = await helper.collection.getData(collection.collectionId); - expect(collectionData).to.be.null; - }); - - itSub('New collectionOwner has access to changeCollectionOwner', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.changeOwner(alice, bob.address); - await collection.changeOwner(bob, charlie.address); - const collectionData = await collection.getData(); - expect(collectionData?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(charlie.address)); - }); -}); - -describe('Negative Integration Test changeCollectionOwner(collection_id, new_owner):', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('Not owner can\'t change owner.', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const changeOwnerTx = () => collection.changeOwner(bob, bob.address); - await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Collection admin can\'t change owner.', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.addAdmin(alice, {Substrate: bob.address}); - const changeOwnerTx = () => collection.changeOwner(bob, bob.address); - await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Can\'t change owner of a non-existing collection.', async ({helper}) => { - const collectionId = NON_EXISTENT_COLLECTION_ID; - const changeOwnerTx = () => helper.collection.changeOwner(bob, collectionId, bob.address); - await expect(changeOwnerTx()).to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('Former collectionOwner not allowed to sponsorship management operations in the collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.changeOwner(alice, bob.address); - - const changeOwnerTx = () => collection.changeOwner(alice, alice.address); - await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/); - - const afterChanging = await helper.collection.getData(collection.collectionId); - expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address)); - - const setSponsorTx = () => collection.setSponsor(alice, charlie.address); - const confirmSponsorshipTx = () => collection.confirmSponsorship(alice); - const removeSponsorTx = () => collection.removeSponsor(alice); - await expect(setSponsorTx()).to.be.rejectedWith(/common\.NoPermission/); - await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); - await expect(removeSponsorTx()).to.be.rejectedWith(/common\.NoPermission/); - - const limits = { - accountTokenOwnershipLimit: 1, - tokenLimit: 1, - sponsorTransferTimeout: 1, - ownerCanDestroy: true, - ownerCanTransfer: true, - }; - - const setLimitsTx = () => collection.setLimits(alice, limits); - await expect(setLimitsTx()).to.be.rejectedWith(/common\.NoPermission/); - - const setPermissionTx = () => collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await expect(setPermissionTx()).to.be.rejectedWith(/common\.NoPermission/); - - const burnTx = () => collection.burn(alice); - await expect(burnTx()).to.be.rejectedWith(/common\.NoPermission/); - }); -}); --- a/js-packages/tests/src/check-event/burnItemEvent.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, itSub} from '../util/index.js'; -import type {IEvent} from '@unique/playgrounds/src/types.js'; - - -describe('Burn Item event ', () => { - let alice: IKeyringPair; - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - itSub('Check event from burnItem(): ', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}); - await token.burn(alice); - await helper.wait.newBlocks(1); - - const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; - const eventStrings = event.map(e => `${e.section}.${e.method}`); - - expect(eventStrings).to.contains('common.ItemDestroyed'); - expect(eventStrings).to.contains('treasury.Deposit'); - expect(eventStrings).to.contains('system.ExtrinsicSuccess'); - }); -}); --- a/js-packages/tests/src/check-event/createCollectionEvent.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, itSub, expect} from '../util/index.js'; -import type {IEvent} from '@unique/playgrounds/src/types.js'; - -describe('Create collection event ', () => { - let alice: IKeyringPair; - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - itSub('Check event from createCollection(): ', async ({helper}) => { - await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - await helper.wait.newBlocks(1); - const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; - const eventStrings = event.map(e => `${e.section}.${e.method}`); - - expect(eventStrings).to.contains('common.CollectionCreated'); - expect(eventStrings).to.contains('treasury.Deposit'); - expect(eventStrings).to.contains('system.ExtrinsicSuccess'); - }); -}); --- a/js-packages/tests/src/check-event/createItemEvent.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from '../util/index.js'; -import type {IEvent} from '@unique/playgrounds/src/types.js'; - -describe('Create Item event ', () => { - let alice: IKeyringPair; - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - itSub('Check event from createItem(): ', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - await collection.mintToken(alice, {Substrate: alice.address}); - await helper.wait.newBlocks(1); - const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; - const eventStrings = event.map(e => `${e.section}.${e.method}`); - - expect(eventStrings).to.contains('common.ItemCreated'); - expect(eventStrings).to.contains('treasury.Deposit'); - expect(eventStrings).to.contains('system.ExtrinsicSuccess'); - }); -}); --- a/js-packages/tests/src/check-event/createMultipleItemsEvent.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, itSub, expect} from '../util/index.js'; -import type {IEvent} from '@unique/playgrounds/src/types.js'; - -describe('Create Multiple Items Event event ', () => { - let alice: IKeyringPair; - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - itSub('Check event from createMultipleItems(): ', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - await collection.mintMultipleTokens(alice, [ - {owner: {Substrate: alice.address}}, - {owner: {Substrate: alice.address}}, - ]); - - await helper.wait.newBlocks(1); - const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; - const eventStrings = event.map(e => `${e.section}.${e.method}`); - - expect(eventStrings).to.contains('common.ItemCreated'); - expect(eventStrings).to.contains('treasury.Deposit'); - expect(eventStrings).to.contains('system.ExtrinsicSuccess'); - }); -}); --- a/js-packages/tests/src/check-event/destroyCollectionEvent.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from '../util/index.js'; -import type {IEvent} from '@unique/playgrounds/src/types.js'; - -describe('Destroy collection event ', () => { - let alice: IKeyringPair; - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - - itSub('Check event from destroyCollection(): ', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - await collection.burn(alice); - await helper.wait.newBlocks(1); - const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; - const eventStrings = event.map(e => `${e.section}.${e.method}`); - - expect(eventStrings).to.contains('common.CollectionDestroyed'); - expect(eventStrings).to.contains('treasury.Deposit'); - expect(eventStrings).to.contains('system.ExtrinsicSuccess'); - }); -}); --- a/js-packages/tests/src/check-event/transferEvent.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, itSub} from '../util/index.js'; -import type {IEvent} from '@unique/playgrounds/src/types.js'; - -describe('Transfer event ', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - }); - }); - - itSub('Check event from transfer(): ', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}); - await token.transfer(alice, {Substrate: bob.address}); - await helper.wait.newBlocks(1); - const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; - const eventStrings = event.map(e => `${e.section}.${e.method}`); - - expect(eventStrings).to.contains('common.Transfer'); - expect(eventStrings).to.contains('treasury.Deposit'); - expect(eventStrings).to.contains('system.ExtrinsicSuccess'); - }); -}); --- a/js-packages/tests/src/check-event/transferFromEvent.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, itSub} from '../util/index.js'; -import type {IEvent} from '@unique/playgrounds/src/types.js'; - -describe('Transfer event ', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - }); - }); - - itSub('Check event from transfer(): ', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}); - await token.transferFrom(alice, {Substrate: alice.address}, {Substrate: bob.address}); - await helper.wait.newBlocks(1); - const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; - const eventStrings = event.map(e => `${e.section}.${e.method}`); - - expect(eventStrings).to.contains('common.Transfer'); - expect(eventStrings).to.contains('treasury.Deposit'); - expect(eventStrings).to.contains('system.ExtrinsicSuccess'); - }); -}); --- a/js-packages/tests/src/collator-selection/collatorSelection.seqtest.ts +++ /dev/null @@ -1,423 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util/index.js'; - -async function nodeAddress(name: string) { - // eslint-disable-next-line require-await - return await usingPlaygrounds(async (helper) => { - const envNodeStash = `RELAY_UNIQUE_NODE_${name.toUpperCase()}_STASH`; - - const nodeStash = process.env[envNodeStash]; - if(nodeStash) { - return helper.address.normalizeSubstrateToChainFormat(nodeStash); - } else { - throw Error(`"${envNodeStash}" env var is not set`); - } - }); -} - -async function getInitialInvulnerables() { - return await Promise.all([ - nodeAddress('alpha'), - nodeAddress('beta'), - nodeAddress('gamma'), - nodeAddress('delta'), - ]); -} - -async function resetInvulnerables() { - await usingPlaygrounds(async (helper, privateKey) => { - const superuser = await privateKey('//Alice'); - const initialInvulnerables = await getInitialInvulnerables(); - - const invulnerables = await helper.collatorSelection.getInvulnerables(); - - // Remove all invulnerables but the first one - const firstInvulnerable = invulnerables[0]; - - let nonce = await helper.chain.getNonce(superuser.address); - await Promise.all(invulnerables.slice(1).map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++}))); - - // Add the initial invulnerables - await Promise.all(initialInvulnerables.map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [invulnerable], true, {nonce: nonce++}))); - - // Remove the first invulnerable if it's not an initial one - if(!initialInvulnerables.includes(firstInvulnerable)) { - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [firstInvulnerable]); - } - }); -} - -// todo:collator Most preferable to launch this test in parallel somehow -- or change the session period (1 hr). -describe('Integration Test: Collator Selection', () => { - let superuser: IKeyringPair; - let previousLicenseBond = 0n; - let licenseBond = 0n; - - before(async function() { - if(!process.env.RUN_COLLATOR_TESTS) this.skip(); - - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.CollatorSelection]); - superuser = await privateKey('//Alice'); - - previousLicenseBond = await helper.collatorSelection.getLicenseBond(); - licenseBond = 10n * helper.balance.getOneTokenNominal(); - await helper.getSudo().collatorSelection.setLicenseBond(superuser, licenseBond); - }); - }); - - describe('Dynamic shuffling of collators', () => { - // These two are the default invulnerables, and should return to be invulnerables after this suite. - let alphaNode: string; - let betaNode: string; - - let gammaNode: string; - let deltaNode: string; - - before(async function() { - await usingPlaygrounds(async (helper) => { - // todo:collator see again if blocks start to be finalized in dev mode - // Skip the collator block production in dev mode, since the blocks are sealed automatically. - if(await helper.arrange.isDevNode()) this.skip(); - - [alphaNode, betaNode, gammaNode, deltaNode] = await getInitialInvulnerables(); - - const invulnerables = await helper.collatorSelection.getInvulnerables(); - expect(invulnerables.length, 'Invalid initial invulnerables number').to.be.equal(4); - expect(invulnerables, 'Invalid initial invulnerables').containSubset([alphaNode, betaNode, gammaNode, deltaNode]); - }); - }); - - itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => { - let nonce = await helper.chain.getNonce(superuser.address); - - nonce = await helper.chain.getNonce(superuser.address); - await expect(Promise.all([ - helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alphaNode], true, {nonce: nonce++}), - helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [betaNode], true, {nonce: nonce++}), - ])).to.be.fulfilled; - - const newInvulnerables = await helper.collatorSelection.getInvulnerables(); - expect(newInvulnerables).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2); - - await helper.wait.newSessions(2); - - const newValidators = await helper.callRpc('api.query.session.validators'); - expect(newValidators).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2); - - const lastBlockNumber = await helper.chain.getLatestBlockNumber(); - await helper.wait.newBlocks(1); - const lastGammaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [gammaNode])).toNumber(); - const lastDeltaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [deltaNode])).toNumber(); - expect(lastGammaBlock >= lastBlockNumber || lastDeltaBlock >= lastBlockNumber).to.be.true; - }); - - after(async () => { - await resetInvulnerables(); - }); - }); - - describe('Getting and releasing licenses to collate', () => { - let crowd: IKeyringPair[]; - - before(async function() { - await usingPlaygrounds(async (helper) => { - crowd = await helper.arrange.createCrowd(20, 100n, superuser); - - // set session keys for everyone - await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc))); - }); - }); - - describe('Positive', () => { - itSub('Can lease and release a license', async ({helper}) => { - const account = crowd.pop()!; - - // make sure it does not have any reserved funds - expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(0n); - - // getting a license reserves a license bond cost - await helper.collatorSelection.obtainLicense(account); - expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond); - expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(licenseBond); - - // releasing a license un-reserves the license bond cost - await helper.collatorSelection.releaseLicense(account); - expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n); - - const balance = await helper.balance.getSubstrateFull(account.address); - expect(balance.reserved).to.be.equal(0n); - expect(balance.free > 100n - licenseBond); - }); - - itSub('Can force revoke a license', async ({helper}) => { - const account = crowd.pop()!; - - // getting a license reserves a license bond cost - const previousBalance = await helper.balance.getSubstrateFull(account.address); - await helper.collatorSelection.obtainLicense(account); - expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond); - - // force-releasing a license un-reserves the license bond cost as well - await helper.getSudo().collatorSelection.forceReleaseLicense(superuser, account.address); - expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(previousBalance.reserved); - - const balance = await helper.balance.getSubstrateFull(account.address); - expect(balance.reserved).to.be.equal(previousBalance.reserved); - expect(balance.free > previousBalance.free - licenseBond); - }); - }); - - describe('Negative', () => { - itSub('Cannot get a license without session keys set', async ({helper}) => { - const [account] = await helper.arrange.createAccounts([100n], superuser); - await expect(helper.collatorSelection.obtainLicense(account)) - .to.be.rejectedWith(/collatorSelection.ValidatorNotRegistered/); - }); - - itSub('Cannot register a license twice', async ({helper}) => { - const account = crowd.pop()!; - await helper.collatorSelection.obtainLicense(account); - await expect(helper.collatorSelection.obtainLicense(account)) - .to.be.rejectedWith(/collatorSelection.AlreadyHoldingLicense/); - }); - - itSub('Cannot release a license twice', async ({helper}) => { - const account = crowd.pop()!; - await helper.collatorSelection.obtainLicense(account); - await helper.collatorSelection.releaseLicense(account); - await expect(helper.collatorSelection.releaseLicense(account)) - .to.be.rejectedWith(/collatorSelection.NoLicense/); - }); - - itSub('Cannot force revoke a license as non-sudo', async ({helper}) => { - const account = crowd.pop()!; - await helper.collatorSelection.obtainLicense(account); - await expect(helper.collatorSelection.forceReleaseLicense(superuser, account.address)) - .to.be.rejectedWith(/BadOrigin/); - }); - }); - }); - - describe('Onboarding, collating, and offboarding as collator candidates', () => { - let crowd: IKeyringPair[]; - - before(async function() { - await usingPlaygrounds(async (helper) => { - crowd = await helper.arrange.createCrowd(20, 100n, superuser); - - // set session keys for everyone - await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc))); - }); - }); - - describe('Positive', () => { - itSub('Can onboard and offboard repeatedly', async ({helper}) => { - const account = crowd.pop()!; - await helper.collatorSelection.obtainLicense(account); - await helper.collatorSelection.onboard(account); - expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]); - - await helper.collatorSelection.offboard(account); - expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]); - - await helper.collatorSelection.onboard(account); - expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]); - - await helper.collatorSelection.offboard(account); - expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]); - }); - - itSub('Penalizes and forfeits license from faulty collators', async ({helper}) => { - // This one shouldn't even be able to produce blocks. - const account = crowd.pop()!; - await helper.collatorSelection.obtainLicense(account); - await helper.collatorSelection.onboard(account); - expect(await helper.collatorSelection.getCandidates()).to.contain(account.address); - - // Wait for 3 new sessions before checking that the collator will be kicked: - // one to get collator onboarded, and another two for the collator to fail - await helper.wait.newSessions(3); - - expect(await helper.collatorSelection.getCandidates()).to.not.contain(account.address); - expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n); - - // The account's reserved funds get slashed as a penalty - const balance = await helper.balance.getSubstrateFull(account.address); - expect(balance.reserved).to.be.equal(0n); - expect(balance.free < 100n - licenseBond); - }); - }); - - describe('Negative', () => { - itSub('Cannot onboard without a license', async ({helper}) => { - const account = crowd.pop()!; - await expect(helper.collatorSelection.onboard(account)) - .to.be.rejectedWith(/collatorSelection.NoLicense/); - }); - - itSub('Cannot offboard without a license', async ({helper}) => { - const account = crowd.pop()!; - await expect(helper.collatorSelection.offboard(account)) - .to.be.rejectedWith(/collatorSelection.NotCandidate/); - }); - - itSub('Cannot offboard while not onboarded', async ({helper}) => { - const account = crowd.pop()!; - await helper.collatorSelection.obtainLicense(account); - await expect(helper.collatorSelection.offboard(account)) - .to.be.rejectedWith(/collatorSelection.NotCandidate/); - }); - - itSub('Cannot onboard while already onboarded', async ({helper}) => { - const account = crowd.pop()!; - await helper.collatorSelection.obtainLicense(account); - await helper.collatorSelection.onboard(account); - await expect(helper.collatorSelection.onboard(account)) - .to.be.rejectedWith(/collatorSelection.AlreadyCandidate/); - }); - }); - }); - - describe('Addition and removal of invulnerables', () => { - describe('Positive', () => { - itSub('Adds an invulnerable', async ({helper}) => { - const [account] = await helper.arrange.createAccounts([10n], superuser); - const invulnerables = await helper.collatorSelection.getInvulnerables(); - - await helper.session.setOwnKeysFromAddress(account); - await helper.getSudo().collatorSelection.addInvulnerable(superuser, account.address); - - const newInvulnerables = await helper.collatorSelection.getInvulnerables(); - expect(invulnerables.concat(account.address)).to.have.all.members(newInvulnerables); - }); - - itSub('Removes an invulnerable', async ({helper}) => { - const invulnerables = await helper.collatorSelection.getInvulnerables(); - const lastInvulnerable = invulnerables.pop()!; - - await helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable); - const newInvulnerables = await helper.collatorSelection.getInvulnerables(); - // invulnerables had its last element removed, so they should be equal - expect(newInvulnerables).to.have.all.members(invulnerables); - }); - }); - - describe('Negative', () => { - itSub('Does not duplicate an invulnerable', async ({helper}) => { - const invulnerables = await helper.collatorSelection.getInvulnerables(); - // adding an already invulnerable should not fail, but should not duplicate it either - await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, invulnerables[0])) - .to.be.fulfilled; - const newInvulnerables = await helper.collatorSelection.getInvulnerables(); - expect(newInvulnerables).to.have.all.members(invulnerables); - }); - - itSub('Cannot remove a non-existent invulnerable', async ({helper}) => { - const [account] = await helper.arrange.createAccounts([0n], superuser); - await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, account.address)) - .to.be.rejectedWith(/collatorSelection.NotInvulnerable/); - }); - - itSub('Cannot allow invulnerables to be empty', async ({helper}) => { - const invulnerables = await helper.collatorSelection.getInvulnerables(); - const lastInvulnerable = invulnerables.pop()!; - - let nonce = await helper.chain.getNonce(superuser.address); - await Promise.all(invulnerables.map((i: any) => - helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i], true, {nonce: nonce++}))); - - await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable)) - .to.be.rejectedWith(/collatorSelection.TooFewInvulnerables/); - - const newInvulnerables = await helper.collatorSelection.getInvulnerables(); - expect(newInvulnerables).to.be.deep.equal([lastInvulnerable]); - - // restore the invulnerables to the previous state - nonce = await helper.chain.getNonce(superuser.address); - await Promise.all(invulnerables.map((i: any) => - helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i], true, {nonce: nonce++}))); - }); - - itSub('Cannot have too many invulnerables', async ({helper}) => { - // todo:collator make sure that there is enough session time for a set of tests - // 28 non-functioning collators, teehee. - - const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length; - const invulnerablesUntilLimit = (await helper.collatorSelection.getDesiredCollators()) - invulnerablesLength; - const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser); - const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser); - - await Promise.all(newInvulnerables.map((i: IKeyringPair) => - helper.session.setOwnKeysFromAddress(i))); - await helper.session.setOwnKeysFromAddress(lastInvulnerable); - - let nonce = await helper.chain.getNonce(superuser.address); - await Promise.all(newInvulnerables.map((i: IKeyringPair) => - helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i.address], true, {nonce: nonce++}))); - - await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, lastInvulnerable.address)) - .to.be.rejectedWith(/collatorSelection.TooManyInvulnerables/); - - // restore the invulnerables to the previous state - nonce = await helper.chain.getNonce(superuser.address); - await Promise.all(newInvulnerables.map((i: IKeyringPair) => - helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i.address], true, {nonce: nonce++}))); - }); - - itSub('Forbids a non-sudo to add an invulnerable', async ({helper}) => { - const [account] = await helper.arrange.createAccounts([10n], superuser); - const invulnerables = await helper.collatorSelection.getInvulnerables(); - - await helper.session.setOwnKeysFromAddress(account); - await expect(helper.collatorSelection.addInvulnerable(superuser, account.address)) - .to.be.rejectedWith(/BadOrigin/); - - const newInvulnerables = await helper.collatorSelection.getInvulnerables(); - expect(newInvulnerables).to.be.members(invulnerables); - }); - - itSub('Forbids a non-sudo to remove an invulnerable', async ({helper}) => { - const invulnerables = await helper.collatorSelection.getInvulnerables(); - await expect(helper.collatorSelection.removeInvulnerable(superuser, invulnerables[0])) - .to.be.rejectedWith(/BadOrigin/); - expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables); - }); - - after(async function() { - await resetInvulnerables(); - }); - }); - }); - - after(async function() { - if(!process.env.RUN_COLLATOR_TESTS) return; - - await usingPlaygrounds(async (helper) => { - if(helper.fetchMissingPalletNames([Pallets.CollatorSelection]).length != 0) return; - - await helper.getSudo().collatorSelection.setLicenseBond(superuser, previousLicenseBond); - - const candidates = await helper.collatorSelection.getCandidates(); - let nonce = await helper.chain.getNonce(superuser.address); - await Promise.all(candidates.map(candidate => - helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.forceReleaseLicense', [candidate], true, {nonce: nonce++}))); - }); - }); -}); --- a/js-packages/tests/src/collator-selection/identity.seqtest.ts +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util/index.js'; -import {UniqueHelper} from '@unique/playgrounds/src/unique.js'; - -async function getIdentities(helper: UniqueHelper) { - const identities: [string, any][] = []; - for(const [key, value] of await helper.getApi().query.identity.identityOf.entries()) - identities.push([(key as any).toHuman(), (value as any).unwrap()]); - return identities; -} - -async function getIdentityAccounts(helper: UniqueHelper) { - return (await getIdentities(helper)).flatMap(([key, _value]) => key); -} - -async function getSubIdentityAccounts(helper: UniqueHelper, address: string) { - return ((await helper.getApi().query.identity.subsOf(address)).toHuman() as any)[1]; -} - -async function getSubIdentityName(helper: UniqueHelper, address: string) { - return ((await helper.getApi().query.identity.superOf(address)).toHuman() as any); -} - -describe('Integration Test: Identities Manipulation', () => { - let superuser: IKeyringPair; - - before(async function() { - if(!process.env.RUN_COLLATOR_TESTS) this.skip(); - - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.Identity]); - superuser = await privateKey('//Alice'); - }); - }); - - itSub('Normal calls do not work', async ({helper}) => { - // console.error = () => {}; - await expect(helper.executeExtrinsic(superuser, 'api.tx.identity.setIdentity', [{info: {display: {Raw: 'Meowser'}}}] as any)) - .to.be.rejectedWith(/Transaction call is not expected/); - }); - - describe('Identities', () => { - itSub('Sets identities', async ({helper}) => { - const oldIdentitiesCount = (await getIdentityAccounts(helper)).length; - - const crowdSize = 10; - const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser); - const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]); - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any); - - expect((await getIdentityAccounts(helper)).length).to.be.equal(oldIdentitiesCount + crowdSize); - }); - - itSub('Setting identities does not delete existing but does overwrite', async ({helper}) => { - const crowd = await helper.arrange.createCrowd(10, 0n, superuser); - const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]); - - // insert a single identity - let singleIdentity = identities.pop()!; - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [[singleIdentity]] as any); - - const oldIdentitiesCount = (await getIdentityAccounts(helper)).length; - - // change an identity and push it with a few new others - singleIdentity = [singleIdentity[0], {info: {display: {Raw: 'something special'}}}]; - identities.push(singleIdentity); - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any); - - // oldIdentitiesCount + 9 because one identity is overwritten, not inserted on top - expect((await getIdentityAccounts(helper)).length).to.be.equal(oldIdentitiesCount + 9); - expect((await helper.callRpc('api.query.identity.identityOf', [singleIdentity[0]])).toHuman().info.display) - .to.be.deep.equal({Raw: 'something special'}); - }); - - itSub('Removes identities', async ({helper}) => { - const crowd = await helper.arrange.createCrowd(10, 0n, superuser); - const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]); - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any); - const oldIdentities = await getIdentityAccounts(helper); - - // delete a couple, check that they are no longer there - const scapegoats = [crowd.pop()!.address, crowd.pop()!.address]; - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [scapegoats]); - const newIdentities = await getIdentityAccounts(helper); - expect(newIdentities.concat(scapegoats)).to.be.have.members(oldIdentities); - }); - }); - - describe('Sub-identities', () => { - itSub('Sets subs', async ({helper}) => { - const crowdSize = 18; - const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser); - const supers = [crowd.pop()!, crowd.pop()!, crowd.pop()!]; - - const subsPerSup = crowd.length / supers.length; - let subCount = 0; - const subs = [ - crowd.slice(subCount, subCount += subsPerSup + 1), - crowd.slice(subCount, subCount += subsPerSup), - crowd.slice(subCount, subCount += subsPerSup - 1), - ]; - - const subsInfo = supers.map((acc, i) => [ - acc.address, [ - 1000000n + BigInt(i + 1), - subs[i].map((sub, j) => [ - sub.address, {Raw: `accounter #${j}`}, - ]), - ], - ]); - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo] as any); - - for(let i = 0; i < supers.length; i++) { - // check deposit - expect(((await helper.getApi().query.identity.subsOf(supers[i].address)).toJSON() as any)[0]).to.be.equal(1000001 + i); - - const subsAccounts = await getSubIdentityAccounts(helper, supers[i].address); - // check sub-identities as account ids - expect(subsAccounts).to.include.members(subs[i].map(x => x.address)); - - for(let j = 0; j < subsAccounts.length; j++) { - // check sub-identities' names - expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `accounter #${j}`}); - } - } - }); - - itSub('Setting sub-identities does not delete other existing but does overwrite own', async ({helper}) => { - const crowdSize = 18; - const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser); - const supers = [crowd.pop()!, crowd.pop()!, crowd.pop()!]; - - const subsPerSup = crowd.length / supers.length; - let subCount = 0; - const subs = [ - crowd.slice(subCount, subCount += subsPerSup + 1), - crowd.slice(subCount, subCount += subsPerSup), - crowd.slice(subCount, subCount += subsPerSup - 1), - ]; - - const subsInfo1 = supers.map((acc, i) => [ - acc.address, [ - 1000000n + BigInt(i + 1), - subs[i].map((sub, j) => [ - sub.address, {Raw: `accounter #${j}`}, - ]), - ], - ]); - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo1] as any); - - // change some sub-identities... - subs[2].pop(); subs[2].pop(); subs[2].push(...await helper.arrange.createAccounts([0n], superuser)); - - // ...and set them - const subsInfo2 = [[ - supers[2].address, [ - 999999n, - subs[2].map((sub, j) => [ - sub.address, {Raw: `discounter #${j}`}, - ]), - ], - ]]; - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo2] as any); - - // make sure everything else is the same - for(let i = 0; i < supers.length - 1; i++) { - // check deposit - expect(((await helper.getApi().query.identity.subsOf(supers[i].address)).toJSON() as any)[0]).to.be.equal(1000001 + i); - - const subsAccounts = await getSubIdentityAccounts(helper, supers[i].address); - // check sub-identities as account ids - expect(subsAccounts).to.include.members(subs[i].map(x => x.address)); - - for(let j = 0; j < subsAccounts; j++) { - // check sub-identities' names - expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `accounter #${j}`}); - } - } - - // check deposit - expect(((await helper.getApi().query.identity.subsOf(supers[2].address)).toJSON() as any)[0]).to.be.equal(999999); - - const subsAccounts = await getSubIdentityAccounts(helper, supers[2].address); - // check sub-identities as account ids - expect(subsAccounts).to.include.members(subs[2].map(x => x.address)); - - for(let j = 0; j < subsAccounts.length; j++) { - // check sub-identities' names - expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `discounter #${j}`}); - } - }); - - itSub('Removes sub-identities', async ({helper}) => { - const crowdSize = 3; - const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser); - const sup = crowd.pop()!; - - const subsInfo1 = [[ - sup.address, [ - 1000000n, - crowd.map((sub, j) => [ - sub.address, {Raw: `accounter #${j}`}, - ]), - ], - ]]; - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo1] as any); - - // empty sub-identities should delete the records - const subsInfo2 = [[ - sup.address, [ - 1000000n, - [], - ], - ]]; - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo2] as any); - - // check deposit - expect((await helper.getApi().query.identity.subsOf(sup.address)).toHuman()).to.be.deep.equal(['0', []]); - - for(let j = 0; j < crowd.length; j++) { - // check sub-identities' names - expect((await getSubIdentityName(helper, crowd[j].address))).to.be.null; - } - }); - - itSub('Removing identities deletes associated sub-identities', async ({helper}) => { - const crowd = await helper.arrange.createCrowd(3, 0n, superuser); - const sup = crowd.pop()!; - - // insert identity - const identities = [[sup.address, {info: {display: {Raw: 'mental'}}}]]; - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any); - - // and its sub-identities - const subsInfo = [[ - sup.address, [ - 1000000n, - crowd.map((sub, j) => [ - sub.address, {Raw: `accounter #${j}`}, - ]), - ], - ]]; - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo] as any); - - // delete top identity - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [[sup.address]]); - - // check that sub-identities are deleted - expect((await helper.getApi().query.identity.subsOf(sup.address)).toHuman()).to.be.deep.equal(['0', []]); - - for(let j = 0; j < crowd.length; j++) { - // check sub-identities' names - expect((await getSubIdentityName(helper, crowd[j].address))).to.be.null; - } - }); - }); - - after(async function() { - if(!process.env.RUN_COLLATOR_TESTS) return; - - await usingPlaygrounds(async helper => { - if(helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) return; - - const identitiesToRemove: string[] = await getIdentityAccounts(helper); - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [identitiesToRemove]); - }); - }); -}); --- a/js-packages/tests/src/config.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import process from 'process'; - -const config = { - relayUrl: process.env.RELAY_URL || 'ws://127.0.0.1:9844', - substrateUrl: process.env.RELAY_OPAL_URL || process.env.RELAY_QUARTZ_URL || process.env.RELAY_UNIQUE_URL || process.env.RELAY_SAPPHIRE_URL || 'ws://127.0.0.1:9944', - acalaUrl: process.env.RELAY_ACALA_URL || 'ws://127.0.0.1:9946', - karuraUrl: process.env.RELAY_KARURA_URL || 'ws://127.0.0.1:9946', - moonbeamUrl: process.env.RELAY_MOONBEAM_URL || 'ws://127.0.0.1:9947', - moonriverUrl: process.env.RELAY_MOONRIVER_URL || 'ws://127.0.0.1:9947', - astarUrl: process.env.RELAY_ASTAR_URL || 'ws://127.0.0.1:9949', - shidenUrl: process.env.RELAY_SHIDEN_URL || 'ws://127.0.0.1:9949', - westmintUrl: process.env.RELAY_WESTMINT_URL || 'ws://127.0.0.1:9948', - statemineUrl: process.env.RELAY_STATEMINE_URL || 'ws://127.0.0.1:9948', - statemintUrl: process.env.RELAY_STATEMINT_URL || 'ws://127.0.0.1:9948', - polkadexUrl: process.env.RELAY_POLKADEX_URL || 'ws://127.0.0.1:9950', -}; - -export default config; --- a/js-packages/tests/src/config_docker.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import process from 'process'; - -const config = { - substrateUrl: process.env.substrateUrl || 'ws://blockchain_nodes:9944', -}; - -export default config; --- a/js-packages/tests/src/confirmSponsorship.test.ts +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js'; -import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js'; - -async function setSponsorHelper(collection: any, signer: IKeyringPair, sponsorAddress: string) { - await collection.setSponsor(signer, sponsorAddress); - const raw = (await collection.getData())?.raw; - expect(raw.sponsorship.Unconfirmed).to.be.equal(sponsorAddress); -} - -async function confirmSponsorHelper(collection: any, signer: IKeyringPair) { - await collection.confirmSponsorship(signer); - const raw = (await collection.getData())?.raw; - expect(raw.sponsorship.Confirmed).to.be.equal(signer.address); -} - -describe('integration test: ext. confirmSponsorship():', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - let zeroBalance: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie, zeroBalance] = await helper.arrange.createAccounts([100n, 100n, 100n, 0n], donor); - }); - }); - - itSub('Confirm collection sponsorship', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await setSponsorHelper(collection, alice, bob.address); - await confirmSponsorHelper(collection, bob); - }); - - itSub('Add sponsor to a collection after the same sponsor was already added and confirmed', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await setSponsorHelper(collection, alice, bob.address); - await confirmSponsorHelper(collection, bob); - await setSponsorHelper(collection, alice, bob.address); - }); - itSub('Add new sponsor to a collection after another sponsor was already added and confirmed', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await setSponsorHelper(collection, alice, bob.address); - await confirmSponsorHelper(collection, bob); - await setSponsorHelper(collection, alice, charlie.address); - }); - - itSub('NFT: Transfer fees are paid by the sponsor after confirmation', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); - const token = await collection.mintToken(alice, {Substrate: zeroBalance.address}); - await token.transfer(zeroBalance, {Substrate: alice.address}); - const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); - expect(bobBalanceAfter < bobBalanceBefore).to.be.true; - }); - - itSub('Fungible: Transfer fees are paid by the sponsor after confirmation', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); - await collection.mint(alice, 100n, {Substrate: zeroBalance.address}); - await collection.transfer(zeroBalance, {Substrate: alice.address}, 1n); - const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); - expect(bobBalanceAfter < bobBalanceBefore).to.be.true; - }); - - itSub.ifWithPallets('ReFungible: Transfer fees are paid by the sponsor after confirmation', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); - const token = await collection.mintToken(alice, 100n, {Substrate: zeroBalance.address}); - await token.transfer(zeroBalance, {Substrate: alice.address}, 1n); - const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); - expect(bobBalanceAfter < bobBalanceBefore).to.be.true; - }); - - itSub('CreateItem fees are paid by the sponsor after confirmation', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await collection.addToAllowList(alice, {Substrate: zeroBalance.address}); - - const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); - await collection.mintToken(zeroBalance, {Substrate: zeroBalance.address}); - const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); - - expect(bobBalanceAfter < bobBalanceBefore).to.be.true; - }); - - itSub('NFT: Sponsoring of transfers is rate limited', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: { - sponsorTransferTimeout: 1000, - }}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - - const token = await collection.mintToken(alice, {Substrate: alice.address}); - await token.transfer(alice, {Substrate: zeroBalance.address}); - const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); - - const transferTx = () => token.transfer(zeroBalance, {Substrate: alice.address}); - await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees'); - const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); - - expect(bobBalanceAfter === bobBalanceBefore).to.be.true; - }); - - itSub('Fungible: Sponsoring is rate limited', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: { - sponsorTransferTimeout: 1000, - }}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - - await collection.mint(alice, 100n, {Substrate: zeroBalance.address}); - await collection.transfer(zeroBalance, {Substrate: zeroBalance.address}, 1n); - const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); - - const transferTx = () => collection.transfer(zeroBalance, {Substrate: zeroBalance.address}); - await expect(transferTx()).to.be.rejected; - const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); - - expect(bobBalanceAfter === bobBalanceBefore).to.be.true; - }); - - itSub.ifWithPallets('ReFungible: Sponsoring is rate limited', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: { - sponsorTransferTimeout: 1000, - }}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - - const token = await collection.mintToken(alice, 100n, {Substrate: zeroBalance.address}); - await token.transfer(zeroBalance, {Substrate: alice.address}); - - const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); - const transferTx = () => token.transfer(zeroBalance, {Substrate: alice.address}); - await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees'); - const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); - - expect(bobBalanceAfter === bobBalanceBefore).to.be.true; - }); - - itSub('NFT: Sponsoring of createItem is rate limited', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: { - sponsoredDataRateLimit: {blocks: 1000}, - sponsorTransferTimeout: 1000, - }}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - await collection.setPermissions(alice, {mintMode: true, access: 'AllowList'}); - await collection.addToAllowList(alice, {Substrate: zeroBalance.address}); - - await collection.mintToken(zeroBalance, {Substrate: zeroBalance.address}); - - const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); - const mintTx = () => collection.mintToken(zeroBalance, {Substrate: zeroBalance.address}); - await expect(mintTx()).to.be.rejectedWith('Inability to pay some fees'); - const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); - - expect(bobBalanceAfter === bobBalanceBefore).to.be.true; - }); -}); - -describe('(!negative test!) integration test: ext. confirmSponsorship():', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - let ownerZeroBalance: IKeyringPair; - let senderZeroBalance: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie, ownerZeroBalance, senderZeroBalance] = await helper.arrange.createAccounts([100n, 100n, 100n, 0n, 0n], donor); - }); - }); - - itSub('(!negative test!) Confirm sponsorship for a collection that never existed', async ({helper}) => { - const collectionId = NON_EXISTENT_COLLECTION_ID; - const confirmSponsorshipTx = () => helper.collection.confirmSponsorship(bob, collectionId); - await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('(!negative test!) Confirm sponsorship using a non-sponsor address', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.setSponsor(alice, bob.address); - const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie); - await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); - }); - - itSub('(!negative test!) Confirm sponsorship using owner address', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.setSponsor(alice, bob.address); - const confirmSponsorshipTx = () => collection.confirmSponsorship(alice); - await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); - }); - - itSub('(!negative test!) Confirm sponsorship by collection admin', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.setSponsor(alice, bob.address); - await collection.addAdmin(alice, {Substrate: charlie.address}); - const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie); - await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); - }); - - itSub('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie); - await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); - }); - - itSub('(!negative test!) Confirm sponsorship in a collection that was destroyed', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.burn(alice); - const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie); - await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('(!negative test!) Transfer fees are not paid by the sponsor if the transfer failed', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - const token = await collection.mintToken(alice, {Substrate: ownerZeroBalance.address}); - const sponsorBalanceBefore = await helper.balance.getSubstrate(bob.address); - const transferTx = () => token.transfer(senderZeroBalance, {Substrate: alice.address}); - await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees'); - const sponsorBalanceAfter = await helper.balance.getSubstrate(bob.address); - expect(sponsorBalanceAfter).to.equal(sponsorBalanceBefore); - }); -}); --- a/js-packages/tests/src/connection.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {itSub, expect, usingPlaygrounds} from './util/index.js'; - -describe('Connection smoke test', () => { - itSub('Connection can be established', async ({helper}) => { - const health = (await helper.callRpc('api.rpc.system.health')).toJSON(); - expect(health).to.be.not.empty; - }); - - it('Cannot connect to 255.255.255.255', async () => { - await expect((async () => { - await usingPlaygrounds(async helper => { - await helper.callRpc('api.rpc.system.health'); - }, 'ws://255.255.255.255:9944'); - })()).to.be.eventually.rejected; - }); -}); --- a/js-packages/tests/src/createCollection.test.ts +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js'; -import {CollectionFlag} from '@unique/playgrounds/src/types.js'; -import type {ICollectionCreationOptions, IProperty} from '@unique/playgrounds/src/types.js'; -import {UniqueHelper} from '@unique/playgrounds/src/unique.js'; - -async function mintCollectionHelper(helper: UniqueHelper, signer: IKeyringPair, options: ICollectionCreationOptions, type?: 'nft' | 'fungible' | 'refungible') { - let collection; - if(type === 'nft') { - collection = await helper.nft.mintCollection(signer, options); - } else if(type === 'fungible') { - collection = await helper.ft.mintCollection(signer, options, 0); - } else { - collection = await helper.rft.mintCollection(signer, options); - } - const data = await collection.getData(); - expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(signer.address)); - expect(data?.name).to.be.equal(options.name); - expect(data?.description).to.be.equal(options.description); - expect(data?.raw.tokenPrefix).to.be.equal(options.tokenPrefix); - if(options.properties) { - expect(data?.raw.properties).to.be.deep.equal(options.properties); - } - if(options.adminList) { - expect(data?.admins).to.be.deep.equal(options.adminList); - } - - if(options.flags) { - if((options.flags[0] & 64) != 0) - expect(data?.raw.flags.erc721metadata).to.be.true; - if((options.flags[0] & 128) != 0) - expect(data?.raw.flags.foreign).to.be.false; - } - - if(options.tokenPropertyPermissions) { - expect(data?.raw.tokenPropertyPermissions).to.be.deep.equal(options.tokenPropertyPermissions); - } - - return collection; -} - -describe('integration test: ext. createCollection():', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - itSub('Create new NFT collection', async ({helper}) => { - await mintCollectionHelper(helper, alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 'nft'); - }); - itSub('Create new NFT collection whith collection_name of maximum length (64 bytes)', async ({helper}) => { - await mintCollectionHelper(helper, alice, {name: 'A'.repeat(64), description: 'descr', tokenPrefix: 'COL'}, 'nft'); - }); - itSub('Create new NFT collection whith collection_description of maximum length (256 bytes)', async ({helper}) => { - await mintCollectionHelper(helper, alice, {name: 'name', description: 'A'.repeat(256), tokenPrefix: 'COL'}, 'nft'); - }); - itSub('Create new NFT collection whith token_prefix of maximum length (16 bytes)', async ({helper}) => { - await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(16)}, 'nft'); - }); - - itSub('Create new Fungible collection', async ({helper}) => { - await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible'); - }); - - itSub.ifWithPallets('Create new ReFungible collection', [Pallets.ReFungible], async ({helper}) => { - await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'refungible'); - }); - - itSub('create new collection with properties', async ({helper}) => { - await mintCollectionHelper(helper, alice, { - name: 'name', description: 'descr', tokenPrefix: 'COL', - properties: [{key: 'key1', value: 'val1'}], - tokenPropertyPermissions: [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}], - }, 'nft'); - }); - - itSub('create new collection with admin', async ({helper}) => { - await mintCollectionHelper(helper, alice, { - name: 'name', description: 'descr', tokenPrefix: 'COL', - adminList: [{Substrate: bob.address}], - }, 'nft'); - }); - - itSub('create new collection with flags', async ({helper}) => { - await mintCollectionHelper(helper, alice, { - name: 'name', description: 'descr', tokenPrefix: 'COL', - flags: [CollectionFlag.Erc721metadata], - }, 'nft'); - - // User can not set Foreign flag itself - - await expect(mintCollectionHelper(helper, alice, { - name: 'name', description: 'descr', tokenPrefix: 'COL', - flags: [CollectionFlag.Foreign], - }, 'nft')).to.be.rejectedWith(/common.NoPermission/); - - await expect(mintCollectionHelper(helper, alice, { - name: 'name', description: 'descr', tokenPrefix: 'COL', - flags: [CollectionFlag.Erc721metadata, CollectionFlag.Foreign], - }, 'nft')).to.be.rejectedWith(/common.NoPermission/); - }); - - itSub('Create new collection with extra fields', async ({helper}) => { - const collection = await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible'); - await collection.setPermissions(alice, {access: 'AllowList'}); - await collection.setLimits(alice, {accountTokenOwnershipLimit: 3}); - const data = await collection.getData(); - const limits = await collection.getEffectiveLimits(); - const raw = data?.raw; - - expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address)); - expect(data?.name).to.be.equal('name'); - expect(data?.description).to.be.equal('descr'); - expect(raw.permissions.access).to.be.equal('AllowList'); - expect(raw.mode).to.be.deep.equal({Fungible: '0'}); - expect(limits.accountTokenOwnershipLimit).to.be.equal(3); - }); - - itSub('New collection is not external', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}); - const data = await collection.getData(); - expect(data?.raw.readOnly).to.be.false; - }); -}); - -describe('(!negative test!) integration test: ext. createCollection():', () => { - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([100n], donor); - }); - }); - - itSub('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async ({helper}) => { - const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'A'.repeat(65), description: 'descr', tokenPrefix: 'COL'}); - await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error'); - }); - itSub('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async ({helper}) => { - const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'A'.repeat(257), tokenPrefix: 'COL'}); - await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error'); - }); - itSub('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async ({helper}) => { - const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(17)}); - await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error'); - }); - - itSub('(!negative test!) fails when bad limits are set', async ({helper}) => { - const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', limits: {tokenLimit: 0}}); - await expect(mintCollectionTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/); - }); - - itSub('(!negative test!) create collection with incorrect property limit (64 elements)', async ({helper}) => { - const props: IProperty[] = []; - - for(let i = 0; i < 65; i++) { - props.push({key: `key${i}`, value: `value${i}`}); - } - const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props}); - await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error'); - }); - - itSub('(!negative test!) create collection with incorrect property limit (40 kb)', async ({helper}) => { - const props: IProperty[] = []; - - for(let i = 0; i < 32; i++) { - props.push({key: `key${i}`.repeat(80), value: `value${i}`.repeat(80)}); - } - - const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props}); - await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error'); - }); -}); --- a/js-packages/tests/src/createItem.test.ts +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js'; -import type {IProperty, ICrossAccountId} from '@unique/playgrounds/src/types.js'; -import {UniqueHelper} from '@unique/playgrounds/src/unique.js'; - -async function mintTokenHelper(helper: UniqueHelper, collection: any, signer: IKeyringPair, owner: ICrossAccountId, type: 'nft' | 'fungible' | 'refungible'='nft', properties?: IProperty[]) { - let token; - const itemCountBefore = await helper.collection.getLastTokenId(collection.collectionId); - const itemBalanceBefore = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt(); - if(type === 'nft') { - token = await collection.mintToken(signer, owner, properties); - } else if(type === 'fungible') { - await collection.mint(signer, 10n, owner); - } else { - token = await collection.mintToken(signer, 100n, owner, properties); - } - - const itemCountAfter = await helper.collection.getLastTokenId(collection.collectionId); - const itemBalanceAfter = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt(); - - if(type === 'fungible') { - expect(itemBalanceAfter - itemBalanceBefore).to.be.equal(10n); - } else { - expect(itemCountAfter).to.be.equal(itemCountBefore + 1); - } - - return token; -} - - -describe('integration test: ext. ():', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('Create new item in NFT collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}); - }); - itSub('Create new item in Fungible collection', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}, 'fungible'); - }); - itSub('Check events on create new item in Fungible collection', async ({helper}) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); - const to = {Substrate: alice.address}; - { - const createData = {fungible: {value: 100}}; - const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]); - const result = helper.util.extractTokensFromCreationResult(events); - expect(result.tokens[0].amount).to.be.equal(100n); - expect(result.tokens[0].collectionId).to.be.equal(collectionId); - expect(result.tokens[0].owner).to.be.deep.equal(to); - } - { - const createData = {fungible: {value: 50}}; - const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]); - const result = helper.util.extractTokensFromCreationResult(events); - expect(result.tokens[0].amount).to.be.equal(50n); - expect(result.tokens[0].collectionId).to.be.equal(collectionId); - expect(result.tokens[0].owner).to.be.deep.equal(to); - } - }); - itSub.ifWithPallets('Create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}, 'refungible'); - }); - itSub('Create new item in NFT collection with collection admin permissions', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.addAdmin(alice, {Substrate: bob.address}); - await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}); - }); - itSub('Create new item in Fungible collection with collection admin permissions', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await collection.addAdmin(alice, {Substrate: bob.address}); - await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}, 'fungible'); - }); - itSub.ifWithPallets('Create new item in ReFungible collection with collection admin permissions', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await collection.addAdmin(alice, {Substrate: bob.address}); - await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}, 'refungible'); - }); - - itSub('Set property Admin', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', - properties: [{key: 'k', value: 'v'}], - tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: false, mutable: true, collectionAdmin: true}}], - }); - await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]); - }); - - itSub('Set property AdminConst', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', - properties: [{key: 'k', value: 'v'}], - tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}}], - }); - await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]); - }); - - itSub('Set property itemOwnerOrAdmin', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', - properties: [{key: 'k', value: 'v'}], - tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}], - }); - await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]); - }); - - itSub('Check total pieces of Fungible token', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - const amount = 10n; - await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'fungible'); - { - const totalPieces = await collection.getTotalPieces(); - expect(totalPieces).to.be.equal(amount); - } - await collection.transfer(bob, {Substrate: alice.address}, 1n); - { - const totalPieces = await collection.getTotalPieces(); - expect(totalPieces).to.be.equal(amount); - } - }); - - itSub('Check total pieces of NFT token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const amount = 1n; - const token = await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}); - { - const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]); - expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount); - } - await token.transfer(bob, {Substrate: alice.address}); - { - const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]); - expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount); - } - }); - - itSub.ifWithPallets('Check total pieces of ReFungible token', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const amount = 100n; - const token = await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'refungible'); - { - const totalPieces = await token.getTotalPieces(); - expect(totalPieces).to.be.equal(amount); - } - await token.transfer(bob, {Substrate: alice.address}, 60n); - { - const totalPieces = await token.getTotalPieces(); - expect(totalPieces).to.be.equal(amount); - } - }); -}); - -describe('Negative integration test: ext. createItem():', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('Regular user cannot create new item in NFT collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const mintTx = () => collection.mintToken(bob, {Substrate: bob.address}); - await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); - }); - itSub('Regular user cannot create new item in Fungible collection', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - const mintTx = () => collection.mint(bob, 10n, {Substrate: bob.address}); - await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); - }); - itSub.ifWithPallets('Regular user cannot create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const mintTx = () => collection.mintToken(bob, 100n, {Substrate: bob.address}); - await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); - }); - - itSub('No editing rights', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', - tokenPropertyPermissions: [{key: 'k', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}], - }); - const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]); - await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('User doesnt have editing rights', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', - tokenPropertyPermissions: [{key: 'k', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}], - }); - const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]); - await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Adding property without access rights', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]); - await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Adding more than 64 prps', async ({helper}) => { - const props: IProperty[] = []; - - for(let i = 0; i < 65; i++) { - props.push({key: `key${i}`, value: `value${i}`}); - } - - - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, props); - await expect(mintTx()).to.be.rejectedWith('Verification Error'); - }); - - itSub('Trying to add bigger property than allowed', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'k1', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}, - {key: 'k2', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}, - ], - }); - const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [ - {key: 'k1', value: 'vvvvvv'.repeat(5000)}, - {key: 'k2', value: 'vvv'.repeat(5000)}, - ]); - await expect(mintTx()).to.be.rejectedWith(/common\.NoSpaceForProperty/); - }); - - itSub('Check total pieces for invalid Fungible token', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - const invalidTokenId = 1_000_000; - expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true; - expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n); - }); - - itSub('Check total pieces for invalid NFT token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const invalidTokenId = 1_000_000; - expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true; - expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n); - }); - - itSub.ifWithPallets('Check total pieces for invalid Refungible token', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const invalidTokenId = 1_000_000; - expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true; - expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n); - }); -}); --- a/js-packages/tests/src/createMultipleItems.test.ts +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, Pallets, itSub} from './util/index.js'; - -describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => { - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([100n], donor); - }); - }); - - itSub('Create 0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'data', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}}, - ], - }); - const args = [ - {properties: [{key: 'data', value: '1'}]}, - {properties: [{key: 'data', value: '2'}]}, - {properties: [{key: 'data', value: '3'}]}, - ]; - const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - for(const [i, token] of tokens.entries()) { - const tokenData = await token.getData(); - expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)}); - expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value); - } - }); - - itSub('Create 0x01, 0x02, 0x03 items in active Fungible collection and verify tokens data in chain', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - }); - const args = [ - {value: 1n}, - {value: 2n}, - {value: 3n}, - ]; - await helper.ft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, args, {Substrate: alice.address}); - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(6n); - }); - - itSub.ifWithPallets('Create 0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - }); - const args = [ - {pieces: 1n}, - {pieces: 2n}, - {pieces: 3n}, - ]; - const tokens = await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - - for(const [i, token] of tokens.entries()) { - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(BigInt(i + 1)); - } - }); - - itSub('Can mint amount of items equals to collection limits', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - limits: { - tokenLimit: 2, - }, - }); - const args = [{}, {}]; - await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - }); - - itSub('Create 0x31, 0x32, 0x33 items in active NFT with property Admin', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'data', permission: {tokenOwner: false, mutable: true, collectionAdmin: true}}, - ], - }); - const args = [ - {properties: [{key: 'data', value: '1'}]}, - {properties: [{key: 'data', value: '2'}]}, - {properties: [{key: 'data', value: '3'}]}, - ]; - const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - for(const [i, token] of tokens.entries()) { - const tokenData = await token.getData(); - expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)}); - expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value); - } - }); - - itSub('Create 0x31, 0x32, 0x33 items in active NFT with property AdminConst', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'data', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}}, - ], - }); - const args = [ - {properties: [{key: 'data', value: '1'}]}, - {properties: [{key: 'data', value: '2'}]}, - {properties: [{key: 'data', value: '3'}]}, - ]; - const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - for(const [i, token] of tokens.entries()) { - const tokenData = await token.getData(); - expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)}); - expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value); - } - }); - - itSub('Create 0x31, 0x32, 0x33 items in active NFT with property itemOwnerOrAdmin', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, - ], - }); - const args = [ - {properties: [{key: 'data', value: '1'}]}, - {properties: [{key: 'data', value: '2'}]}, - {properties: [{key: 'data', value: '3'}]}, - ]; - const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - for(const [i, token] of tokens.entries()) { - const tokenData = await token.getData(); - expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)}); - expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value); - } - }); -}); - -describe('Negative Integration Test createMultipleItems(collection_id, owner, items_data):', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('Regular user cannot create items in active NFT collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - }); - const args = [ - {}, - {}, - ]; - const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args); - await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); - }); - - itSub('Regular user cannot create items in active Fungible collection', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - }); - const args = [ - {value: 1n}, - {value: 2n}, - {value: 3n}, - ]; - const mintTx = () => helper.ft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, args, {Substrate: alice.address}); - await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); - }); - - itSub.ifWithPallets('Regular user cannot create items in active ReFungible collection', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - }); - const args = [ - {pieces: 1n}, - {pieces: 1n}, - {pieces: 1n}, - ]; - const mintTx = () => helper.rft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args); - await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/); - }); - - itSub('Create token in not existing collection', async ({helper}) => { - const collectionId = 1_000_000; - const args = [ - {}, - {}, - ]; - const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(bob, collectionId, {Substrate: alice.address}, args); - await expect(mintTx()).to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('Create NFTs that has reached the maximum data limit', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, - ], - }); - const args = [ - {properties: [{key: 'data', value: 'A'.repeat(32769)}]}, - {properties: [{key: 'data', value: 'B'.repeat(32769)}]}, - {properties: [{key: 'data', value: 'C'.repeat(32769)}]}, - ]; - const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - await expect(mintTx()).to.be.rejectedWith('Verification Error'); - }); - - itSub.ifWithPallets('Create Refungible tokens that has reached the maximum data limit', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, - ], - }); - const args = [ - {pieces: 10n, properties: [{key: 'data', value: 'A'.repeat(32769)}]}, - {pieces: 10n, properties: [{key: 'data', value: 'B'.repeat(32769)}]}, - {pieces: 10n, properties: [{key: 'data', value: 'C'.repeat(32769)}]}, - ]; - const mintTx = () => helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - await expect(mintTx()).to.be.rejectedWith('Verification Error'); - }); - - itSub.ifWithPallets('Create tokens with different types', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - }); - - const types = ['NFT', 'Fungible', 'ReFungible']; - await expect(helper.executeExtrinsic( - alice, - 'api.tx.unique.createMultipleItems', - [collectionId, {Substrate: alice.address}, types], - )).to.be.rejectedWith(/nonfungible\.NotNonfungibleDataUsedToMintFungibleCollectionToken/); - }); - - itSub('Create tokens with different data limits <> maximum data limit', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, - ], - }); - const args = [ - {properties: [{key: 'data', value: 'A'}]}, - {properties: [{key: 'data', value: 'B'.repeat(32769)}]}, - ]; - const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - await expect(mintTx()).to.be.rejectedWith('Verification Error'); - }); - - itSub('Fails when minting tokens exceeds collectionLimits amount', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, - ], - limits: { - tokenLimit: 1, - }, - }); - const args = [ - {}, - {}, - ]; - const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - await expect(mintTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/); - }); - - itSub('User doesnt have editing rights', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'data', permission: {tokenOwner: false, mutable: true, collectionAdmin: false}}, - ], - }); - const args = [ - {properties: [{key: 'data', value: 'A'}]}, - ]; - const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Adding property without access rights', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - properties: [ - { - key: 'data', - value: 'v', - }, - ], - }); - const args = [ - {properties: [{key: 'data', value: 'A'}]}, - ]; - const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Adding more than 64 prps', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - }); - const prps = []; - - for(let i = 0; i < 65; i++) { - prps.push({key: `key${i}`, value: `value${i}`}); - } - - const args = [ - {properties: prps}, - {properties: prps}, - {properties: prps}, - ]; - - const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args); - await expect(mintTx()).to.be.rejectedWith('Verification Error'); - }); -}); --- a/js-packages/tests/src/createMultipleItemsEx.test.ts +++ /dev/null @@ -1,442 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, expect, Pallets, itSub} from './util/index.js'; -import type {IProperty} from '@unique/playgrounds/src/types.js'; - -describe('Integration Test: createMultipleItemsEx', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('can initialize multiple NFT with different owners', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - }); - const args = [ - { - owner: {Substrate: alice.address}, - }, - { - owner: {Substrate: bob.address}, - }, - { - owner: {Substrate: charlie.address}, - }, - ]; - - const tokens = await collection.mintMultipleTokens(alice, args); - for(const [i, token] of tokens.entries()) { - expect(await token.getOwner()).to.be.deep.equal(args[i].owner); - } - }); - - itSub('createMultipleItemsEx with property Admin', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - { - key: 'k', - permission: { - mutable: true, - collectionAdmin: true, - tokenOwner: false, - }, - }, - ], - }); - - const args = [ - { - owner: {Substrate: alice.address}, - properties: [{key: 'k', value: 'v1'}], - }, - { - owner: {Substrate: bob.address}, - properties: [{key: 'k', value: 'v2'}], - }, - { - owner: {Substrate: charlie.address}, - properties: [{key: 'k', value: 'v3'}], - }, - ]; - - const tokens = await collection.mintMultipleTokens(alice, args); - for(const [i, token] of tokens.entries()) { - expect(await token.getOwner()).to.be.deep.equal(args[i].owner); - expect(await token.getData()).to.not.be.empty; - } - }); - - itSub('createMultipleItemsEx with property AdminConst', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - { - key: 'k', - permission: { - mutable: false, - collectionAdmin: true, - tokenOwner: false, - }, - }, - ], - }); - - const args = [ - { - owner: {Substrate: alice.address}, - properties: [{key: 'k', value: 'v1'}], - }, - { - owner: {Substrate: bob.address}, - properties: [{key: 'k', value: 'v2'}], - }, - { - owner: {Substrate: charlie.address}, - properties: [{key: 'k', value: 'v3'}], - }, - ]; - - const tokens = await collection.mintMultipleTokens(alice, args); - for(const [i, token] of tokens.entries()) { - expect(await token.getOwner()).to.be.deep.equal(args[i].owner); - expect(await token.getData()).to.not.be.empty; - } - }); - - itSub('createMultipleItemsEx with property itemOwnerOrAdmin', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - { - key: 'k', - permission: { - mutable: false, - collectionAdmin: true, - tokenOwner: true, - }, - }, - ], - }); - - const args = [ - { - owner: {Substrate: alice.address}, - properties: [{key: 'k', value: 'v1'}], - }, - { - owner: {Substrate: bob.address}, - properties: [{key: 'k', value: 'v2'}], - }, - { - owner: {Substrate: charlie.address}, - properties: [{key: 'k', value: 'v3'}], - }, - ]; - - const tokens = await collection.mintMultipleTokens(alice, args); - for(const [i, token] of tokens.entries()) { - expect(await token.getOwner()).to.be.deep.equal(args[i].owner); - expect(await token.getData()).to.not.be.empty; - } - }); - - itSub('can initialize fungible with multiple owners', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - }, 0); - - await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx',[collection.collectionId, { - Fungible: new Map([ - [JSON.stringify({Substrate: alice.address}), 50], - [JSON.stringify({Substrate: bob.address}), 100], - ]), - }], true); - - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(50n); - expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(100n); - }); - - itSub.ifWithPallets('can initialize an RFT with multiple owners', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'k', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}}, - ], - }); - - await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx', [collection.collectionId, { - RefungibleMultipleOwners: { - users: new Map([ - [JSON.stringify({Substrate: alice.address}), 1], - [JSON.stringify({Substrate: bob.address}), 2], - ]), - properties: [ - {key: 'k', value: 'v'}, - ], - }, - }], true); - const tokenId = await collection.getLastTokenId(); - expect(tokenId).to.be.equal(1); - expect(await collection.getTokenBalance(1, {Substrate: alice.address})).to.be.equal(1n); - expect(await collection.getTokenBalance(1, {Substrate: bob.address})).to.be.equal(2n); - }); - - itSub.ifWithPallets('can initialize multiple RFTs with the same owner', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - {key: 'k', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}}, - ], - }); - - await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx', [collection.collectionId, { - RefungibleMultipleItems: [ - { - user: {Substrate: alice.address}, pieces: 1, - properties: [ - {key: 'k', value: 'v1'}, - ], - }, - { - user: {Substrate: alice.address}, pieces: 3, - properties: [ - {key: 'k', value: 'v2'}, - ], - }, - ], - }], true); - - expect(await collection.getLastTokenId()).to.be.equal(2); - expect(await collection.getTokenBalance(1, {Substrate: alice.address})).to.be.equal(1n); - expect(await collection.getTokenBalance(2, {Substrate: alice.address})).to.be.equal(3n); - - const tokenData1 = await helper.rft.getToken(collection.collectionId, 1); - expect(tokenData1).to.not.be.null; - expect(tokenData1?.properties[0]).to.be.deep.equal({key: 'k', value: 'v1'}); - - const tokenData2 = await helper.rft.getToken(collection.collectionId, 2); - expect(tokenData2).to.not.be.null; - expect(tokenData2?.properties[0]).to.be.deep.equal({key: 'k', value: 'v2'}); - }); -}); - -describe('Negative test: createMultipleItemsEx', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('No editing rights', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - { - key: 'k', - permission: { - mutable: true, - collectionAdmin: false, - tokenOwner: false, - }, - }, - ], - }); - - const args = [ - { - owner: {Substrate: alice.address}, - properties: [{key: 'k', value: 'v1'}], - }, - { - owner: {Substrate: bob.address}, - properties: [{key: 'k', value: 'v2'}], - }, - { - owner: {Substrate: charlie.address}, - properties: [{key: 'k', value: 'v3'}], - }, - ]; - - await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('User doesnt have editing rights', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - { - key: 'k', - permission: { - mutable: false, - collectionAdmin: false, - tokenOwner: false, - }, - }, - ], - }); - - const args = [ - { - owner: {Substrate: alice.address}, - properties: [{key: 'k', value: 'v1'}], - }, - { - owner: {Substrate: bob.address}, - properties: [{key: 'k', value: 'v2'}], - }, - { - owner: {Substrate: charlie.address}, - properties: [{key: 'k', value: 'v3'}], - }, - ]; - - await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Adding property without access rights', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - }); - - const args = [ - { - owner: {Substrate: alice.address}, - properties: [{key: 'k', value: 'v1'}], - }, - { - owner: {Substrate: bob.address}, - properties: [{key: 'k', value: 'v2'}], - }, - { - owner: {Substrate: charlie.address}, - properties: [{key: 'k', value: 'v3'}], - }, - ]; - - await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Adding more than 64 properties', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - { - key: 'k', - permission: { - mutable: true, - collectionAdmin: true, - tokenOwner: true, - }, - }, - ], - }); - - const properties: IProperty[] = []; - - for(let i = 0; i < 65; i++) { - properties.push({key: `k${i}`, value: `v${i}`}); - } - - const args = [ - { - owner: {Substrate: alice.address}, - properties: properties, - }, - { - owner: {Substrate: bob.address}, - properties: properties, - }, - { - owner: {Substrate: charlie.address}, - properties: properties, - }, - ]; - - await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith('Verification Error'); - }); - - itSub('Trying to add bigger property than allowed', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'name', - description: 'descr', - tokenPrefix: 'COL', - tokenPropertyPermissions: [ - { - key: 'k', - permission: { - mutable: true, - collectionAdmin: true, - tokenOwner: true, - }, - }, - ], - }); - - const args = [ - { - owner: {Substrate: alice.address}, - properties: [{key: 'k', value: 'A'.repeat(32769)}], - }, - { - owner: {Substrate: bob.address}, - properties: [{key: 'k', value: 'A'.repeat(32769)}], - }, - { - owner: {Substrate: charlie.address}, - properties: [{key: 'k', value: 'A'.repeat(32769)}], - }, - ]; - - await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith('Verification Error'); - }); -}); --- a/js-packages/tests/src/creditFeesToTreasury.seqtest.ts +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {ApiPromise} from '@polkadot/api'; -import {usingPlaygrounds, expect, itSub} from './util/index.js'; -import type {u32} from '@polkadot/types-codec'; - -const TREASURY = '5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z'; -const saneMinimumFee = 0.05; -const saneMaximumFee = 0.5; -const createCollectionDeposit = 100; - -// Skip the inflation block pauses if the block is close to inflation block -// until the inflation happens -/*eslint no-async-promise-executor: "off"*/ -function skipInflationBlock(api: ApiPromise): Promise { - const promise = new Promise(async (resolve) => { - const inflationBlockInterval = api.consts.inflation.inflationBlockInterval as u32; - const blockInterval = inflationBlockInterval.toNumber(); - const unsubscribe = await api.rpc.chain.subscribeNewHeads(head => { - const currentBlock = head.number.toNumber(); - if(currentBlock % blockInterval < blockInterval - 10) { - unsubscribe(); - resolve(); - } else { - console.log(`Skipping inflation block, current block: ${currentBlock}`); - } - }); - }); - - return promise; -} - -describe('integration test: Fees must be credited to Treasury:', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('Total issuance does not change', async ({helper}) => { - const api = helper.getApi(); - await skipInflationBlock(api); - await helper.wait.newBlocks(1); - - const totalBefore = (await helper.callRpc('api.query.balances.totalIssuance', [])).toBigInt(); - - await helper.balance.transferToSubstrate(alice, bob.address, 1n); - - const totalAfter = (await helper.callRpc('api.query.balances.totalIssuance', [])).toBigInt(); - - expect(totalAfter).to.be.equal(totalBefore); - }); - - itSub('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async ({helper}) => { - await skipInflationBlock(helper.getApi()); - await helper.wait.newBlocks(1); - - const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY); - const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); - - const amount = 1n; - await helper.balance.transferToSubstrate(alice, bob.address, amount); - - const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY); - const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); - - const fee = aliceBalanceBefore - aliceBalanceAfter - amount; - const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore; - - expect(treasuryIncrease).to.be.equal(fee); - }); - - itSub('Treasury balance increased by failed tx fee', async ({helper}) => { - const api = helper.getApi(); - await helper.wait.newBlocks(1); - - const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY); - const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); - - await expect(helper.signTransaction(bob, api.tx.balances.forceSetBalance(alice.address, 0))).to.be.rejected; - - const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY); - const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); - - const fee = bobBalanceBefore - bobBalanceAfter; - const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore; - - expect(treasuryIncrease).to.be.equal(fee); - }); - - itSub('NFT Transactions also send fees to Treasury', async ({helper}) => { - await skipInflationBlock(helper.getApi()); - await helper.wait.newBlocks(1); - - const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY); - const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); - - await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY); - const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); - const fee = aliceBalanceBefore - aliceBalanceAfter; - const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore; - - expect(treasuryIncrease).to.be.equal(fee); - }); - - itSub('Fees are sane', async ({helper}) => { - const unique = helper.balance.getOneTokenNominal(); - await skipInflationBlock(helper.getApi()); - await helper.wait.newBlocks(1); - - const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); - - await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); - const fee = aliceBalanceBefore - aliceBalanceAfter; - - expect(fee / unique < BigInt(Math.ceil(saneMaximumFee + createCollectionDeposit))).to.be.true; - expect(fee / unique < BigInt(Math.ceil(saneMinimumFee + createCollectionDeposit))).to.be.true; - }); - - itSub('NFT Transfer fee is close to 0.1 Unique', async ({helper}) => { - await skipInflationBlock(helper.getApi()); - await helper.wait.newBlocks(1); - - const collection = await helper.nft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - }); - // const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); - const token = await collection.mintToken(alice, {Substrate: alice.address}); - - const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); - await token.transfer(alice, {Substrate: bob.address}); - const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); - - const fee = Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal()); - const expectedTransferFee = 0.1; - // fee drifts because of NextFeeMultiplier - const tolerance = 0.001; - - expect(Math.abs(fee - expectedTransferFee)).to.be.lessThan(tolerance); - }); -}); --- a/js-packages/tests/src/destroyCollection.test.ts +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, expect, usingPlaygrounds, Pallets} from './util/index.js'; - -describe('integration test: ext. destroyCollection():', () => { - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([100n], donor); - }); - }); - - itSub('NFT collection can be destroyed', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - }); - await collection.burn(alice); - expect(await collection.getData()).to.be.null; - }); - itSub('Fungible collection can be destroyed', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - }, 0); - await collection.burn(alice); - expect(await collection.getData()).to.be.null; - }); - itSub.ifWithPallets('ReFungible collection can be destroyed', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - }); - await collection.burn(alice); - expect(await collection.getData()).to.be.null; - }); -}); - -describe('(!negative test!) integration test: ext. destroyCollection():', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('(!negative test!) Destroy a collection that never existed', async ({helper}) => { - const collectionId = 1_000_000; - await expect(helper.collection.burn(alice, collectionId)).to.be.rejectedWith(/common\.CollectionNotFound/); - }); - itSub('(!negative test!) Destroy a collection that has already been destroyed', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - }); - await collection.burn(alice); - await expect(collection.burn(alice)).to.be.rejectedWith(/common\.CollectionNotFound/); - }); - itSub('(!negative test!) Destroy a collection using non-owner account', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - }); - await expect(collection.burn(bob)).to.be.rejectedWith(/common\.NoPermission/); - }); - itSub('(!negative test!) Destroy a collection using collection admin account', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - }); - await collection.addAdmin(alice, {Substrate: bob.address}); - await expect(collection.burn(bob)).to.be.rejectedWith(/common\.NoPermission/); - }); - itSub('fails when OwnerCanDestroy == false', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - limits: { - ownerCanDestroy: false, - }, - }); - await expect(collection.burn(alice)).to.be.rejectedWith(/common\.NoPermission/); - }); - itSub('fails when a collection still has a token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - }); - await collection.mintToken(alice, {Substrate: alice.address}); - await expect(collection.burn(alice)).to.be.rejectedWith(/common\.CantDestroyNotEmptyCollection/); - }); -}); --- a/js-packages/tests/src/enableDisableTransfer.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from './util/index.js'; - -describe('Enable/Disable Transfers', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('User can transfer token with enabled transfer flag', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - limits: { - transfersEnabled: true, - }, - }); - const token = await collection.mintToken(alice, {Substrate: alice.address}); - await token.transfer(alice, {Substrate: bob.address}); - expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); - }); - - itSub('User can\'n transfer token with disabled transfer flag', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - limits: { - transfersEnabled: false, - }, - }); - const token = await collection.mintToken(alice, {Substrate: alice.address}); - await expect(token.transfer(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.TransferNotAllowed/); - }); -}); - -describe('Negative Enable/Disable Transfers', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('Non-owner cannot change transfer flag', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, { - name: 'test', - description: 'test', - tokenPrefix: 'test', - limits: { - transfersEnabled: true, - }, - }); - - await expect(collection.setLimits(bob, {transfersEnabled: false})).to.be.rejectedWith(/common\.NoPermission/); - }); -}); --- a/js-packages/tests/src/eth/abi/collectionHelpers.json +++ /dev/null @@ -1,267 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "collectionId", - "type": "address" - } - ], - "name": "CollectionChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "collectionId", - "type": "address" - } - ], - "name": "CollectionCreated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "collectionId", - "type": "address" - } - ], - "name": "CollectionDestroyed", - "type": "event" - }, - { - "inputs": [ - { "internalType": "uint32", "name": "collectionId", "type": "uint32" } - ], - "name": "collectionAddress", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionCreationFee", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "collectionAddress", - "type": "address" - } - ], - "name": "collectionId", - "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "string", "name": "name", "type": "string" }, - { "internalType": "string", "name": "description", "type": "string" }, - { - "internalType": "string", - "name": "token_prefix", - "type": "string" - }, - { - "internalType": "enum CollectionMode", - "name": "mode", - "type": "uint8" - }, - { "internalType": "uint8", "name": "decimals", "type": "uint8" }, - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "properties", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { - "components": [ - { - "internalType": "enum TokenPermissionField", - "name": "code", - "type": "uint8" - }, - { "internalType": "bool", "name": "value", "type": "bool" } - ], - "internalType": "struct PropertyPermission[]", - "name": "permissions", - "type": "tuple[]" - } - ], - "internalType": "struct TokenPropertyPermission[]", - "name": "token_property_permissions", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress[]", - "name": "admin_list", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "bool", "name": "token_owner", "type": "bool" }, - { - "internalType": "bool", - "name": "collection_admin", - "type": "bool" - }, - { - "internalType": "address[]", - "name": "restricted", - "type": "address[]" - } - ], - "internalType": "struct CollectionNestingAndPermission", - "name": "nesting_settings", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "enum CollectionLimitField", - "name": "field", - "type": "uint8" - }, - { "internalType": "uint256", "name": "value", "type": "uint256" } - ], - "internalType": "struct CollectionLimitValue[]", - "name": "limits", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "pending_sponsor", - "type": "tuple" - }, - { - "internalType": "CollectionFlags", - "name": "flags", - "type": "uint8" - } - ], - "internalType": "struct CreateCollectionData", - "name": "data", - "type": "tuple" - } - ], - "name": "createCollection", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string", "name": "name", "type": "string" }, - { "internalType": "uint8", "name": "decimals", "type": "uint8" }, - { "internalType": "string", "name": "description", "type": "string" }, - { "internalType": "string", "name": "tokenPrefix", "type": "string" } - ], - "name": "createFTCollection", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string", "name": "name", "type": "string" }, - { "internalType": "string", "name": "description", "type": "string" }, - { "internalType": "string", "name": "tokenPrefix", "type": "string" } - ], - "name": "createNFTCollection", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string", "name": "name", "type": "string" }, - { "internalType": "string", "name": "description", "type": "string" }, - { "internalType": "string", "name": "tokenPrefix", "type": "string" } - ], - "name": "createRFTCollection", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "collectionAddress", - "type": "address" - } - ], - "name": "destroyCollection", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "collectionAddress", - "type": "address" - } - ], - "name": "isCollectionExist", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "collection", "type": "address" }, - { "internalType": "string", "name": "baseUri", "type": "string" } - ], - "name": "makeCollectionERC721MetadataCompatible", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } - ], - "name": "supportsInterface", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - } -] --- a/js-packages/tests/src/eth/abi/contractHelpers.json +++ /dev/null @@ -1,326 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "ContractSponsorRemoved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "sponsor", - "type": "address" - } - ], - "name": "ContractSponsorSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "sponsor", - "type": "address" - } - ], - "name": "ContractSponsorshipConfirmed", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { "internalType": "address", "name": "user", "type": "address" } - ], - "name": "allowed", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "allowlistEnabled", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "confirmSponsorship", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "contractOwner", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "hasPendingSponsor", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "hasSponsor", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "removeSponsor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "selfSponsoredEnable", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { "internalType": "address", "name": "sponsor", "type": "address" } - ], - "name": "setSponsor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { "internalType": "uint256", "name": "feeLimit", "type": "uint256" } - ], - "name": "setSponsoringFeeLimit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { - "internalType": "enum SponsoringModeT", - "name": "mode", - "type": "uint8" - } - ], - "name": "setSponsoringMode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { "internalType": "uint32", "name": "rateLimit", "type": "uint32" } - ], - "name": "setSponsoringRateLimit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "sponsor", - "outputs": [ - { - "components": [ - { "internalType": "bool", "name": "status", "type": "bool" }, - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "value", - "type": "tuple" - } - ], - "internalType": "struct OptionCrossAddress", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "sponsoringEnabled", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "sponsoringFeeLimit", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - } - ], - "name": "sponsoringRateLimit", - "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } - ], - "name": "supportsInterface", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { "internalType": "address", "name": "user", "type": "address" }, - { "internalType": "bool", "name": "isAllowed", "type": "bool" } - ], - "name": "toggleAllowed", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "contractAddress", - "type": "address" - }, - { "internalType": "bool", "name": "enabled", "type": "bool" } - ], - "name": "toggleAllowlist", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] --- a/js-packages/tests/src/eth/abi/fungible.json +++ /dev/null @@ -1,722 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "newAdmin", - "type": "tuple" - } - ], - "name": "addCollectionAdminCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "addToCollectionAllowListCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" }, - { "internalType": "address", "name": "spender", "type": "address" } - ], - "name": "allowance", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "owner", - "type": "tuple" - }, - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "spender", - "type": "tuple" - } - ], - "name": "allowanceCross", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "allowlistedCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "spender", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "approve", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "spender", - "type": "tuple" - }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "approveCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" } - ], - "name": "balanceOf", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "owner", - "type": "tuple" - } - ], - "name": "balanceOfCross", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "from", - "type": "tuple" - }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "burnFromCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "newOwner", - "type": "tuple" - } - ], - "name": "changeCollectionOwnerCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "collectionAdmins", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionHelperAddress", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionLimits", - "outputs": [ - { - "components": [ - { - "internalType": "enum CollectionLimitField", - "name": "field", - "type": "uint8" - }, - { - "components": [ - { "internalType": "bool", "name": "status", "type": "bool" }, - { "internalType": "uint256", "name": "value", "type": "uint256" } - ], - "internalType": "struct OptionUint256", - "name": "value", - "type": "tuple" - } - ], - "internalType": "struct CollectionLimit[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionNesting", - "outputs": [ - { - "components": [ - { "internalType": "bool", "name": "token_owner", "type": "bool" }, - { - "internalType": "bool", - "name": "collection_admin", - "type": "bool" - }, - { - "internalType": "address[]", - "name": "restricted", - "type": "address[]" - } - ], - "internalType": "struct CollectionNestingAndPermission", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionOwner", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string[]", "name": "keys", "type": "string[]" } - ], - "name": "collectionProperties", - "outputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], - "name": "collectionProperty", - "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionSponsor", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "confirmCollectionSponsorship", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "contractAddress", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string[]", "name": "keys", "type": "string[]" } - ], - "name": "deleteCollectionProperties", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "description", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "hasCollectionPendingSponsor", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "isOwnerOrAdminCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "mint", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "internalType": "struct AmountForAddress[]", - "name": "amounts", - "type": "tuple[]" - } - ], - "name": "mintBulk", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "mintCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "admin", - "type": "tuple" - } - ], - "name": "removeCollectionAdminCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "removeCollectionSponsor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "removeFromCollectionAllowListCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" } - ], - "name": "setCollectionAccess", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "enum CollectionLimitField", - "name": "field", - "type": "uint8" - }, - { - "components": [ - { "internalType": "bool", "name": "status", "type": "bool" }, - { "internalType": "uint256", "name": "value", "type": "uint256" } - ], - "internalType": "struct OptionUint256", - "name": "value", - "type": "tuple" - } - ], - "internalType": "struct CollectionLimit", - "name": "limit", - "type": "tuple" - } - ], - "name": "setCollectionLimit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }], - "name": "setCollectionMintMode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "bool", "name": "token_owner", "type": "bool" }, - { - "internalType": "bool", - "name": "collection_admin", - "type": "bool" - }, - { - "internalType": "address[]", - "name": "restricted", - "type": "address[]" - } - ], - "internalType": "struct CollectionNestingAndPermission", - "name": "collectionNestingAndPermissions", - "type": "tuple" - } - ], - "name": "setCollectionNesting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "properties", - "type": "tuple[]" - } - ], - "name": "setCollectionProperties", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "sponsor", - "type": "tuple" - } - ], - "name": "setCollectionSponsorCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } - ], - "name": "supportsInterface", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transfer", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transferCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transferFrom", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "from", - "type": "tuple" - }, - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transferFromCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "uniqueCollectionType", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - } -] --- a/js-packages/tests/src/eth/abi/fungibleDeprecated.json +++ /dev/null @@ -1,151 +0,0 @@ -[ - { - "inputs": [ - { "internalType": "address", "name": "newAdmin", "type": "address" } - ], - "name": "addCollectionAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" } - ], - "name": "addToCollectionAllowList", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "burnFrom", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "newOwner", "type": "address" } - ], - "name": "changeCollectionOwner", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], - "name": "deleteCollectionProperty", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" } - ], - "name": "isOwnerOrAdmin", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "admin", "type": "address" } - ], - "name": "removeCollectionAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" } - ], - "name": "removeFromCollectionAllowList", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "name": "setCollectionProperty", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "sponsor", "type": "address" } - ], - "name": "setCollectionSponsor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "collectionNestingPermissions", - "outputs": [ - { - "components": [ - { - "internalType": "enum CollectionPermissionField", - "name": "field", - "type": "uint8" - }, - { "internalType": "bool", "name": "value", "type": "bool" } - ], - "internalType": "struct CollectionNestingPermission[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionNestingRestrictedCollectionIds", - "outputs": [ - { - "components": [ - { "internalType": "bool", "name": "token_owner", "type": "bool" }, - { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" } - ], - "internalType": "struct CollectionNesting", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }], - "name": "setCollectionNesting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bool", "name": "enable", "type": "bool" }, - { - "internalType": "address[]", - "name": "collections", - "type": "address[]" - } - ], - "name": "setCollectionNesting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] --- a/js-packages/tests/src/eth/abi/nativeFungible.json +++ /dev/null @@ -1,201 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" }, - { "internalType": "address", "name": "spender", "type": "address" } - ], - "name": "allowance", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "spender", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "approve", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" } - ], - "name": "balanceOf", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "owner", - "type": "tuple" - } - ], - "name": "balanceOfCross", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } - ], - "name": "supportsInterface", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transfer", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transferCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transferFrom", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "from", - "type": "tuple" - }, - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transferFromCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - } -] --- a/js-packages/tests/src/eth/abi/nonFungible.json +++ /dev/null @@ -1,988 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "approved", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "operator", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "approved", - "type": "bool" - } - ], - "name": "ApprovalForAll", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "TokenChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "newAdmin", - "type": "tuple" - } - ], - "name": "addCollectionAdminCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "addToCollectionAllowListCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "allowlistedCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "approved", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "approve", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "approved", - "type": "tuple" - }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "approveCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" } - ], - "name": "balanceOf", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "owner", - "type": "tuple" - } - ], - "name": "balanceOfCross", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "burn", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "from", - "type": "tuple" - }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "burnFromCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "newOwner", - "type": "tuple" - } - ], - "name": "changeCollectionOwnerCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "collectionAdmins", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionHelperAddress", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionLimits", - "outputs": [ - { - "components": [ - { - "internalType": "enum CollectionLimitField", - "name": "field", - "type": "uint8" - }, - { - "components": [ - { "internalType": "bool", "name": "status", "type": "bool" }, - { "internalType": "uint256", "name": "value", "type": "uint256" } - ], - "internalType": "struct OptionUint256", - "name": "value", - "type": "tuple" - } - ], - "internalType": "struct CollectionLimit[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionNesting", - "outputs": [ - { - "components": [ - { "internalType": "bool", "name": "token_owner", "type": "bool" }, - { - "internalType": "bool", - "name": "collection_admin", - "type": "bool" - }, - { - "internalType": "address[]", - "name": "restricted", - "type": "address[]" - } - ], - "internalType": "struct CollectionNestingAndPermission", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionOwner", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string[]", "name": "keys", "type": "string[]" } - ], - "name": "collectionProperties", - "outputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], - "name": "collectionProperty", - "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionSponsor", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "confirmCollectionSponsorship", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "contractAddress", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string[]", "name": "keys", "type": "string[]" } - ], - "name": "deleteCollectionProperties", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "string[]", "name": "keys", "type": "string[]" } - ], - "name": "deleteProperties", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "description", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "getApproved", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "hasCollectionPendingSponsor", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" }, - { "internalType": "address", "name": "operator", "type": "address" } - ], - "name": "isApprovedForAll", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "isOwnerOrAdminCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "to", "type": "address" }], - "name": "mint", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "owner", - "type": "tuple" - }, - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "properties", - "type": "tuple[]" - } - ], - "internalType": "struct MintTokenData[]", - "name": "data", - "type": "tuple[]" - } - ], - "name": "mintBulkCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "properties", - "type": "tuple[]" - } - ], - "name": "mintCross", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "string", "name": "tokenUri", "type": "string" } - ], - "name": "mintWithTokenURI", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "nextTokenId", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "ownerOf", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "ownerOfCross", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "string[]", "name": "keys", "type": "string[]" } - ], - "name": "properties", - "outputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "string", "name": "key", "type": "string" } - ], - "name": "property", - "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "admin", - "type": "tuple" - } - ], - "name": "removeCollectionAdminCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "removeCollectionSponsor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "removeFromCollectionAllowListCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "safeTransferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "bytes", "name": "data", "type": "bytes" } - ], - "name": "safeTransferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "operator", "type": "address" }, - { "internalType": "bool", "name": "approved", "type": "bool" } - ], - "name": "setApprovalForAll", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" } - ], - "name": "setCollectionAccess", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "enum CollectionLimitField", - "name": "field", - "type": "uint8" - }, - { - "components": [ - { "internalType": "bool", "name": "status", "type": "bool" }, - { "internalType": "uint256", "name": "value", "type": "uint256" } - ], - "internalType": "struct OptionUint256", - "name": "value", - "type": "tuple" - } - ], - "internalType": "struct CollectionLimit", - "name": "limit", - "type": "tuple" - } - ], - "name": "setCollectionLimit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }], - "name": "setCollectionMintMode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "bool", "name": "token_owner", "type": "bool" }, - { - "internalType": "bool", - "name": "collection_admin", - "type": "bool" - }, - { - "internalType": "address[]", - "name": "restricted", - "type": "address[]" - } - ], - "internalType": "struct CollectionNestingAndPermission", - "name": "collectionNestingAndPermissions", - "type": "tuple" - } - ], - "name": "setCollectionNesting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "properties", - "type": "tuple[]" - } - ], - "name": "setCollectionProperties", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "sponsor", - "type": "tuple" - } - ], - "name": "setCollectionSponsorCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "properties", - "type": "tuple[]" - } - ], - "name": "setProperties", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { - "components": [ - { - "internalType": "enum TokenPermissionField", - "name": "code", - "type": "uint8" - }, - { "internalType": "bool", "name": "value", "type": "bool" } - ], - "internalType": "struct PropertyPermission[]", - "name": "permissions", - "type": "tuple[]" - } - ], - "internalType": "struct TokenPropertyPermission[]", - "name": "permissions", - "type": "tuple[]" - } - ], - "name": "setTokenPropertyPermissions", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } - ], - "name": "supportsInterface", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "index", "type": "uint256" } - ], - "name": "tokenByIndex", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" }, - { "internalType": "uint256", "name": "index", "type": "uint256" } - ], - "name": "tokenOfOwnerByIndex", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "tokenPropertyPermissions", - "outputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { - "components": [ - { - "internalType": "enum TokenPermissionField", - "name": "code", - "type": "uint8" - }, - { "internalType": "bool", "name": "value", "type": "bool" } - ], - "internalType": "struct PropertyPermission[]", - "name": "permissions", - "type": "tuple[]" - } - ], - "internalType": "struct TokenPropertyPermission[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "tokenURI", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "transfer", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "transferCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "transferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "from", - "type": "tuple" - }, - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "transferFromCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "uniqueCollectionType", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - } -] --- a/js-packages/tests/src/eth/abi/nonFungibleDeprecated.json +++ /dev/null @@ -1,172 +0,0 @@ -[ - { - "inputs": [ - { "internalType": "address", "name": "newAdmin", "type": "address" } - ], - "name": "addCollectionAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" } - ], - "name": "addToCollectionAllowList", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "burnFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], - "name": "deleteCollectionProperty", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" } - ], - "name": "isOwnerOrAdmin", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "admin", "type": "address" } - ], - "name": "removeCollectionAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" } - ], - "name": "removeFromCollectionAllowList", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "name": "setCollectionProperty", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "sponsor", "type": "address" } - ], - "name": "setCollectionSponsor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "name": "setProperty", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "newOwner", "type": "address" } - ], - "name": "changeCollectionOwner", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "string", "name": "key", "type": "string" } - ], - "name": "deleteProperty", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "collectionNestingPermissions", - "outputs": [ - { - "components": [ - { - "internalType": "enum CollectionPermissionField", - "name": "field", - "type": "uint8" - }, - { "internalType": "bool", "name": "value", "type": "bool" } - ], - "internalType": "struct CollectionNestingPermission[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionNestingRestrictedCollectionIds", - "outputs": [ - { - "components": [ - { "internalType": "bool", "name": "token_owner", "type": "bool" }, - { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" } - ], - "internalType": "struct CollectionNesting", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }], - "name": "setCollectionNesting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bool", "name": "enable", "type": "bool" }, - { - "internalType": "address[]", - "name": "collections", - "type": "address[]" - } - ], - "name": "setCollectionNesting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] --- a/js-packages/tests/src/eth/abi/reFungible.json +++ /dev/null @@ -1,995 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "approved", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "operator", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "approved", - "type": "bool" - } - ], - "name": "ApprovalForAll", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "TokenChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "newAdmin", - "type": "tuple" - } - ], - "name": "addCollectionAdminCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "addToCollectionAllowListCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "allowlistedCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "approved", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "approve", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" } - ], - "name": "balanceOf", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "owner", - "type": "tuple" - } - ], - "name": "balanceOfCross", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "burn", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "from", - "type": "tuple" - }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "burnFromCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "newOwner", - "type": "tuple" - } - ], - "name": "changeCollectionOwnerCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "collectionAdmins", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionHelperAddress", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionLimits", - "outputs": [ - { - "components": [ - { - "internalType": "enum CollectionLimitField", - "name": "field", - "type": "uint8" - }, - { - "components": [ - { "internalType": "bool", "name": "status", "type": "bool" }, - { "internalType": "uint256", "name": "value", "type": "uint256" } - ], - "internalType": "struct OptionUint256", - "name": "value", - "type": "tuple" - } - ], - "internalType": "struct CollectionLimit[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionNesting", - "outputs": [ - { - "components": [ - { "internalType": "bool", "name": "token_owner", "type": "bool" }, - { - "internalType": "bool", - "name": "collection_admin", - "type": "bool" - }, - { - "internalType": "address[]", - "name": "restricted", - "type": "address[]" - } - ], - "internalType": "struct CollectionNestingAndPermission", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionOwner", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string[]", "name": "keys", "type": "string[]" } - ], - "name": "collectionProperties", - "outputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], - "name": "collectionProperty", - "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionSponsor", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "confirmCollectionSponsorship", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "contractAddress", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string[]", "name": "keys", "type": "string[]" } - ], - "name": "deleteCollectionProperties", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "string[]", "name": "keys", "type": "string[]" } - ], - "name": "deleteProperties", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "description", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "getApproved", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "hasCollectionPendingSponsor", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" }, - { "internalType": "address", "name": "operator", "type": "address" } - ], - "name": "isApprovedForAll", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "isOwnerOrAdminCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "to", "type": "address" }], - "name": "mint", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "components": [ - { - "internalType": "address", - "name": "eth", - "type": "address" - }, - { - "internalType": "uint256", - "name": "sub", - "type": "uint256" - } - ], - "internalType": "struct CrossAddress", - "name": "owner", - "type": "tuple" - }, - { "internalType": "uint128", "name": "pieces", "type": "uint128" } - ], - "internalType": "struct OwnerPieces[]", - "name": "owners", - "type": "tuple[]" - }, - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "properties", - "type": "tuple[]" - } - ], - "internalType": "struct MintTokenData[]", - "name": "tokenProperties", - "type": "tuple[]" - } - ], - "name": "mintBulkCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "properties", - "type": "tuple[]" - } - ], - "name": "mintCross", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "string", "name": "tokenUri", "type": "string" } - ], - "name": "mintWithTokenURI", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "nextTokenId", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "ownerOf", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "ownerOfCross", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "string[]", "name": "keys", "type": "string[]" } - ], - "name": "properties", - "outputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "string", "name": "key", "type": "string" } - ], - "name": "property", - "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "admin", - "type": "tuple" - } - ], - "name": "removeCollectionAdminCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "removeCollectionSponsor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "user", - "type": "tuple" - } - ], - "name": "removeFromCollectionAllowListCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "safeTransferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "bytes", "name": "data", "type": "bytes" } - ], - "name": "safeTransferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "operator", "type": "address" }, - { "internalType": "bool", "name": "approved", "type": "bool" } - ], - "name": "setApprovalForAll", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" } - ], - "name": "setCollectionAccess", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "enum CollectionLimitField", - "name": "field", - "type": "uint8" - }, - { - "components": [ - { "internalType": "bool", "name": "status", "type": "bool" }, - { "internalType": "uint256", "name": "value", "type": "uint256" } - ], - "internalType": "struct OptionUint256", - "name": "value", - "type": "tuple" - } - ], - "internalType": "struct CollectionLimit", - "name": "limit", - "type": "tuple" - } - ], - "name": "setCollectionLimit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }], - "name": "setCollectionMintMode", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "bool", "name": "token_owner", "type": "bool" }, - { - "internalType": "bool", - "name": "collection_admin", - "type": "bool" - }, - { - "internalType": "address[]", - "name": "restricted", - "type": "address[]" - } - ], - "internalType": "struct CollectionNestingAndPermission", - "name": "collectionNestingAndPermissions", - "type": "tuple" - } - ], - "name": "setCollectionNesting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "properties", - "type": "tuple[]" - } - ], - "name": "setCollectionProperties", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "sponsor", - "type": "tuple" - } - ], - "name": "setCollectionSponsorCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "internalType": "struct Property[]", - "name": "properties", - "type": "tuple[]" - } - ], - "name": "setProperties", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { - "components": [ - { - "internalType": "enum TokenPermissionField", - "name": "code", - "type": "uint8" - }, - { "internalType": "bool", "name": "value", "type": "bool" } - ], - "internalType": "struct PropertyPermission[]", - "name": "permissions", - "type": "tuple[]" - } - ], - "internalType": "struct TokenPropertyPermission[]", - "name": "permissions", - "type": "tuple[]" - } - ], - "name": "setTokenPropertyPermissions", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } - ], - "name": "supportsInterface", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "index", "type": "uint256" } - ], - "name": "tokenByIndex", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "token", "type": "uint256" } - ], - "name": "tokenContractAddress", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" }, - { "internalType": "uint256", "name": "index", "type": "uint256" } - ], - "name": "tokenOfOwnerByIndex", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "tokenPropertyPermissions", - "outputs": [ - { - "components": [ - { "internalType": "string", "name": "key", "type": "string" }, - { - "components": [ - { - "internalType": "enum TokenPermissionField", - "name": "code", - "type": "uint8" - }, - { "internalType": "bool", "name": "value", "type": "bool" } - ], - "internalType": "struct PropertyPermission[]", - "name": "permissions", - "type": "tuple[]" - } - ], - "internalType": "struct TokenPropertyPermission[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "tokenURI", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "transfer", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "transferCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "transferFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "from", - "type": "tuple" - }, - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "transferFromCross", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "uniqueCollectionType", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - } -] --- a/js-packages/tests/src/eth/abi/reFungibleDeprecated.json +++ /dev/null @@ -1,200 +0,0 @@ -[ - { - "inputs": [ - { "internalType": "address", "name": "newAdmin", "type": "address" } - ], - "name": "addCollectionAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" } - ], - "name": "addToCollectionAllowList", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "uint256", "name": "tokenId", "type": "uint256" } - ], - "name": "burnFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], - "name": "deleteCollectionProperty", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" } - ], - "name": "isOwnerOrAdmin", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "admin", "type": "address" } - ], - "name": "removeCollectionAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" } - ], - "name": "removeFromCollectionAllowList", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "name": "setCollectionProperty", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "sponsor", "type": "address" } - ], - "name": "setCollectionSponsor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "string", "name": "key", "type": "string" }, - { "internalType": "bytes", "name": "value", "type": "bytes" } - ], - "name": "setProperty", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "newOwner", "type": "address" } - ], - "name": "changeCollectionOwner", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, - { "internalType": "string", "name": "key", "type": "string" } - ], - "name": "deleteProperty", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256[]", "name": "tokenIds", "type": "uint256[]" } - ], - "name": "mintBulk", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "to", "type": "address" }, - { - "components": [ - { "internalType": "uint256", "name": "field_0", "type": "uint256" }, - { "internalType": "string", "name": "field_1", "type": "string" } - ], - "internalType": "struct Tuple0[]", - "name": "tokens", - "type": "tuple[]" - } - ], - "name": "mintBulkWithTokenURI", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "collectionNestingPermissions", - "outputs": [ - { - "components": [ - { - "internalType": "enum CollectionPermissionField", - "name": "field", - "type": "uint8" - }, - { "internalType": "bool", "name": "value", "type": "bool" } - ], - "internalType": "struct CollectionNestingPermission[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "collectionNestingRestrictedCollectionIds", - "outputs": [ - { - "components": [ - { "internalType": "bool", "name": "token_owner", "type": "bool" }, - { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" } - ], - "internalType": "struct CollectionNesting", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }], - "name": "setCollectionNesting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bool", "name": "enable", "type": "bool" }, - { - "internalType": "address[]", - "name": "collections", - "type": "address[]" - } - ], - "name": "setCollectionNesting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] --- a/js-packages/tests/src/eth/abi/reFungibleToken.json +++ /dev/null @@ -1,286 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" }, - { "internalType": "address", "name": "spender", "type": "address" } - ], - "name": "allowance", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "owner", - "type": "tuple" - }, - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "spender", - "type": "tuple" - } - ], - "name": "allowanceCross", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "spender", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "approve", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "spender", - "type": "tuple" - }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "approveCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "owner", "type": "address" } - ], - "name": "balanceOf", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "owner", - "type": "tuple" - } - ], - "name": "balanceOfCross", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "from", - "type": "tuple" - }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "burnFromCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "parentToken", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "parentTokenId", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "repartition", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" } - ], - "name": "supportsInterface", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transfer", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transferCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transferFrom", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "from", - "type": "tuple" - }, - { - "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } - ], - "internalType": "struct CrossAddress", - "name": "to", - "type": "tuple" - }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "transferFromCross", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - } -] --- a/js-packages/tests/src/eth/abi/reFungibleTokenDeprecated.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "inputs": [ - { "internalType": "address", "name": "from", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "burnFrom", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - } -] --- a/js-packages/tests/src/eth/allowlist.test.ts +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {Pallets} from '../util/index.js'; -import {itEth, usingEthPlaygrounds, expect, SponsoringMode} from './util/index.js'; -import {CreateCollectionData} from './util/playgrounds/types.js'; - -describe('EVM contract allowlist', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Contract allowlist can be toggled', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const flipper = await helper.eth.deployFlipper(owner); - const helpers = helper.ethNativeContract.contractHelpers(owner); - - // Any user is allowed by default - expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.false; - - // Enable - await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); - expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.true; - - // Disable - await helpers.methods.toggleAllowlist(flipper.options.address, false).send({from: owner}); - expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.false; - }); - - itEth('Non-allowlisted user can\'t call contract with allowlist enabled', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - const flipper = await helper.eth.deployFlipper(owner); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - - // User can flip with allowlist disabled - await flipper.methods.flip().send({from: caller}); - expect(await flipper.methods.getValue().call()).to.be.true; - - // Tx will be reverted if user is not in allowlist - await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); - await expect(flipper.methods.flip().send({from: caller})).to.rejected; - expect(await flipper.methods.getValue().call()).to.be.true; - - // Adding caller to allowlist will make contract callable again - await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner}); - await flipper.methods.flip().send({from: caller}); - expect(await flipper.methods.getValue().call()).to.be.false; - }); -}); - -describe('EVM collection allowlist', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - // Soft-deprecated - itEth('Collection allowlist can be added and removed by [eth] address', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const user = helper.eth.createAccount(); - const crossUser = helper.ethCrossAccount.fromAddress(user); - - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - - expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false; - await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner}); - expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.true; - - await collectionEvm.methods.removeFromCollectionAllowList(user).send({from: owner}); - expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false; - }); - - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {mode: 'ft' as const, requiredPallets: []}, - ].map(testCase => - itEth.ifWithPallets(`Collection allowlist can be added and removed by [cross] address for ${testCase.mode}`, testCase.requiredPallets, async ({helper}) => { - const owner = (await helper.eth.createAccountWithBalance(donor)).toLowerCase(); - const [userSub] = await helper.arrange.createAccounts([10n], donor); - const userEth = await helper.eth.createAccountWithBalance(donor); - const mintParams = testCase.mode === 'ft' ? [userEth, 100] : [userEth]; - - const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub); - const userCrossEth = helper.ethCrossAccount.fromAddress(userEth); - const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner); - - // Can addToCollectionAllowListCross: - expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false; - await collectionEvm.methods.addToCollectionAllowListCross(userCrossSub).send({from: owner}); - await collectionEvm.methods.addToCollectionAllowListCross(userCrossEth).send({from: owner}); - await collectionEvm.methods.addToCollectionAllowListCross(ownerCrossEth).send({from: owner}); - - // Accounts are in allowed list: - expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true; - expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true; - expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.true; - expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.true; - - await collectionEvm.methods.mint(...mintParams).send({from: owner}); // token #1 - await collectionEvm.methods.mint(...mintParams).send({from: owner}); // token #2 - await collectionEvm.methods.setCollectionAccess(SponsoringMode.Allowlisted).send({from: owner}); - - // allowlisted account can transfer and transferCross from eth: - await collectionEvm.methods.transfer(owner, 1).send({from: userEth}); - await collectionEvm.methods.transferCross(userCrossSub, 2).send({from: userEth}); - - if(testCase.mode === 'ft') { - expect(await helper.ft.getBalance(collectionId, {Ethereum: owner})).to.eq(1n); - expect(await helper.ft.getBalance(collectionId, {Substrate: userSub.address})).to.eq(2n); - } else { - expect(await helper.nft.getTokenOwner(collectionId, 1)).to.deep.eq({Ethereum: owner}); - expect(await helper.nft.getTokenOwner(collectionId, 2)).to.deep.eq({Substrate: userSub.address}); - } - - // allowlisted cross substrate accounts can transfer from Substrate: - testCase.mode === 'ft' - ? await helper.ft.transfer(userSub, collectionId, {Ethereum: userEth}, 2n) - : await helper.collection.transferToken(userSub, collectionId, 2, {Ethereum: userEth}); - - // can removeFromCollectionAllowListCross: - await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossSub).send({from: owner}); - await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossEth).send({from: owner}); - expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false; - expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.false; - expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.false; - expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.false; - - // cannot transfer anymore - await collectionEvm.methods.mint(...mintParams).send({from: owner}); - await expect(collectionEvm.methods.transfer(owner, 2).send({from: userEth})).to.be.rejectedWith(/Transaction has been reverted/); - })); - - [ - // cross-methods - {mode: 'nft' as const, cross: true, requiredPallets: []}, - {mode: 'rft' as const, cross: true, requiredPallets: [Pallets.ReFungible]}, - {mode: 'ft' as const, cross: true, requiredPallets: []}, - // soft-deprecated - {mode: 'nft' as const, cross: false, requiredPallets: []}, - {mode: 'rft' as const, cross: false, requiredPallets: [Pallets.ReFungible]}, - {mode: 'ft' as const, cross: false, requiredPallets: []}, - ].map(testCase => - itEth.ifWithPallets(`Non-owner cannot add or remove from collection allowlist ${testCase.cross ? 'cross ' : ''}${testCase.mode}`, testCase.requiredPallets, async ({helper}) => { - // Select methods: - const addToAllowList = testCase.cross ? 'addToCollectionAllowListCross' : 'addToCollectionAllowList'; - const removeFromAllowList = testCase.cross ? 'removeFromCollectionAllowListCross' : 'removeFromCollectionAllowList'; - - const owner = await helper.eth.createAccountWithBalance(donor); - const notOwner = await helper.eth.createAccountWithBalance(donor); - const userSub = donor; - const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub); - const userEth = helper.eth.createAccount(); - const userCrossEth = helper.ethCrossAccount.fromAddress(userEth); - - const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, !testCase.cross); - - expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false; - expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.false; - - // 1. notOwner cannot add to allow list: - // 1.1 plain ethereum or cross address: - await expect(collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission'); - // 1.2 cross-substrate address: - if(testCase.cross) - await expect(collectionEvm.methods[addToAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission'); - - // 2. owner can add to allow list: - // 2.1 plain ethereum or cross address: - await collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).send({from: owner}); - // 2.2 cross-substrate address: - if(testCase.cross) { - await collectionEvm.methods[addToAllowList](userCrossSub).send({from: owner}); - expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true; - } - expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true; - - // 3. notOwner cannot remove from allow list: - // 3.1 plain ethereum or cross address: - await expect(collectionEvm.methods[removeFromAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission'); - // 3.2 cross-substrate address: - if(testCase.cross) - await expect(collectionEvm.methods[removeFromAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission'); - })); -}); --- a/js-packages/tests/src/eth/api/CollectionHelpers.sol +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-License-Identifier: OTHER -// This code is automatically generated - -pragma solidity >=0.8.0 <0.9.0; - -/// @dev common stubs holder -interface Dummy { - -} - -interface ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) external view returns (bool); -} - -/// @dev inlined interface -interface CollectionHelpersEvents { - event CollectionCreated(address indexed owner, address indexed collectionId); - event CollectionDestroyed(address indexed collectionId); - event CollectionChanged(address indexed collectionId); -} - -/// @title Contract, which allows users to operate with collections -/// @dev the ERC-165 identifier for this interface is 0x94e5af0d -interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents { - /// Create a collection - /// @return address Address of the newly created collection - /// @dev EVM selector for this function is: 0x72b5bea7, - /// or in textual repr: createCollection((string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],(address,uint256),uint8)) - function createCollection(CreateCollectionData memory data) external payable returns (address); - - /// Create an NFT collection - /// @param name Name of the collection - /// @param description Informative description of the collection - /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications - /// @return address Address of the newly created collection - /// @dev EVM selector for this function is: 0x844af658, - /// or in textual repr: createNFTCollection(string,string,string) - function createNFTCollection( - string memory name, - string memory description, - string memory tokenPrefix - ) external payable returns (address); - - // /// Create an NFT collection - // /// @param name Name of the collection - // /// @param description Informative description of the collection - // /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications - // /// @return address Address of the newly created collection - // /// @dev EVM selector for this function is: 0xe34a6844, - // /// or in textual repr: createNonfungibleCollection(string,string,string) - // function createNonfungibleCollection(string memory name, string memory description, string memory tokenPrefix) external payable returns (address); - - /// @dev EVM selector for this function is: 0xab173450, - /// or in textual repr: createRFTCollection(string,string,string) - function createRFTCollection( - string memory name, - string memory description, - string memory tokenPrefix - ) external payable returns (address); - - /// @dev EVM selector for this function is: 0x7335b79f, - /// or in textual repr: createFTCollection(string,uint8,string,string) - function createFTCollection( - string memory name, - uint8 decimals, - string memory description, - string memory tokenPrefix - ) external payable returns (address); - - /// @dev EVM selector for this function is: 0x85624258, - /// or in textual repr: makeCollectionERC721MetadataCompatible(address,string) - function makeCollectionERC721MetadataCompatible(address collection, string memory baseUri) external; - - /// @dev EVM selector for this function is: 0x564e321f, - /// or in textual repr: destroyCollection(address) - function destroyCollection(address collectionAddress) external; - - /// Check if a collection exists - /// @param collectionAddress Address of the collection in question - /// @return bool Does the collection exist? - /// @dev EVM selector for this function is: 0xc3de1494, - /// or in textual repr: isCollectionExist(address) - function isCollectionExist(address collectionAddress) external view returns (bool); - - /// @dev EVM selector for this function is: 0xd23a7ab1, - /// or in textual repr: collectionCreationFee() - function collectionCreationFee() external view returns (uint256); - - /// Returns address of a collection. - /// @param collectionId - CollectionId of the collection - /// @return eth mirror address of the collection - /// @dev EVM selector for this function is: 0x2e716683, - /// or in textual repr: collectionAddress(uint32) - function collectionAddress(uint32 collectionId) external view returns (address); - - /// Returns collectionId of a collection. - /// @param collectionAddress - Eth address of the collection - /// @return collectionId of the collection - /// @dev EVM selector for this function is: 0xb5cb7498, - /// or in textual repr: collectionId(address) - function collectionId(address collectionAddress) external view returns (uint32); -} - -/// Collection properties -struct CreateCollectionData { - /// Collection name - string name; - /// Collection description - string description; - /// Token prefix - string token_prefix; - /// Token type (NFT, FT or RFT) - CollectionMode mode; - /// Fungible token precision - uint8 decimals; - /// Custom Properties - Property[] properties; - /// Permissions for token properties - TokenPropertyPermission[] token_property_permissions; - /// Collection admins - CrossAddress[] admin_list; - /// Nesting settings - CollectionNestingAndPermission nesting_settings; - /// Collection limits - CollectionLimitValue[] limits; - /// Collection sponsor - CrossAddress pending_sponsor; - /// Extra collection flags - CollectionFlags flags; -} - -type CollectionFlags is uint8; - -library CollectionFlagsLib { - /// Tokens in foreign collections can be transferred, but not burnt - CollectionFlags constant foreignField = CollectionFlags.wrap(128); - /// Supports ERC721Metadata - CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64); - /// External collections can't be managed using `unique` api - CollectionFlags constant externalField = CollectionFlags.wrap(1); - - /// Reserved flags - function reservedField(uint8 value) public pure returns (CollectionFlags) { - require(value < 1 << 5, "out of bound value"); - return CollectionFlags.wrap(value << 1); - } -} - -/// Cross account struct -struct CrossAddress { - address eth; - uint256 sub; -} - -/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. -struct CollectionLimitValue { - CollectionLimitField field; - uint256 value; -} - -/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. -enum CollectionLimitField { - /// How many tokens can a user have on one account. - AccountTokenOwnership, - /// How many bytes of data are available for sponsorship. - SponsoredDataSize, - /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] - SponsoredDataRateLimit, - /// How many tokens can be mined into this collection. - TokenLimit, - /// Timeouts for transfer sponsoring. - SponsorTransferTimeout, - /// Timeout for sponsoring an approval in passed blocks. - SponsorApproveTimeout, - /// Whether the collection owner of the collection can send tokens (which belong to other users). - OwnerCanTransfer, - /// Can the collection owner burn other people's tokens. - OwnerCanDestroy, - /// Is it possible to send tokens from this collection between users. - TransferEnabled -} - -/// Nested collections and permissions -struct CollectionNestingAndPermission { - /// Owner of token can nest tokens under it. - bool token_owner; - /// Admin of token collection can nest tokens under token. - bool collection_admin; - /// If set - only tokens from specified collections can be nested. - address[] restricted; -} - -/// Ethereum representation of Token Property Permissions. -struct TokenPropertyPermission { - /// Token property key. - string key; - /// Token property permissions. - PropertyPermission[] permissions; -} - -/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value. -struct PropertyPermission { - /// TokenPermission field. - TokenPermissionField code; - /// TokenPermission value. - bool value; -} - -/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. -enum TokenPermissionField { - /// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] - Mutable, - /// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] - TokenOwner, - /// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] - CollectionAdmin -} - -/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). -struct Property { - string key; - bytes value; -} - -/// Type of tokens in collection -enum CollectionMode { - /// Nonfungible - Nonfungible, - /// Fungible - Fungible, - /// Refungible - Refungible -} --- a/js-packages/tests/src/eth/api/ContractHelpers.sol +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-License-Identifier: OTHER -// This code is automatically generated - -pragma solidity >=0.8.0 <0.9.0; - -/// @dev common stubs holder -interface Dummy { - -} - -interface ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) external view returns (bool); -} - -/// @dev inlined interface -interface ContractHelpersEvents { - event ContractSponsorSet(address indexed contractAddress, address sponsor); - event ContractSponsorshipConfirmed(address indexed contractAddress, address sponsor); - event ContractSponsorRemoved(address indexed contractAddress); -} - -/// @title Magic contract, which allows users to reconfigure other contracts -/// @dev the ERC-165 identifier for this interface is 0x30afad04 -interface ContractHelpers is Dummy, ERC165, ContractHelpersEvents { - /// Get user, which deployed specified contract - /// @dev May return zero address in case if contract is deployed - /// using uniquenetwork evm-migration pallet, or using other terms not - /// intended by pallet-evm - /// @dev Returns zero address if contract does not exists - /// @param contractAddress Contract to get owner of - /// @return address Owner of contract - /// @dev EVM selector for this function is: 0x5152b14c, - /// or in textual repr: contractOwner(address) - function contractOwner(address contractAddress) external view returns (address); - - /// Set sponsor. - /// @param contractAddress Contract for which a sponsor is being established. - /// @param sponsor User address who set as pending sponsor. - /// @dev EVM selector for this function is: 0xf01fba93, - /// or in textual repr: setSponsor(address,address) - function setSponsor(address contractAddress, address sponsor) external; - - /// Set contract as self sponsored. - /// - /// @param contractAddress Contract for which a self sponsoring is being enabled. - /// @dev EVM selector for this function is: 0x89f7d9ae, - /// or in textual repr: selfSponsoredEnable(address) - function selfSponsoredEnable(address contractAddress) external; - - /// Remove sponsor. - /// - /// @param contractAddress Contract for which a sponsorship is being removed. - /// @dev EVM selector for this function is: 0xef784250, - /// or in textual repr: removeSponsor(address) - function removeSponsor(address contractAddress) external; - - /// Confirm sponsorship. - /// - /// @dev Caller must be same that set via [`setSponsor`]. - /// - /// @param contractAddress Сontract for which need to confirm sponsorship. - /// @dev EVM selector for this function is: 0xabc00001, - /// or in textual repr: confirmSponsorship(address) - function confirmSponsorship(address contractAddress) external; - - /// Get current sponsor. - /// - /// @param contractAddress The contract for which a sponsor is requested. - /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0x766c4f37, - /// or in textual repr: sponsor(address) - function sponsor(address contractAddress) external view returns (OptionCrossAddress memory); - - /// Check tat contract has confirmed sponsor. - /// - /// @param contractAddress The contract for which the presence of a confirmed sponsor is checked. - /// @return **true** if contract has confirmed sponsor. - /// @dev EVM selector for this function is: 0x97418603, - /// or in textual repr: hasSponsor(address) - function hasSponsor(address contractAddress) external view returns (bool); - - /// Check tat contract has pending sponsor. - /// - /// @param contractAddress The contract for which the presence of a pending sponsor is checked. - /// @return **true** if contract has pending sponsor. - /// @dev EVM selector for this function is: 0x39b9b242, - /// or in textual repr: hasPendingSponsor(address) - function hasPendingSponsor(address contractAddress) external view returns (bool); - - /// @dev EVM selector for this function is: 0x6027dc61, - /// or in textual repr: sponsoringEnabled(address) - function sponsoringEnabled(address contractAddress) external view returns (bool); - - /// @dev EVM selector for this function is: 0xfde8a560, - /// or in textual repr: setSponsoringMode(address,uint8) - function setSponsoringMode(address contractAddress, SponsoringModeT mode) external; - - /// Get current contract sponsoring rate limit - /// @param contractAddress Contract to get sponsoring rate limit of - /// @return uint32 Amount of blocks between two sponsored transactions - /// @dev EVM selector for this function is: 0xf29694d8, - /// or in textual repr: sponsoringRateLimit(address) - function sponsoringRateLimit(address contractAddress) external view returns (uint32); - - /// Set contract sponsoring rate limit - /// @dev Sponsoring rate limit - is a minimum amount of blocks that should - /// pass between two sponsored transactions - /// @param contractAddress Contract to change sponsoring rate limit of - /// @param rateLimit Target rate limit - /// @dev Only contract owner can change this setting - /// @dev EVM selector for this function is: 0x77b6c908, - /// or in textual repr: setSponsoringRateLimit(address,uint32) - function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) external; - - /// Set contract sponsoring fee limit - /// @dev Sponsoring fee limit - is maximum fee that could be spent by - /// single transaction - /// @param contractAddress Contract to change sponsoring fee limit of - /// @param feeLimit Fee limit - /// @dev Only contract owner can change this setting - /// @dev EVM selector for this function is: 0x03aed665, - /// or in textual repr: setSponsoringFeeLimit(address,uint256) - function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) external; - - /// Get current contract sponsoring fee limit - /// @param contractAddress Contract to get sponsoring fee limit of - /// @return uint256 Maximum amount of fee that could be spent by single - /// transaction - /// @dev EVM selector for this function is: 0x75b73606, - /// or in textual repr: sponsoringFeeLimit(address) - function sponsoringFeeLimit(address contractAddress) external view returns (uint256); - - /// Is specified user present in contract allow list - /// @dev Contract owner always implicitly included - /// @param contractAddress Contract to check allowlist of - /// @param user User to check - /// @return bool Is specified users exists in contract allowlist - /// @dev EVM selector for this function is: 0x5c658165, - /// or in textual repr: allowed(address,address) - function allowed(address contractAddress, address user) external view returns (bool); - - /// Toggle user presence in contract allowlist - /// @param contractAddress Contract to change allowlist of - /// @param user Which user presence should be toggled - /// @param isAllowed `true` if user should be allowed to be sponsored - /// or call this contract, `false` otherwise - /// @dev Only contract owner can change this setting - /// @dev EVM selector for this function is: 0x4706cc1c, - /// or in textual repr: toggleAllowed(address,address,bool) - function toggleAllowed( - address contractAddress, - address user, - bool isAllowed - ) external; - - /// Is this contract has allowlist access enabled - /// @dev Allowlist always can have users, and it is used for two purposes: - /// in case of allowlist sponsoring mode, users will be sponsored if they exist in allowlist - /// in case of allowlist access enabled, only users from allowlist may call this contract - /// @param contractAddress Contract to get allowlist access of - /// @return bool Is specified contract has allowlist access enabled - /// @dev EVM selector for this function is: 0xc772ef6c, - /// or in textual repr: allowlistEnabled(address) - function allowlistEnabled(address contractAddress) external view returns (bool); - - /// Toggle contract allowlist access - /// @param contractAddress Contract to change allowlist access of - /// @param enabled Should allowlist access to be enabled? - /// @dev EVM selector for this function is: 0x36de20f5, - /// or in textual repr: toggleAllowlist(address,bool) - function toggleAllowlist(address contractAddress, bool enabled) external; -} - -/// Available contract sponsoring modes -enum SponsoringModeT { - /// Sponsoring is disabled - Disabled, - /// Only users from allowlist will be sponsored - Allowlisted, - /// All users will be sponsored - Generous -} - -/// Optional value -struct OptionCrossAddress { - /// Shows the status of accessibility of value - bool status; - /// Actual value if `status` is true - CrossAddress value; -} - -/// Cross account struct -struct CrossAddress { - address eth; - uint256 sub; -} --- a/js-packages/tests/src/eth/api/UniqueFungible.sol +++ /dev/null @@ -1,510 +0,0 @@ -// SPDX-License-Identifier: OTHER -// This code is automatically generated - -pragma solidity >=0.8.0 <0.9.0; - -/// @dev common stubs holder -interface Dummy { - -} - -interface ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) external view returns (bool); -} - -/// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xb34d97e9 -interface Collection is Dummy, ERC165 { - // /// Set collection property. - // /// - // /// @param key Property key. - // /// @param value Propery value. - // /// @dev EVM selector for this function is: 0x2f073f66, - // /// or in textual repr: setCollectionProperty(string,bytes) - // function setCollectionProperty(string memory key, bytes memory value) external; - - /// Set collection properties. - /// - /// @param properties Vector of properties key/value pair. - /// @dev EVM selector for this function is: 0x50b26b2a, - /// or in textual repr: setCollectionProperties((string,bytes)[]) - function setCollectionProperties(Property[] memory properties) external; - - // /// Delete collection property. - // /// - // /// @param key Property key. - // /// @dev EVM selector for this function is: 0x7b7debce, - // /// or in textual repr: deleteCollectionProperty(string) - // function deleteCollectionProperty(string memory key) external; - - /// Delete collection properties. - /// - /// @param keys Properties keys. - /// @dev EVM selector for this function is: 0xee206ee3, - /// or in textual repr: deleteCollectionProperties(string[]) - function deleteCollectionProperties(string[] memory keys) external; - - /// Get collection property. - /// - /// @dev Throws error if key not found. - /// - /// @param key Property key. - /// @return bytes The property corresponding to the key. - /// @dev EVM selector for this function is: 0xcf24fd6d, - /// or in textual repr: collectionProperty(string) - function collectionProperty(string memory key) external view returns (bytes memory); - - /// Get collection properties. - /// - /// @param keys Properties keys. Empty keys for all propertyes. - /// @return Vector of properties key/value pairs. - /// @dev EVM selector for this function is: 0x285fb8e6, - /// or in textual repr: collectionProperties(string[]) - function collectionProperties(string[] memory keys) external view returns (Property[] memory); - - // /// Set the sponsor of the collection. - // /// - // /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. - // /// - // /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract. - // /// @dev EVM selector for this function is: 0x7623402e, - // /// or in textual repr: setCollectionSponsor(address) - // function setCollectionSponsor(address sponsor) external; - - /// Set the sponsor of the collection. - /// - /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. - /// - /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. - /// @dev EVM selector for this function is: 0x84a1d5a8, - /// or in textual repr: setCollectionSponsorCross((address,uint256)) - function setCollectionSponsorCross(CrossAddress memory sponsor) external; - - /// Whether there is a pending sponsor. - /// @dev EVM selector for this function is: 0x058ac185, - /// or in textual repr: hasCollectionPendingSponsor() - function hasCollectionPendingSponsor() external view returns (bool); - - /// Collection sponsorship confirmation. - /// - /// @dev After setting the sponsor for the collection, it must be confirmed with this function. - /// @dev EVM selector for this function is: 0x3c50e97a, - /// or in textual repr: confirmCollectionSponsorship() - function confirmCollectionSponsorship() external; - - /// Remove collection sponsor. - /// @dev EVM selector for this function is: 0x6e0326a3, - /// or in textual repr: removeCollectionSponsor() - function removeCollectionSponsor() external; - - /// Get current sponsor. - /// - /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0x6ec0a9f1, - /// or in textual repr: collectionSponsor() - function collectionSponsor() external view returns (CrossAddress memory); - - /// Get current collection limits. - /// - /// @return Array of collection limits - /// @dev EVM selector for this function is: 0xf63bc572, - /// or in textual repr: collectionLimits() - function collectionLimits() external view returns (CollectionLimit[] memory); - - /// Set limits for the collection. - /// @dev Throws error if limit not found. - /// @param limit Some limit. - /// @dev EVM selector for this function is: 0x2316ee74, - /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) - function setCollectionLimit(CollectionLimit memory limit) external; - - /// Get contract address. - /// @dev EVM selector for this function is: 0xf6b4dfb4, - /// or in textual repr: contractAddress() - function contractAddress() external view returns (address); - - /// Add collection admin. - /// @param newAdmin Cross account administrator address. - /// @dev EVM selector for this function is: 0x859aa7d6, - /// or in textual repr: addCollectionAdminCross((address,uint256)) - function addCollectionAdminCross(CrossAddress memory newAdmin) external; - - /// Remove collection admin. - /// @param admin Cross account administrator address. - /// @dev EVM selector for this function is: 0x6c0cd173, - /// or in textual repr: removeCollectionAdminCross((address,uint256)) - function removeCollectionAdminCross(CrossAddress memory admin) external; - - // /// Add collection admin. - // /// @param newAdmin Address of the added administrator. - // /// @dev EVM selector for this function is: 0x92e462c7, - // /// or in textual repr: addCollectionAdmin(address) - // function addCollectionAdmin(address newAdmin) external; - - // /// Remove collection admin. - // /// - // /// @param admin Address of the removed administrator. - // /// @dev EVM selector for this function is: 0xfafd7b42, - // /// or in textual repr: removeCollectionAdmin(address) - // function removeCollectionAdmin(address admin) external; - - /// @dev EVM selector for this function is: 0x0b9f3890, - /// or in textual repr: setCollectionNesting((bool,bool,address[])) - function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external; - - // /// Toggle accessibility of collection nesting. - // /// - // /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled' - // /// @dev EVM selector for this function is: 0x112d4586, - // /// or in textual repr: setCollectionNesting(bool) - // function setCollectionNesting(bool enable) external; - - // /// Toggle accessibility of collection nesting. - // /// - // /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled' - // /// @param collections Addresses of collections that will be available for nesting. - // /// @dev EVM selector for this function is: 0x64872396, - // /// or in textual repr: setCollectionNesting(bool,address[]) - // function setCollectionNesting(bool enable, address[] memory collections) external; - - /// @dev EVM selector for this function is: 0x92c660a8, - /// or in textual repr: collectionNesting() - function collectionNesting() external view returns (CollectionNestingAndPermission memory); - - // /// Returns nesting for a collection - // /// @dev EVM selector for this function is: 0x22d25bfe, - // /// or in textual repr: collectionNestingRestrictedCollectionIds() - // function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory); - - // /// Returns permissions for a collection - // /// @dev EVM selector for this function is: 0x5b2eaf4b, - // /// or in textual repr: collectionNestingPermissions() - // function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory); - - /// Set the collection access method. - /// @param mode Access mode - /// @dev EVM selector for this function is: 0x41835d4c, - /// or in textual repr: setCollectionAccess(uint8) - function setCollectionAccess(AccessMode mode) external; - - /// Checks that user allowed to operate with collection. - /// - /// @param user User address to check. - /// @dev EVM selector for this function is: 0x91b6df49, - /// or in textual repr: allowlistedCross((address,uint256)) - function allowlistedCross(CrossAddress memory user) external view returns (bool); - - // /// Add the user to the allowed list. - // /// - // /// @param user Address of a trusted user. - // /// @dev EVM selector for this function is: 0x67844fe6, - // /// or in textual repr: addToCollectionAllowList(address) - // function addToCollectionAllowList(address user) external; - - /// Add user to allowed list. - /// - /// @param user User cross account address. - /// @dev EVM selector for this function is: 0xa0184a3a, - /// or in textual repr: addToCollectionAllowListCross((address,uint256)) - function addToCollectionAllowListCross(CrossAddress memory user) external; - - // /// Remove the user from the allowed list. - // /// - // /// @param user Address of a removed user. - // /// @dev EVM selector for this function is: 0x85c51acb, - // /// or in textual repr: removeFromCollectionAllowList(address) - // function removeFromCollectionAllowList(address user) external; - - /// Remove user from allowed list. - /// - /// @param user User cross account address. - /// @dev EVM selector for this function is: 0x09ba452a, - /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) - function removeFromCollectionAllowListCross(CrossAddress memory user) external; - - /// Switch permission for minting. - /// - /// @param mode Enable if "true". - /// @dev EVM selector for this function is: 0x00018e84, - /// or in textual repr: setCollectionMintMode(bool) - function setCollectionMintMode(bool mode) external; - - // /// Check that account is the owner or admin of the collection - // /// - // /// @param user account to verify - // /// @return "true" if account is the owner or admin - // /// @dev EVM selector for this function is: 0x9811b0c7, - // /// or in textual repr: isOwnerOrAdmin(address) - // function isOwnerOrAdmin(address user) external view returns (bool); - - /// Check that account is the owner or admin of the collection - /// - /// @param user User cross account to verify - /// @return "true" if account is the owner or admin - /// @dev EVM selector for this function is: 0x3e75a905, - /// or in textual repr: isOwnerOrAdminCross((address,uint256)) - function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool); - - /// Returns collection type - /// - /// @return `Fungible` or `NFT` or `ReFungible` - /// @dev EVM selector for this function is: 0xd34b55b8, - /// or in textual repr: uniqueCollectionType() - function uniqueCollectionType() external view returns (string memory); - - /// Get collection owner. - /// - /// @return Tuble with sponsor address and his substrate mirror. - /// If address is canonical then substrate mirror is zero and vice versa. - /// @dev EVM selector for this function is: 0xdf727d3b, - /// or in textual repr: collectionOwner() - function collectionOwner() external view returns (CrossAddress memory); - - // /// Changes collection owner to another account - // /// - // /// @dev Owner can be changed only by current owner - // /// @param newOwner new owner account - // /// @dev EVM selector for this function is: 0x4f53e226, - // /// or in textual repr: changeCollectionOwner(address) - // function changeCollectionOwner(address newOwner) external; - - /// Get collection administrators - /// - /// @return Vector of tuples with admins address and his substrate mirror. - /// If address is canonical then substrate mirror is zero and vice versa. - /// @dev EVM selector for this function is: 0x5813216b, - /// or in textual repr: collectionAdmins() - function collectionAdmins() external view returns (CrossAddress[] memory); - - /// Changes collection owner to another account - /// - /// @dev Owner can be changed only by current owner - /// @param newOwner new owner cross account - /// @dev EVM selector for this function is: 0x6496c497, - /// or in textual repr: changeCollectionOwnerCross((address,uint256)) - function changeCollectionOwnerCross(CrossAddress memory newOwner) external; -} - -/// Cross account struct -struct CrossAddress { - address eth; - uint256 sub; -} - -/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]). -enum AccessMode { - /// Access grant for owner and admins. Used as default. - Normal, - /// Like a [`Normal`](AccessMode::Normal) but also users in allow list. - AllowList -} - -/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. -struct CollectionNestingPermission { - CollectionPermissionField field; - bool value; -} - -/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. -enum CollectionPermissionField { - /// Owner of token can nest tokens under it. - TokenOwner, - /// Admin of token collection can nest tokens under token. - CollectionAdmin -} - -/// Nested collections. -struct CollectionNesting { - bool token_owner; - uint256[] ids; -} - -/// Nested collections and permissions -struct CollectionNestingAndPermission { - /// Owner of token can nest tokens under it. - bool token_owner; - /// Admin of token collection can nest tokens under token. - bool collection_admin; - /// If set - only tokens from specified collections can be nested. - address[] restricted; -} - -/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. -struct CollectionLimit { - CollectionLimitField field; - OptionUint256 value; -} - -/// Optional value -struct OptionUint256 { - /// Shows the status of accessibility of value - bool status; - /// Actual value if `status` is true - uint256 value; -} - -/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. -enum CollectionLimitField { - /// How many tokens can a user have on one account. - AccountTokenOwnership, - /// How many bytes of data are available for sponsorship. - SponsoredDataSize, - /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] - SponsoredDataRateLimit, - /// How many tokens can be mined into this collection. - TokenLimit, - /// Timeouts for transfer sponsoring. - SponsorTransferTimeout, - /// Timeout for sponsoring an approval in passed blocks. - SponsorApproveTimeout, - /// Whether the collection owner of the collection can send tokens (which belong to other users). - OwnerCanTransfer, - /// Can the collection owner burn other people's tokens. - OwnerCanDestroy, - /// Is it possible to send tokens from this collection between users. - TransferEnabled -} - -/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). -struct Property { - string key; - bytes value; -} - -/// @dev the ERC-165 identifier for this interface is 0x69d14d3e -interface ERC20UniqueExtensions is Dummy, ERC165 { - /// @dev Function to check the amount of tokens that an owner allowed to a spender. - /// @param owner crossAddress The address which owns the funds. - /// @param spender crossAddress The address which will spend the funds. - /// @return A uint256 specifying the amount of tokens still available for the spender. - /// @dev EVM selector for this function is: 0xe0af4bd7, - /// or in textual repr: allowanceCross((address,uint256),(address,uint256)) - function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) external view returns (uint256); - - /// @notice A description for the collection. - /// @dev EVM selector for this function is: 0x7284e416, - /// or in textual repr: description() - function description() external view returns (string memory); - - /// @dev EVM selector for this function is: 0x269e6158, - /// or in textual repr: mintCross((address,uint256),uint256) - function mintCross(CrossAddress memory to, uint256 amount) external returns (bool); - - /// @dev EVM selector for this function is: 0x0ecd0ab0, - /// or in textual repr: approveCross((address,uint256),uint256) - function approveCross(CrossAddress memory spender, uint256 amount) external returns (bool); - - // /// Burn tokens from account - // /// @dev Function that burns an `amount` of the tokens of a given account, - // /// deducting from the sender's allowance for said account. - // /// @param from The account whose tokens will be burnt. - // /// @param amount The amount that will be burnt. - // /// @dev EVM selector for this function is: 0x79cc6790, - // /// or in textual repr: burnFrom(address,uint256) - // function burnFrom(address from, uint256 amount) external returns (bool); - - /// Burn tokens from account - /// @dev Function that burns an `amount` of the tokens of a given account, - /// deducting from the sender's allowance for said account. - /// @param from The account whose tokens will be burnt. - /// @param amount The amount that will be burnt. - /// @dev EVM selector for this function is: 0xbb2f5a58, - /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(CrossAddress memory from, uint256 amount) external returns (bool); - - /// Mint tokens for multiple accounts. - /// @param amounts array of pairs of account address and amount - /// @dev EVM selector for this function is: 0x1acf2d55, - /// or in textual repr: mintBulk((address,uint256)[]) - function mintBulk(AmountForAddress[] memory amounts) external returns (bool); - - /// @dev EVM selector for this function is: 0x2ada85ff, - /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(CrossAddress memory to, uint256 amount) external returns (bool); - - /// @dev EVM selector for this function is: 0xd5cf430b, - /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) - function transferFromCross( - CrossAddress memory from, - CrossAddress memory to, - uint256 amount - ) external returns (bool); - - /// @notice Returns collection helper contract address - /// @dev EVM selector for this function is: 0x1896cce6, - /// or in textual repr: collectionHelperAddress() - function collectionHelperAddress() external view returns (address); - - /// @notice Balance of account - /// @param owner An cross address for whom to query the balance - /// @return The number of fingibles owned by `owner`, possibly zero - /// @dev EVM selector for this function is: 0xec069398, - /// or in textual repr: balanceOfCross((address,uint256)) - function balanceOfCross(CrossAddress memory owner) external view returns (uint256); -} - -struct AmountForAddress { - address to; - uint256 amount; -} - -/// @dev the ERC-165 identifier for this interface is 0x40c10f19 -interface ERC20Mintable is Dummy, ERC165 { - /// Mint tokens for `to` account. - /// @param to account that will receive minted tokens - /// @param amount amount of tokens to mint - /// @dev EVM selector for this function is: 0x40c10f19, - /// or in textual repr: mint(address,uint256) - function mint(address to, uint256 amount) external returns (bool); -} - -/// @dev inlined interface -interface ERC20Events { - event Transfer(address indexed from, address indexed to, uint256 value); - event Approval(address indexed owner, address indexed spender, uint256 value); -} - -/// @dev the ERC-165 identifier for this interface is 0x942e8b22 -interface ERC20 is Dummy, ERC165, ERC20Events { - /// @dev EVM selector for this function is: 0x06fdde03, - /// or in textual repr: name() - function name() external view returns (string memory); - - /// @dev EVM selector for this function is: 0x95d89b41, - /// or in textual repr: symbol() - function symbol() external view returns (string memory); - - /// @dev EVM selector for this function is: 0x18160ddd, - /// or in textual repr: totalSupply() - function totalSupply() external view returns (uint256); - - /// @dev EVM selector for this function is: 0x313ce567, - /// or in textual repr: decimals() - function decimals() external view returns (uint8); - - /// @dev EVM selector for this function is: 0x70a08231, - /// or in textual repr: balanceOf(address) - function balanceOf(address owner) external view returns (uint256); - - /// @dev EVM selector for this function is: 0xa9059cbb, - /// or in textual repr: transfer(address,uint256) - function transfer(address to, uint256 amount) external returns (bool); - - /// @dev EVM selector for this function is: 0x23b872dd, - /// or in textual repr: transferFrom(address,address,uint256) - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); - - /// @dev EVM selector for this function is: 0x095ea7b3, - /// or in textual repr: approve(address,uint256) - function approve(address spender, uint256 amount) external returns (bool); - - /// @dev EVM selector for this function is: 0xdd62ed3e, - /// or in textual repr: allowance(address,address) - function allowance(address owner, address spender) external view returns (uint256); -} - -interface UniqueFungible is Dummy, ERC165, ERC20, ERC20Mintable, ERC20UniqueExtensions, Collection {} --- a/js-packages/tests/src/eth/api/UniqueNFT.sol +++ /dev/null @@ -1,855 +0,0 @@ -// SPDX-License-Identifier: OTHER -// This code is automatically generated - -pragma solidity >=0.8.0 <0.9.0; - -/// @dev common stubs holder -interface Dummy { - -} - -interface ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) external view returns (bool); -} - -/// @dev inlined interface -interface ERC721TokenEvent { - event TokenChanged(uint256 indexed tokenId); -} - -/// @title A contract that allows to set and delete token properties and change token property permissions. -/// @dev the ERC-165 identifier for this interface is 0xde0695c2 -interface TokenProperties is Dummy, ERC165, ERC721TokenEvent { - // /// @notice Set permissions for token property. - // /// @dev Throws error if `msg.sender` is not admin or owner of the collection. - // /// @param key Property key. - // /// @param isMutable Permission to mutate property. - // /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable. - // /// @param tokenOwner Permission to mutate property by token owner if property is mutable. - // /// @dev EVM selector for this function is: 0x222d97fa, - // /// or in textual repr: setTokenPropertyPermission(string,bool,bool,bool) - // function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) external; - - /// @notice Set permissions for token property. - /// @dev Throws error if `msg.sender` is not admin or owner of the collection. - /// @param permissions Permissions for keys. - /// @dev EVM selector for this function is: 0xbd92983a, - /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) - function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) external; - - /// @notice Get permissions for token properties. - /// @dev EVM selector for this function is: 0xf23d7790, - /// or in textual repr: tokenPropertyPermissions() - function tokenPropertyPermissions() external view returns (TokenPropertyPermission[] memory); - - // /// @notice Set token property value. - // /// @dev Throws error if `msg.sender` has no permission to edit the property. - // /// @param tokenId ID of the token. - // /// @param key Property key. - // /// @param value Property value. - // /// @dev EVM selector for this function is: 0x1752d67b, - // /// or in textual repr: setProperty(uint256,string,bytes) - // function setProperty(uint256 tokenId, string memory key, bytes memory value) external; - - /// @notice Set token properties value. - /// @dev Throws error if `msg.sender` has no permission to edit the property. - /// @param tokenId ID of the token. - /// @param properties settable properties - /// @dev EVM selector for this function is: 0x14ed3a6e, - /// or in textual repr: setProperties(uint256,(string,bytes)[]) - function setProperties(uint256 tokenId, Property[] memory properties) external; - - // /// @notice Delete token property value. - // /// @dev Throws error if `msg.sender` has no permission to edit the property. - // /// @param tokenId ID of the token. - // /// @param key Property key. - // /// @dev EVM selector for this function is: 0x066111d1, - // /// or in textual repr: deleteProperty(uint256,string) - // function deleteProperty(uint256 tokenId, string memory key) external; - - /// @notice Delete token properties value. - /// @dev Throws error if `msg.sender` has no permission to edit the property. - /// @param tokenId ID of the token. - /// @param keys Properties key. - /// @dev EVM selector for this function is: 0xc472d371, - /// or in textual repr: deleteProperties(uint256,string[]) - function deleteProperties(uint256 tokenId, string[] memory keys) external; - - /// @notice Get token property value. - /// @dev Throws error if key not found - /// @param tokenId ID of the token. - /// @param key Property key. - /// @return Property value bytes - /// @dev EVM selector for this function is: 0x7228c327, - /// or in textual repr: property(uint256,string) - function property(uint256 tokenId, string memory key) external view returns (bytes memory); -} - -/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). -struct Property { - string key; - bytes value; -} - -/// Ethereum representation of Token Property Permissions. -struct TokenPropertyPermission { - /// Token property key. - string key; - /// Token property permissions. - PropertyPermission[] permissions; -} - -/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value. -struct PropertyPermission { - /// TokenPermission field. - TokenPermissionField code; - /// TokenPermission value. - bool value; -} - -/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. -enum TokenPermissionField { - /// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] - Mutable, - /// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] - TokenOwner, - /// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] - CollectionAdmin -} - -/// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xb34d97e9 -interface Collection is Dummy, ERC165 { - // /// Set collection property. - // /// - // /// @param key Property key. - // /// @param value Propery value. - // /// @dev EVM selector for this function is: 0x2f073f66, - // /// or in textual repr: setCollectionProperty(string,bytes) - // function setCollectionProperty(string memory key, bytes memory value) external; - - /// Set collection properties. - /// - /// @param properties Vector of properties key/value pair. - /// @dev EVM selector for this function is: 0x50b26b2a, - /// or in textual repr: setCollectionProperties((string,bytes)[]) - function setCollectionProperties(Property[] memory properties) external; - - // /// Delete collection property. - // /// - // /// @param key Property key. - // /// @dev EVM selector for this function is: 0x7b7debce, - // /// or in textual repr: deleteCollectionProperty(string) - // function deleteCollectionProperty(string memory key) external; - - /// Delete collection properties. - /// - /// @param keys Properties keys. - /// @dev EVM selector for this function is: 0xee206ee3, - /// or in textual repr: deleteCollectionProperties(string[]) - function deleteCollectionProperties(string[] memory keys) external; - - /// Get collection property. - /// - /// @dev Throws error if key not found. - /// - /// @param key Property key. - /// @return bytes The property corresponding to the key. - /// @dev EVM selector for this function is: 0xcf24fd6d, - /// or in textual repr: collectionProperty(string) - function collectionProperty(string memory key) external view returns (bytes memory); - - /// Get collection properties. - /// - /// @param keys Properties keys. Empty keys for all propertyes. - /// @return Vector of properties key/value pairs. - /// @dev EVM selector for this function is: 0x285fb8e6, - /// or in textual repr: collectionProperties(string[]) - function collectionProperties(string[] memory keys) external view returns (Property[] memory); - - // /// Set the sponsor of the collection. - // /// - // /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. - // /// - // /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract. - // /// @dev EVM selector for this function is: 0x7623402e, - // /// or in textual repr: setCollectionSponsor(address) - // function setCollectionSponsor(address sponsor) external; - - /// Set the sponsor of the collection. - /// - /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. - /// - /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. - /// @dev EVM selector for this function is: 0x84a1d5a8, - /// or in textual repr: setCollectionSponsorCross((address,uint256)) - function setCollectionSponsorCross(CrossAddress memory sponsor) external; - - /// Whether there is a pending sponsor. - /// @dev EVM selector for this function is: 0x058ac185, - /// or in textual repr: hasCollectionPendingSponsor() - function hasCollectionPendingSponsor() external view returns (bool); - - /// Collection sponsorship confirmation. - /// - /// @dev After setting the sponsor for the collection, it must be confirmed with this function. - /// @dev EVM selector for this function is: 0x3c50e97a, - /// or in textual repr: confirmCollectionSponsorship() - function confirmCollectionSponsorship() external; - - /// Remove collection sponsor. - /// @dev EVM selector for this function is: 0x6e0326a3, - /// or in textual repr: removeCollectionSponsor() - function removeCollectionSponsor() external; - - /// Get current sponsor. - /// - /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0x6ec0a9f1, - /// or in textual repr: collectionSponsor() - function collectionSponsor() external view returns (CrossAddress memory); - - /// Get current collection limits. - /// - /// @return Array of collection limits - /// @dev EVM selector for this function is: 0xf63bc572, - /// or in textual repr: collectionLimits() - function collectionLimits() external view returns (CollectionLimit[] memory); - - /// Set limits for the collection. - /// @dev Throws error if limit not found. - /// @param limit Some limit. - /// @dev EVM selector for this function is: 0x2316ee74, - /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) - function setCollectionLimit(CollectionLimit memory limit) external; - - /// Get contract address. - /// @dev EVM selector for this function is: 0xf6b4dfb4, - /// or in textual repr: contractAddress() - function contractAddress() external view returns (address); - - /// Add collection admin. - /// @param newAdmin Cross account administrator address. - /// @dev EVM selector for this function is: 0x859aa7d6, - /// or in textual repr: addCollectionAdminCross((address,uint256)) - function addCollectionAdminCross(CrossAddress memory newAdmin) external; - - /// Remove collection admin. - /// @param admin Cross account administrator address. - /// @dev EVM selector for this function is: 0x6c0cd173, - /// or in textual repr: removeCollectionAdminCross((address,uint256)) - function removeCollectionAdminCross(CrossAddress memory admin) external; - - // /// Add collection admin. - // /// @param newAdmin Address of the added administrator. - // /// @dev EVM selector for this function is: 0x92e462c7, - // /// or in textual repr: addCollectionAdmin(address) - // function addCollectionAdmin(address newAdmin) external; - - // /// Remove collection admin. - // /// - // /// @param admin Address of the removed administrator. - // /// @dev EVM selector for this function is: 0xfafd7b42, - // /// or in textual repr: removeCollectionAdmin(address) - // function removeCollectionAdmin(address admin) external; - - /// @dev EVM selector for this function is: 0x0b9f3890, - /// or in textual repr: setCollectionNesting((bool,bool,address[])) - function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external; - - // /// Toggle accessibility of collection nesting. - // /// - // /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled' - // /// @dev EVM selector for this function is: 0x112d4586, - // /// or in textual repr: setCollectionNesting(bool) - // function setCollectionNesting(bool enable) external; - - // /// Toggle accessibility of collection nesting. - // /// - // /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled' - // /// @param collections Addresses of collections that will be available for nesting. - // /// @dev EVM selector for this function is: 0x64872396, - // /// or in textual repr: setCollectionNesting(bool,address[]) - // function setCollectionNesting(bool enable, address[] memory collections) external; - - /// @dev EVM selector for this function is: 0x92c660a8, - /// or in textual repr: collectionNesting() - function collectionNesting() external view returns (CollectionNestingAndPermission memory); - - // /// Returns nesting for a collection - // /// @dev EVM selector for this function is: 0x22d25bfe, - // /// or in textual repr: collectionNestingRestrictedCollectionIds() - // function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory); - - // /// Returns permissions for a collection - // /// @dev EVM selector for this function is: 0x5b2eaf4b, - // /// or in textual repr: collectionNestingPermissions() - // function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory); - - /// Set the collection access method. - /// @param mode Access mode - /// @dev EVM selector for this function is: 0x41835d4c, - /// or in textual repr: setCollectionAccess(uint8) - function setCollectionAccess(AccessMode mode) external; - - /// Checks that user allowed to operate with collection. - /// - /// @param user User address to check. - /// @dev EVM selector for this function is: 0x91b6df49, - /// or in textual repr: allowlistedCross((address,uint256)) - function allowlistedCross(CrossAddress memory user) external view returns (bool); - - // /// Add the user to the allowed list. - // /// - // /// @param user Address of a trusted user. - // /// @dev EVM selector for this function is: 0x67844fe6, - // /// or in textual repr: addToCollectionAllowList(address) - // function addToCollectionAllowList(address user) external; - - /// Add user to allowed list. - /// - /// @param user User cross account address. - /// @dev EVM selector for this function is: 0xa0184a3a, - /// or in textual repr: addToCollectionAllowListCross((address,uint256)) - function addToCollectionAllowListCross(CrossAddress memory user) external; - - // /// Remove the user from the allowed list. - // /// - // /// @param user Address of a removed user. - // /// @dev EVM selector for this function is: 0x85c51acb, - // /// or in textual repr: removeFromCollectionAllowList(address) - // function removeFromCollectionAllowList(address user) external; - - /// Remove user from allowed list. - /// - /// @param user User cross account address. - /// @dev EVM selector for this function is: 0x09ba452a, - /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) - function removeFromCollectionAllowListCross(CrossAddress memory user) external; - - /// Switch permission for minting. - /// - /// @param mode Enable if "true". - /// @dev EVM selector for this function is: 0x00018e84, - /// or in textual repr: setCollectionMintMode(bool) - function setCollectionMintMode(bool mode) external; - - // /// Check that account is the owner or admin of the collection - // /// - // /// @param user account to verify - // /// @return "true" if account is the owner or admin - // /// @dev EVM selector for this function is: 0x9811b0c7, - // /// or in textual repr: isOwnerOrAdmin(address) - // function isOwnerOrAdmin(address user) external view returns (bool); - - /// Check that account is the owner or admin of the collection - /// - /// @param user User cross account to verify - /// @return "true" if account is the owner or admin - /// @dev EVM selector for this function is: 0x3e75a905, - /// or in textual repr: isOwnerOrAdminCross((address,uint256)) - function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool); - - /// Returns collection type - /// - /// @return `Fungible` or `NFT` or `ReFungible` - /// @dev EVM selector for this function is: 0xd34b55b8, - /// or in textual repr: uniqueCollectionType() - function uniqueCollectionType() external view returns (string memory); - - /// Get collection owner. - /// - /// @return Tuble with sponsor address and his substrate mirror. - /// If address is canonical then substrate mirror is zero and vice versa. - /// @dev EVM selector for this function is: 0xdf727d3b, - /// or in textual repr: collectionOwner() - function collectionOwner() external view returns (CrossAddress memory); - - // /// Changes collection owner to another account - // /// - // /// @dev Owner can be changed only by current owner - // /// @param newOwner new owner account - // /// @dev EVM selector for this function is: 0x4f53e226, - // /// or in textual repr: changeCollectionOwner(address) - // function changeCollectionOwner(address newOwner) external; - - /// Get collection administrators - /// - /// @return Vector of tuples with admins address and his substrate mirror. - /// If address is canonical then substrate mirror is zero and vice versa. - /// @dev EVM selector for this function is: 0x5813216b, - /// or in textual repr: collectionAdmins() - function collectionAdmins() external view returns (CrossAddress[] memory); - - /// Changes collection owner to another account - /// - /// @dev Owner can be changed only by current owner - /// @param newOwner new owner cross account - /// @dev EVM selector for this function is: 0x6496c497, - /// or in textual repr: changeCollectionOwnerCross((address,uint256)) - function changeCollectionOwnerCross(CrossAddress memory newOwner) external; -} - -/// Cross account struct -struct CrossAddress { - address eth; - uint256 sub; -} - -/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]). -enum AccessMode { - /// Access grant for owner and admins. Used as default. - Normal, - /// Like a [`Normal`](AccessMode::Normal) but also users in allow list. - AllowList -} - -/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. -struct CollectionNestingPermission { - CollectionPermissionField field; - bool value; -} - -/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. -enum CollectionPermissionField { - /// Owner of token can nest tokens under it. - TokenOwner, - /// Admin of token collection can nest tokens under token. - CollectionAdmin -} - -/// Nested collections. -struct CollectionNesting { - bool token_owner; - uint256[] ids; -} - -/// Nested collections and permissions -struct CollectionNestingAndPermission { - /// Owner of token can nest tokens under it. - bool token_owner; - /// Admin of token collection can nest tokens under token. - bool collection_admin; - /// If set - only tokens from specified collections can be nested. - address[] restricted; -} - -/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. -struct CollectionLimit { - CollectionLimitField field; - OptionUint256 value; -} - -/// Optional value -struct OptionUint256 { - /// Shows the status of accessibility of value - bool status; - /// Actual value if `status` is true - uint256 value; -} - -/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. -enum CollectionLimitField { - /// How many tokens can a user have on one account. - AccountTokenOwnership, - /// How many bytes of data are available for sponsorship. - SponsoredDataSize, - /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] - SponsoredDataRateLimit, - /// How many tokens can be mined into this collection. - TokenLimit, - /// Timeouts for transfer sponsoring. - SponsorTransferTimeout, - /// Timeout for sponsoring an approval in passed blocks. - SponsorApproveTimeout, - /// Whether the collection owner of the collection can send tokens (which belong to other users). - OwnerCanTransfer, - /// Can the collection owner burn other people's tokens. - OwnerCanDestroy, - /// Is it possible to send tokens from this collection between users. - TransferEnabled -} - -/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension -/// @dev See https://eips.ethereum.org/EIPS/eip-721 -/// @dev the ERC-165 identifier for this interface is 0x5b5e139f -interface ERC721Metadata is Dummy, ERC165 { - // /// @notice A descriptive name for a collection of NFTs in this contract - // /// @dev real implementation of this function lies in `ERC721UniqueExtensions` - // /// @dev EVM selector for this function is: 0x06fdde03, - // /// or in textual repr: name() - // function name() external view returns (string memory); - - // /// @notice An abbreviated name for NFTs in this contract - // /// @dev real implementation of this function lies in `ERC721UniqueExtensions` - // /// @dev EVM selector for this function is: 0x95d89b41, - // /// or in textual repr: symbol() - // function symbol() external view returns (string memory); - - /// @notice A distinct Uniform Resource Identifier (URI) for a given asset. - /// - /// @dev If the token has a `url` property and it is not empty, it is returned. - /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`. - /// If the collection property `baseURI` is empty or absent, return "" (empty string) - /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix - /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings). - /// - /// @return token's const_metadata - /// @dev EVM selector for this function is: 0xc87b56dd, - /// or in textual repr: tokenURI(uint256) - function tokenURI(uint256 tokenId) external view returns (string memory); -} - -/// @title ERC721 Token that can be irreversibly burned (destroyed). -/// @dev the ERC-165 identifier for this interface is 0x42966c68 -interface ERC721Burnable is Dummy, ERC165 { - /// @notice Burns a specific ERC721 token. - /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized - /// operator of the current owner. - /// @param tokenId The NFT to approve - /// @dev EVM selector for this function is: 0x42966c68, - /// or in textual repr: burn(uint256) - function burn(uint256 tokenId) external; -} - -/// @title ERC721 minting logic. -/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6 -interface ERC721UniqueMintable is Dummy, ERC165 { - /// @notice Function to mint a token. - /// @param to The new owner - /// @return uint256 The id of the newly minted token - /// @dev EVM selector for this function is: 0x6a627842, - /// or in textual repr: mint(address) - function mint(address to) external returns (uint256); - - // /// @notice Function to mint a token. - // /// @dev `tokenId` should be obtained with `nextTokenId` method, - // /// unlike standard, you can't specify it manually - // /// @param to The new owner - // /// @param tokenId ID of the minted NFT - // /// @dev EVM selector for this function is: 0x40c10f19, - // /// or in textual repr: mint(address,uint256) - // function mint(address to, uint256 tokenId) external returns (bool); - - /// @notice Function to mint token with the given tokenUri. - /// @param to The new owner - /// @param tokenUri Token URI that would be stored in the NFT properties - /// @return uint256 The id of the newly minted token - /// @dev EVM selector for this function is: 0x45c17782, - /// or in textual repr: mintWithTokenURI(address,string) - function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256); - // /// @notice Function to mint token with the given tokenUri. - // /// @dev `tokenId` should be obtained with `nextTokenId` method, - // /// unlike standard, you can't specify it manually - // /// @param to The new owner - // /// @param tokenId ID of the minted NFT - // /// @param tokenUri Token URI that would be stored in the NFT properties - // /// @dev EVM selector for this function is: 0x50bb4e7f, - // /// or in textual repr: mintWithTokenURI(address,uint256,string) - // function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool); - -} - -/// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x9b397d16 -interface ERC721UniqueExtensions is Dummy, ERC165 { - /// @notice A descriptive name for a collection of NFTs in this contract - /// @dev EVM selector for this function is: 0x06fdde03, - /// or in textual repr: name() - function name() external view returns (string memory); - - /// @notice An abbreviated name for NFTs in this contract - /// @dev EVM selector for this function is: 0x95d89b41, - /// or in textual repr: symbol() - function symbol() external view returns (string memory); - - /// @notice A description for the collection. - /// @dev EVM selector for this function is: 0x7284e416, - /// or in textual repr: description() - function description() external view returns (string memory); - - // /// Returns the owner (in cross format) of the token. - // /// - // /// @param tokenId Id for the token. - // /// @dev EVM selector for this function is: 0x2b29dace, - // /// or in textual repr: crossOwnerOf(uint256) - // function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory); - - /// Returns the owner (in cross format) of the token. - /// - /// @param tokenId Id for the token. - /// @dev EVM selector for this function is: 0xcaa3a4d0, - /// or in textual repr: ownerOfCross(uint256) - function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory); - - /// @notice Count all NFTs assigned to an owner - /// @param owner An cross address for whom to query the balance - /// @return The number of NFTs owned by `owner`, possibly zero - /// @dev EVM selector for this function is: 0xec069398, - /// or in textual repr: balanceOfCross((address,uint256)) - function balanceOfCross(CrossAddress memory owner) external view returns (uint256); - - /// Returns the token properties. - /// - /// @param tokenId Id for the token. - /// @param keys Properties keys. Empty keys for all propertyes. - /// @return Vector of properties key/value pairs. - /// @dev EVM selector for this function is: 0xe07ede7e, - /// or in textual repr: properties(uint256,string[]) - function properties(uint256 tokenId, string[] memory keys) external view returns (Property[] memory); - - /// @notice Set or reaffirm the approved address for an NFT - /// @dev The zero address indicates there is no approved address. - /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized - /// operator of the current owner. - /// @param approved The new substrate address approved NFT controller - /// @param tokenId The NFT to approve - /// @dev EVM selector for this function is: 0x0ecd0ab0, - /// or in textual repr: approveCross((address,uint256),uint256) - function approveCross(CrossAddress memory approved, uint256 tokenId) external; - - /// @notice Transfer ownership of an NFT - /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` - /// is the zero address. Throws if `tokenId` is not a valid NFT. - /// @param to The new owner - /// @param tokenId The NFT to transfer - /// @dev EVM selector for this function is: 0xa9059cbb, - /// or in textual repr: transfer(address,uint256) - function transfer(address to, uint256 tokenId) external; - - /// @notice Transfer ownership of an NFT - /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` - /// is the zero address. Throws if `tokenId` is not a valid NFT. - /// @param to The new owner - /// @param tokenId The NFT to transfer - /// @dev EVM selector for this function is: 0x2ada85ff, - /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(CrossAddress memory to, uint256 tokenId) external; - - /// @notice Transfer ownership of an NFT from cross account address to cross account address - /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` - /// is the zero address. Throws if `tokenId` is not a valid NFT. - /// @param from Cross acccount address of current owner - /// @param to Cross acccount address of new owner - /// @param tokenId The NFT to transfer - /// @dev EVM selector for this function is: 0xd5cf430b, - /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) - function transferFromCross( - CrossAddress memory from, - CrossAddress memory to, - uint256 tokenId - ) external; - - // /// @notice Burns a specific ERC721 token. - // /// @dev Throws unless `msg.sender` is the current owner or an authorized - // /// operator for this NFT. Throws if `from` is not the current owner. Throws - // /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT. - // /// @param from The current owner of the NFT - // /// @param tokenId The NFT to transfer - // /// @dev EVM selector for this function is: 0x79cc6790, - // /// or in textual repr: burnFrom(address,uint256) - // function burnFrom(address from, uint256 tokenId) external; - - /// @notice Burns a specific ERC721 token. - /// @dev Throws unless `msg.sender` is the current owner or an authorized - /// operator for this NFT. Throws if `from` is not the current owner. Throws - /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT. - /// @param from The current owner of the NFT - /// @param tokenId The NFT to transfer - /// @dev EVM selector for this function is: 0xbb2f5a58, - /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(CrossAddress memory from, uint256 tokenId) external; - - /// @notice Returns next free NFT ID. - /// @dev EVM selector for this function is: 0x75794a3c, - /// or in textual repr: nextTokenId() - function nextTokenId() external view returns (uint256); - - // /// @notice Function to mint multiple tokens. - // /// @dev `tokenIds` should be an array of consecutive numbers and first number - // /// should be obtained with `nextTokenId` method - // /// @param to The new owner - // /// @param tokenIds IDs of the minted NFTs - // /// @dev EVM selector for this function is: 0x44a9945e, - // /// or in textual repr: mintBulk(address,uint256[]) - // function mintBulk(address to, uint256[] memory tokenIds) external returns (bool); - - /// @notice Function to mint a token. - /// @param data Array of pairs of token owner and token's properties for minted token - /// @dev EVM selector for this function is: 0xab427b0c, - /// or in textual repr: mintBulkCross(((address,uint256),(string,bytes)[])[]) - function mintBulkCross(MintTokenData[] memory data) external returns (bool); - - // /// @notice Function to mint multiple tokens with the given tokenUris. - // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive - // /// numbers and first number should be obtained with `nextTokenId` method - // /// @param to The new owner - // /// @param tokens array of pairs of token ID and token URI for minted tokens - // /// @dev EVM selector for this function is: 0x36543006, - // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) - // function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) external returns (bool); - - /// @notice Function to mint a token. - /// @param to The new owner crossAccountId - /// @param properties Properties of minted token - /// @return uint256 The id of the newly minted token - /// @dev EVM selector for this function is: 0xb904db03, - /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) - function mintCross(CrossAddress memory to, Property[] memory properties) external returns (uint256); - - /// @notice Returns collection helper contract address - /// @dev EVM selector for this function is: 0x1896cce6, - /// or in textual repr: collectionHelperAddress() - function collectionHelperAddress() external view returns (address); -} - -/// Data for creation token with uri. -struct TokenUri { - /// Id of new token. - uint256 id; - /// Uri of new token. - string uri; -} - -/// Token minting parameters -struct MintTokenData { - /// Minted token owner - CrossAddress owner; - /// Minted token properties - Property[] properties; -} - -/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension -/// @dev See https://eips.ethereum.org/EIPS/eip-721 -/// @dev the ERC-165 identifier for this interface is 0x780e9d63 -interface ERC721Enumerable is Dummy, ERC165 { - /// @notice Enumerate valid NFTs - /// @param index A counter less than `totalSupply()` - /// @return The token identifier for the `index`th NFT, - /// (sort order not specified) - /// @dev EVM selector for this function is: 0x4f6ccce7, - /// or in textual repr: tokenByIndex(uint256) - function tokenByIndex(uint256 index) external view returns (uint256); - - /// @dev Not implemented - /// @dev EVM selector for this function is: 0x2f745c59, - /// or in textual repr: tokenOfOwnerByIndex(address,uint256) - function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); - - /// @notice Count NFTs tracked by this contract - /// @return A count of valid NFTs tracked by this contract, where each one of - /// them has an assigned and queryable owner not equal to the zero address - /// @dev EVM selector for this function is: 0x18160ddd, - /// or in textual repr: totalSupply() - function totalSupply() external view returns (uint256); -} - -/// @dev inlined interface -interface ERC721Events { - event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); - event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); - event ApprovalForAll(address indexed owner, address indexed operator, bool approved); -} - -/// @title ERC-721 Non-Fungible Token Standard -/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md -/// @dev the ERC-165 identifier for this interface is 0x80ac58cd -interface ERC721 is Dummy, ERC165, ERC721Events { - /// @notice Count all NFTs assigned to an owner - /// @dev NFTs assigned to the zero address are considered invalid, and this - /// function throws for queries about the zero address. - /// @param owner An address for whom to query the balance - /// @return The number of NFTs owned by `owner`, possibly zero - /// @dev EVM selector for this function is: 0x70a08231, - /// or in textual repr: balanceOf(address) - function balanceOf(address owner) external view returns (uint256); - - /// @notice Find the owner of an NFT - /// @dev NFTs assigned to zero address are considered invalid, and queries - /// about them do throw. - /// @param tokenId The identifier for an NFT - /// @return The address of the owner of the NFT - /// @dev EVM selector for this function is: 0x6352211e, - /// or in textual repr: ownerOf(uint256) - function ownerOf(uint256 tokenId) external view returns (address); - - /// @dev Not implemented - /// @dev EVM selector for this function is: 0xb88d4fde, - /// or in textual repr: safeTransferFrom(address,address,uint256,bytes) - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory data - ) external; - - /// @dev Not implemented - /// @dev EVM selector for this function is: 0x42842e0e, - /// or in textual repr: safeTransferFrom(address,address,uint256) - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) external; - - /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE - /// TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE - /// THEY MAY BE PERMANENTLY LOST - /// @dev Throws unless `msg.sender` is the current owner or an authorized - /// operator for this NFT. Throws if `from` is not the current owner. Throws - /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT. - /// @param from The current owner of the NFT - /// @param to The new owner - /// @param tokenId The NFT to transfer - /// @dev EVM selector for this function is: 0x23b872dd, - /// or in textual repr: transferFrom(address,address,uint256) - function transferFrom( - address from, - address to, - uint256 tokenId - ) external; - - /// @notice Set or reaffirm the approved address for an NFT - /// @dev The zero address indicates there is no approved address. - /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized - /// operator of the current owner. - /// @param approved The new approved NFT controller - /// @param tokenId The NFT to approve - /// @dev EVM selector for this function is: 0x095ea7b3, - /// or in textual repr: approve(address,uint256) - function approve(address approved, uint256 tokenId) external; - - /// @notice Sets or unsets the approval of a given operator. - /// The `operator` is allowed to transfer all tokens of the `caller` on their behalf. - /// @param operator Operator - /// @param approved Should operator status be granted or revoked? - /// @dev EVM selector for this function is: 0xa22cb465, - /// or in textual repr: setApprovalForAll(address,bool) - function setApprovalForAll(address operator, bool approved) external; - - /// @notice Get the approved address for a single NFT - /// @dev Throws if `tokenId` is not a valid NFT - /// @param tokenId The NFT to find the approved address for - /// @return The approved address for this NFT, or the zero address if there is none - /// @dev EVM selector for this function is: 0x081812fc, - /// or in textual repr: getApproved(uint256) - function getApproved(uint256 tokenId) external view returns (address); - - /// @notice Tells whether the given `owner` approves the `operator`. - /// @dev EVM selector for this function is: 0xe985e9c5, - /// or in textual repr: isApprovedForAll(address,address) - function isApprovedForAll(address owner, address operator) external view returns (bool); -} - -interface UniqueNFT is - Dummy, - ERC165, - ERC721, - ERC721Enumerable, - ERC721UniqueExtensions, - ERC721UniqueMintable, - ERC721Burnable, - ERC721Metadata, - Collection, - TokenProperties -{} --- a/js-packages/tests/src/eth/api/UniqueNativeFungible.sol +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: OTHER -// This code is automatically generated - -pragma solidity >=0.8.0 <0.9.0; - -/// @dev common stubs holder -interface Dummy { - -} - -interface ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) external view returns (bool); -} - -/// @dev the ERC-165 identifier for this interface is 0x1313556c -interface ERC20UniqueExtensions is Dummy, ERC165 { - /// @dev EVM selector for this function is: 0xec069398, - /// or in textual repr: balanceOfCross((address,uint256)) - function balanceOfCross(CrossAddress memory owner) external view returns (uint256); - - /// @dev EVM selector for this function is: 0x2ada85ff, - /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(CrossAddress memory to, uint256 amount) external returns (bool); - - /// @dev EVM selector for this function is: 0xd5cf430b, - /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) - function transferFromCross( - CrossAddress memory from, - CrossAddress memory to, - uint256 amount - ) external returns (bool); -} - -/// Cross account struct -struct CrossAddress { - address eth; - uint256 sub; -} - -/// @dev inlined interface -interface ERC20Events { - event Transfer(address indexed from, address indexed to, uint256 value); - event Approval(address indexed owner, address indexed spender, uint256 value); -} - -/// @dev the ERC-165 identifier for this interface is 0x942e8b22 -interface ERC20 is Dummy, ERC165, ERC20Events { - /// @dev EVM selector for this function is: 0xdd62ed3e, - /// or in textual repr: allowance(address,address) - function allowance(address owner, address spender) external view returns (uint256); - - /// @dev EVM selector for this function is: 0x095ea7b3, - /// or in textual repr: approve(address,uint256) - function approve(address spender, uint256 amount) external returns (bool); - - /// @dev EVM selector for this function is: 0x70a08231, - /// or in textual repr: balanceOf(address) - function balanceOf(address owner) external view returns (uint256); - - /// @dev EVM selector for this function is: 0x313ce567, - /// or in textual repr: decimals() - function decimals() external view returns (uint8); - - /// @dev EVM selector for this function is: 0x06fdde03, - /// or in textual repr: name() - function name() external view returns (string memory); - - /// @dev EVM selector for this function is: 0x95d89b41, - /// or in textual repr: symbol() - function symbol() external view returns (string memory); - - /// @dev EVM selector for this function is: 0x18160ddd, - /// or in textual repr: totalSupply() - function totalSupply() external view returns (uint256); - - /// @dev EVM selector for this function is: 0xa9059cbb, - /// or in textual repr: transfer(address,uint256) - function transfer(address to, uint256 amount) external returns (bool); - - /// @dev EVM selector for this function is: 0x23b872dd, - /// or in textual repr: transferFrom(address,address,uint256) - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} - -interface UniqueNativeFungible is Dummy, ERC165, ERC20, ERC20UniqueExtensions {} --- a/js-packages/tests/src/eth/api/UniqueRefungible.sol +++ /dev/null @@ -1,859 +0,0 @@ -// SPDX-License-Identifier: OTHER -// This code is automatically generated - -pragma solidity >=0.8.0 <0.9.0; - -/// @dev common stubs holder -interface Dummy { - -} - -interface ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) external view returns (bool); -} - -/// @dev inlined interface -interface ERC721TokenEvent { - event TokenChanged(uint256 indexed tokenId); -} - -/// @title A contract that allows to set and delete token properties and change token property permissions. -/// @dev the ERC-165 identifier for this interface is 0xde0695c2 -interface TokenProperties is Dummy, ERC165, ERC721TokenEvent { - // /// @notice Set permissions for token property. - // /// @dev Throws error if `msg.sender` is not admin or owner of the collection. - // /// @param key Property key. - // /// @param isMutable Permission to mutate property. - // /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable. - // /// @param tokenOwner Permission to mutate property by token owner if property is mutable. - // /// @dev EVM selector for this function is: 0x222d97fa, - // /// or in textual repr: setTokenPropertyPermission(string,bool,bool,bool) - // function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) external; - - /// @notice Set permissions for token property. - /// @dev Throws error if `msg.sender` is not admin or owner of the collection. - /// @param permissions Permissions for keys. - /// @dev EVM selector for this function is: 0xbd92983a, - /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) - function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) external; - - /// @notice Get permissions for token properties. - /// @dev EVM selector for this function is: 0xf23d7790, - /// or in textual repr: tokenPropertyPermissions() - function tokenPropertyPermissions() external view returns (TokenPropertyPermission[] memory); - - // /// @notice Set token property value. - // /// @dev Throws error if `msg.sender` has no permission to edit the property. - // /// @param tokenId ID of the token. - // /// @param key Property key. - // /// @param value Property value. - // /// @dev EVM selector for this function is: 0x1752d67b, - // /// or in textual repr: setProperty(uint256,string,bytes) - // function setProperty(uint256 tokenId, string memory key, bytes memory value) external; - - /// @notice Set token properties value. - /// @dev Throws error if `msg.sender` has no permission to edit the property. - /// @param tokenId ID of the token. - /// @param properties settable properties - /// @dev EVM selector for this function is: 0x14ed3a6e, - /// or in textual repr: setProperties(uint256,(string,bytes)[]) - function setProperties(uint256 tokenId, Property[] memory properties) external; - - // /// @notice Delete token property value. - // /// @dev Throws error if `msg.sender` has no permission to edit the property. - // /// @param tokenId ID of the token. - // /// @param key Property key. - // /// @dev EVM selector for this function is: 0x066111d1, - // /// or in textual repr: deleteProperty(uint256,string) - // function deleteProperty(uint256 tokenId, string memory key) external; - - /// @notice Delete token properties value. - /// @dev Throws error if `msg.sender` has no permission to edit the property. - /// @param tokenId ID of the token. - /// @param keys Properties key. - /// @dev EVM selector for this function is: 0xc472d371, - /// or in textual repr: deleteProperties(uint256,string[]) - function deleteProperties(uint256 tokenId, string[] memory keys) external; - - /// @notice Get token property value. - /// @dev Throws error if key not found - /// @param tokenId ID of the token. - /// @param key Property key. - /// @return Property value bytes - /// @dev EVM selector for this function is: 0x7228c327, - /// or in textual repr: property(uint256,string) - function property(uint256 tokenId, string memory key) external view returns (bytes memory); -} - -/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). -struct Property { - string key; - bytes value; -} - -/// Ethereum representation of Token Property Permissions. -struct TokenPropertyPermission { - /// Token property key. - string key; - /// Token property permissions. - PropertyPermission[] permissions; -} - -/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value. -struct PropertyPermission { - /// TokenPermission field. - TokenPermissionField code; - /// TokenPermission value. - bool value; -} - -/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. -enum TokenPermissionField { - /// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] - Mutable, - /// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] - TokenOwner, - /// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] - CollectionAdmin -} - -/// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xb34d97e9 -interface Collection is Dummy, ERC165 { - // /// Set collection property. - // /// - // /// @param key Property key. - // /// @param value Propery value. - // /// @dev EVM selector for this function is: 0x2f073f66, - // /// or in textual repr: setCollectionProperty(string,bytes) - // function setCollectionProperty(string memory key, bytes memory value) external; - - /// Set collection properties. - /// - /// @param properties Vector of properties key/value pair. - /// @dev EVM selector for this function is: 0x50b26b2a, - /// or in textual repr: setCollectionProperties((string,bytes)[]) - function setCollectionProperties(Property[] memory properties) external; - - // /// Delete collection property. - // /// - // /// @param key Property key. - // /// @dev EVM selector for this function is: 0x7b7debce, - // /// or in textual repr: deleteCollectionProperty(string) - // function deleteCollectionProperty(string memory key) external; - - /// Delete collection properties. - /// - /// @param keys Properties keys. - /// @dev EVM selector for this function is: 0xee206ee3, - /// or in textual repr: deleteCollectionProperties(string[]) - function deleteCollectionProperties(string[] memory keys) external; - - /// Get collection property. - /// - /// @dev Throws error if key not found. - /// - /// @param key Property key. - /// @return bytes The property corresponding to the key. - /// @dev EVM selector for this function is: 0xcf24fd6d, - /// or in textual repr: collectionProperty(string) - function collectionProperty(string memory key) external view returns (bytes memory); - - /// Get collection properties. - /// - /// @param keys Properties keys. Empty keys for all propertyes. - /// @return Vector of properties key/value pairs. - /// @dev EVM selector for this function is: 0x285fb8e6, - /// or in textual repr: collectionProperties(string[]) - function collectionProperties(string[] memory keys) external view returns (Property[] memory); - - // /// Set the sponsor of the collection. - // /// - // /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. - // /// - // /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract. - // /// @dev EVM selector for this function is: 0x7623402e, - // /// or in textual repr: setCollectionSponsor(address) - // function setCollectionSponsor(address sponsor) external; - - /// Set the sponsor of the collection. - /// - /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor. - /// - /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. - /// @dev EVM selector for this function is: 0x84a1d5a8, - /// or in textual repr: setCollectionSponsorCross((address,uint256)) - function setCollectionSponsorCross(CrossAddress memory sponsor) external; - - /// Whether there is a pending sponsor. - /// @dev EVM selector for this function is: 0x058ac185, - /// or in textual repr: hasCollectionPendingSponsor() - function hasCollectionPendingSponsor() external view returns (bool); - - /// Collection sponsorship confirmation. - /// - /// @dev After setting the sponsor for the collection, it must be confirmed with this function. - /// @dev EVM selector for this function is: 0x3c50e97a, - /// or in textual repr: confirmCollectionSponsorship() - function confirmCollectionSponsorship() external; - - /// Remove collection sponsor. - /// @dev EVM selector for this function is: 0x6e0326a3, - /// or in textual repr: removeCollectionSponsor() - function removeCollectionSponsor() external; - - /// Get current sponsor. - /// - /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - /// @dev EVM selector for this function is: 0x6ec0a9f1, - /// or in textual repr: collectionSponsor() - function collectionSponsor() external view returns (CrossAddress memory); - - /// Get current collection limits. - /// - /// @return Array of collection limits - /// @dev EVM selector for this function is: 0xf63bc572, - /// or in textual repr: collectionLimits() - function collectionLimits() external view returns (CollectionLimit[] memory); - - /// Set limits for the collection. - /// @dev Throws error if limit not found. - /// @param limit Some limit. - /// @dev EVM selector for this function is: 0x2316ee74, - /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) - function setCollectionLimit(CollectionLimit memory limit) external; - - /// Get contract address. - /// @dev EVM selector for this function is: 0xf6b4dfb4, - /// or in textual repr: contractAddress() - function contractAddress() external view returns (address); - - /// Add collection admin. - /// @param newAdmin Cross account administrator address. - /// @dev EVM selector for this function is: 0x859aa7d6, - /// or in textual repr: addCollectionAdminCross((address,uint256)) - function addCollectionAdminCross(CrossAddress memory newAdmin) external; - - /// Remove collection admin. - /// @param admin Cross account administrator address. - /// @dev EVM selector for this function is: 0x6c0cd173, - /// or in textual repr: removeCollectionAdminCross((address,uint256)) - function removeCollectionAdminCross(CrossAddress memory admin) external; - - // /// Add collection admin. - // /// @param newAdmin Address of the added administrator. - // /// @dev EVM selector for this function is: 0x92e462c7, - // /// or in textual repr: addCollectionAdmin(address) - // function addCollectionAdmin(address newAdmin) external; - - // /// Remove collection admin. - // /// - // /// @param admin Address of the removed administrator. - // /// @dev EVM selector for this function is: 0xfafd7b42, - // /// or in textual repr: removeCollectionAdmin(address) - // function removeCollectionAdmin(address admin) external; - - /// @dev EVM selector for this function is: 0x0b9f3890, - /// or in textual repr: setCollectionNesting((bool,bool,address[])) - function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external; - - // /// Toggle accessibility of collection nesting. - // /// - // /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled' - // /// @dev EVM selector for this function is: 0x112d4586, - // /// or in textual repr: setCollectionNesting(bool) - // function setCollectionNesting(bool enable) external; - - // /// Toggle accessibility of collection nesting. - // /// - // /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled' - // /// @param collections Addresses of collections that will be available for nesting. - // /// @dev EVM selector for this function is: 0x64872396, - // /// or in textual repr: setCollectionNesting(bool,address[]) - // function setCollectionNesting(bool enable, address[] memory collections) external; - - /// @dev EVM selector for this function is: 0x92c660a8, - /// or in textual repr: collectionNesting() - function collectionNesting() external view returns (CollectionNestingAndPermission memory); - - // /// Returns nesting for a collection - // /// @dev EVM selector for this function is: 0x22d25bfe, - // /// or in textual repr: collectionNestingRestrictedCollectionIds() - // function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory); - - // /// Returns permissions for a collection - // /// @dev EVM selector for this function is: 0x5b2eaf4b, - // /// or in textual repr: collectionNestingPermissions() - // function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory); - - /// Set the collection access method. - /// @param mode Access mode - /// @dev EVM selector for this function is: 0x41835d4c, - /// or in textual repr: setCollectionAccess(uint8) - function setCollectionAccess(AccessMode mode) external; - - /// Checks that user allowed to operate with collection. - /// - /// @param user User address to check. - /// @dev EVM selector for this function is: 0x91b6df49, - /// or in textual repr: allowlistedCross((address,uint256)) - function allowlistedCross(CrossAddress memory user) external view returns (bool); - - // /// Add the user to the allowed list. - // /// - // /// @param user Address of a trusted user. - // /// @dev EVM selector for this function is: 0x67844fe6, - // /// or in textual repr: addToCollectionAllowList(address) - // function addToCollectionAllowList(address user) external; - - /// Add user to allowed list. - /// - /// @param user User cross account address. - /// @dev EVM selector for this function is: 0xa0184a3a, - /// or in textual repr: addToCollectionAllowListCross((address,uint256)) - function addToCollectionAllowListCross(CrossAddress memory user) external; - - // /// Remove the user from the allowed list. - // /// - // /// @param user Address of a removed user. - // /// @dev EVM selector for this function is: 0x85c51acb, - // /// or in textual repr: removeFromCollectionAllowList(address) - // function removeFromCollectionAllowList(address user) external; - - /// Remove user from allowed list. - /// - /// @param user User cross account address. - /// @dev EVM selector for this function is: 0x09ba452a, - /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) - function removeFromCollectionAllowListCross(CrossAddress memory user) external; - - /// Switch permission for minting. - /// - /// @param mode Enable if "true". - /// @dev EVM selector for this function is: 0x00018e84, - /// or in textual repr: setCollectionMintMode(bool) - function setCollectionMintMode(bool mode) external; - - // /// Check that account is the owner or admin of the collection - // /// - // /// @param user account to verify - // /// @return "true" if account is the owner or admin - // /// @dev EVM selector for this function is: 0x9811b0c7, - // /// or in textual repr: isOwnerOrAdmin(address) - // function isOwnerOrAdmin(address user) external view returns (bool); - - /// Check that account is the owner or admin of the collection - /// - /// @param user User cross account to verify - /// @return "true" if account is the owner or admin - /// @dev EVM selector for this function is: 0x3e75a905, - /// or in textual repr: isOwnerOrAdminCross((address,uint256)) - function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool); - - /// Returns collection type - /// - /// @return `Fungible` or `NFT` or `ReFungible` - /// @dev EVM selector for this function is: 0xd34b55b8, - /// or in textual repr: uniqueCollectionType() - function uniqueCollectionType() external view returns (string memory); - - /// Get collection owner. - /// - /// @return Tuble with sponsor address and his substrate mirror. - /// If address is canonical then substrate mirror is zero and vice versa. - /// @dev EVM selector for this function is: 0xdf727d3b, - /// or in textual repr: collectionOwner() - function collectionOwner() external view returns (CrossAddress memory); - - // /// Changes collection owner to another account - // /// - // /// @dev Owner can be changed only by current owner - // /// @param newOwner new owner account - // /// @dev EVM selector for this function is: 0x4f53e226, - // /// or in textual repr: changeCollectionOwner(address) - // function changeCollectionOwner(address newOwner) external; - - /// Get collection administrators - /// - /// @return Vector of tuples with admins address and his substrate mirror. - /// If address is canonical then substrate mirror is zero and vice versa. - /// @dev EVM selector for this function is: 0x5813216b, - /// or in textual repr: collectionAdmins() - function collectionAdmins() external view returns (CrossAddress[] memory); - - /// Changes collection owner to another account - /// - /// @dev Owner can be changed only by current owner - /// @param newOwner new owner cross account - /// @dev EVM selector for this function is: 0x6496c497, - /// or in textual repr: changeCollectionOwnerCross((address,uint256)) - function changeCollectionOwnerCross(CrossAddress memory newOwner) external; -} - -/// Cross account struct -struct CrossAddress { - address eth; - uint256 sub; -} - -/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]). -enum AccessMode { - /// Access grant for owner and admins. Used as default. - Normal, - /// Like a [`Normal`](AccessMode::Normal) but also users in allow list. - AllowList -} - -/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. -struct CollectionNestingPermission { - CollectionPermissionField field; - bool value; -} - -/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. -enum CollectionPermissionField { - /// Owner of token can nest tokens under it. - TokenOwner, - /// Admin of token collection can nest tokens under token. - CollectionAdmin -} - -/// Nested collections. -struct CollectionNesting { - bool token_owner; - uint256[] ids; -} - -/// Nested collections and permissions -struct CollectionNestingAndPermission { - /// Owner of token can nest tokens under it. - bool token_owner; - /// Admin of token collection can nest tokens under token. - bool collection_admin; - /// If set - only tokens from specified collections can be nested. - address[] restricted; -} - -/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. -struct CollectionLimit { - CollectionLimitField field; - OptionUint256 value; -} - -/// Optional value -struct OptionUint256 { - /// Shows the status of accessibility of value - bool status; - /// Actual value if `status` is true - uint256 value; -} - -/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. -enum CollectionLimitField { - /// How many tokens can a user have on one account. - AccountTokenOwnership, - /// How many bytes of data are available for sponsorship. - SponsoredDataSize, - /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] - SponsoredDataRateLimit, - /// How many tokens can be mined into this collection. - TokenLimit, - /// Timeouts for transfer sponsoring. - SponsorTransferTimeout, - /// Timeout for sponsoring an approval in passed blocks. - SponsorApproveTimeout, - /// Whether the collection owner of the collection can send tokens (which belong to other users). - OwnerCanTransfer, - /// Can the collection owner burn other people's tokens. - OwnerCanDestroy, - /// Is it possible to send tokens from this collection between users. - TransferEnabled -} - -/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension -/// @dev See https://eips.ethereum.org/EIPS/eip-721 -/// @dev the ERC-165 identifier for this interface is 0x5b5e139f -interface ERC721Metadata is Dummy, ERC165 { - // /// @notice A descriptive name for a collection of NFTs in this contract - // /// @dev real implementation of this function lies in `ERC721UniqueExtensions` - // /// @dev EVM selector for this function is: 0x06fdde03, - // /// or in textual repr: name() - // function name() external view returns (string memory); - - // /// @notice An abbreviated name for NFTs in this contract - // /// @dev real implementation of this function lies in `ERC721UniqueExtensions` - // /// @dev EVM selector for this function is: 0x95d89b41, - // /// or in textual repr: symbol() - // function symbol() external view returns (string memory); - - /// @notice A distinct Uniform Resource Identifier (URI) for a given asset. - /// - /// @dev If the token has a `url` property and it is not empty, it is returned. - /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`. - /// If the collection property `baseURI` is empty or absent, return "" (empty string) - /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix - /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings). - /// - /// @return token's const_metadata - /// @dev EVM selector for this function is: 0xc87b56dd, - /// or in textual repr: tokenURI(uint256) - function tokenURI(uint256 tokenId) external view returns (string memory); -} - -/// @title ERC721 Token that can be irreversibly burned (destroyed). -/// @dev the ERC-165 identifier for this interface is 0x42966c68 -interface ERC721Burnable is Dummy, ERC165 { - /// @notice Burns a specific ERC721 token. - /// @dev Throws unless `msg.sender` is the current RFT owner, or an authorized - /// operator of the current owner. - /// @param tokenId The RFT to approve - /// @dev EVM selector for this function is: 0x42966c68, - /// or in textual repr: burn(uint256) - function burn(uint256 tokenId) external; -} - -/// @title ERC721 minting logic. -/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6 -interface ERC721UniqueMintable is Dummy, ERC165 { - /// @notice Function to mint a token. - /// @param to The new owner - /// @return uint256 The id of the newly minted token - /// @dev EVM selector for this function is: 0x6a627842, - /// or in textual repr: mint(address) - function mint(address to) external returns (uint256); - - // /// @notice Function to mint a token. - // /// @dev `tokenId` should be obtained with `nextTokenId` method, - // /// unlike standard, you can't specify it manually - // /// @param to The new owner - // /// @param tokenId ID of the minted RFT - // /// @dev EVM selector for this function is: 0x40c10f19, - // /// or in textual repr: mint(address,uint256) - // function mint(address to, uint256 tokenId) external returns (bool); - - /// @notice Function to mint token with the given tokenUri. - /// @param to The new owner - /// @param tokenUri Token URI that would be stored in the NFT properties - /// @return uint256 The id of the newly minted token - /// @dev EVM selector for this function is: 0x45c17782, - /// or in textual repr: mintWithTokenURI(address,string) - function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256); - // /// @notice Function to mint token with the given tokenUri. - // /// @dev `tokenId` should be obtained with `nextTokenId` method, - // /// unlike standard, you can't specify it manually - // /// @param to The new owner - // /// @param tokenId ID of the minted RFT - // /// @param tokenUri Token URI that would be stored in the RFT properties - // /// @dev EVM selector for this function is: 0x50bb4e7f, - // /// or in textual repr: mintWithTokenURI(address,uint256,string) - // function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool); - -} - -/// @title Unique extensions for ERC721. -/// @dev the ERC-165 identifier for this interface is 0x4abaabdb -interface ERC721UniqueExtensions is Dummy, ERC165 { - /// @notice A descriptive name for a collection of NFTs in this contract - /// @dev EVM selector for this function is: 0x06fdde03, - /// or in textual repr: name() - function name() external view returns (string memory); - - /// @notice An abbreviated name for NFTs in this contract - /// @dev EVM selector for this function is: 0x95d89b41, - /// or in textual repr: symbol() - function symbol() external view returns (string memory); - - /// @notice A description for the collection. - /// @dev EVM selector for this function is: 0x7284e416, - /// or in textual repr: description() - function description() external view returns (string memory); - - // /// Returns the owner (in cross format) of the token. - // /// - // /// @param tokenId Id for the token. - // /// @dev EVM selector for this function is: 0x2b29dace, - // /// or in textual repr: crossOwnerOf(uint256) - // function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory); - - /// Returns the owner (in cross format) of the token. - /// - /// @param tokenId Id for the token. - /// @dev EVM selector for this function is: 0xcaa3a4d0, - /// or in textual repr: ownerOfCross(uint256) - function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory); - - /// @notice Count all RFTs assigned to an owner - /// @param owner An cross address for whom to query the balance - /// @return The number of RFTs owned by `owner`, possibly zero - /// @dev EVM selector for this function is: 0xec069398, - /// or in textual repr: balanceOfCross((address,uint256)) - function balanceOfCross(CrossAddress memory owner) external view returns (uint256); - - /// Returns the token properties. - /// - /// @param tokenId Id for the token. - /// @param keys Properties keys. Empty keys for all propertyes. - /// @return Vector of properties key/value pairs. - /// @dev EVM selector for this function is: 0xe07ede7e, - /// or in textual repr: properties(uint256,string[]) - function properties(uint256 tokenId, string[] memory keys) external view returns (Property[] memory); - - /// @notice Transfer ownership of an RFT - /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` - /// is the zero address. Throws if `tokenId` is not a valid RFT. - /// Throws if RFT pieces have multiple owners. - /// @param to The new owner - /// @param tokenId The RFT to transfer - /// @dev EVM selector for this function is: 0xa9059cbb, - /// or in textual repr: transfer(address,uint256) - function transfer(address to, uint256 tokenId) external; - - /// @notice Transfer ownership of an RFT - /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` - /// is the zero address. Throws if `tokenId` is not a valid RFT. - /// Throws if RFT pieces have multiple owners. - /// @param to The new owner - /// @param tokenId The RFT to transfer - /// @dev EVM selector for this function is: 0x2ada85ff, - /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(CrossAddress memory to, uint256 tokenId) external; - - /// @notice Transfer ownership of an RFT - /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` - /// is the zero address. Throws if `tokenId` is not a valid RFT. - /// Throws if RFT pieces have multiple owners. - /// @param to The new owner - /// @param tokenId The RFT to transfer - /// @dev EVM selector for this function is: 0xd5cf430b, - /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) - function transferFromCross( - CrossAddress memory from, - CrossAddress memory to, - uint256 tokenId - ) external; - - // /// @notice Burns a specific ERC721 token. - // /// @dev Throws unless `msg.sender` is the current owner or an authorized - // /// operator for this RFT. Throws if `from` is not the current owner. Throws - // /// if `to` is the zero address. Throws if `tokenId` is not a valid RFT. - // /// Throws if RFT pieces have multiple owners. - // /// @param from The current owner of the RFT - // /// @param tokenId The RFT to transfer - // /// @dev EVM selector for this function is: 0x79cc6790, - // /// or in textual repr: burnFrom(address,uint256) - // function burnFrom(address from, uint256 tokenId) external; - - /// @notice Burns a specific ERC721 token. - /// @dev Throws unless `msg.sender` is the current owner or an authorized - /// operator for this RFT. Throws if `from` is not the current owner. Throws - /// if `to` is the zero address. Throws if `tokenId` is not a valid RFT. - /// Throws if RFT pieces have multiple owners. - /// @param from The current owner of the RFT - /// @param tokenId The RFT to transfer - /// @dev EVM selector for this function is: 0xbb2f5a58, - /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(CrossAddress memory from, uint256 tokenId) external; - - /// @notice Returns next free RFT ID. - /// @dev EVM selector for this function is: 0x75794a3c, - /// or in textual repr: nextTokenId() - function nextTokenId() external view returns (uint256); - - // /// @notice Function to mint multiple tokens. - // /// @dev `tokenIds` should be an array of consecutive numbers and first number - // /// should be obtained with `nextTokenId` method - // /// @param to The new owner - // /// @param tokenIds IDs of the minted RFTs - // /// @dev EVM selector for this function is: 0x44a9945e, - // /// or in textual repr: mintBulk(address,uint256[]) - // function mintBulk(address to, uint256[] memory tokenIds) external returns (bool); - - /// @notice Function to mint a token. - /// @param tokenProperties Properties of minted token - /// @dev EVM selector for this function is: 0xdf7a5db7, - /// or in textual repr: mintBulkCross((((address,uint256),uint128)[],(string,bytes)[])[]) - function mintBulkCross(MintTokenData[] memory tokenProperties) external returns (bool); - - // /// @notice Function to mint multiple tokens with the given tokenUris. - // /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive - // /// numbers and first number should be obtained with `nextTokenId` method - // /// @param to The new owner - // /// @param tokens array of pairs of token ID and token URI for minted tokens - // /// @dev EVM selector for this function is: 0x36543006, - // /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[]) - // function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) external returns (bool); - - /// @notice Function to mint a token. - /// @param to The new owner crossAccountId - /// @param properties Properties of minted token - /// @return uint256 The id of the newly minted token - /// @dev EVM selector for this function is: 0xb904db03, - /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) - function mintCross(CrossAddress memory to, Property[] memory properties) external returns (uint256); - - /// Returns EVM address for refungible token - /// - /// @param token ID of the token - /// @dev EVM selector for this function is: 0xab76fac6, - /// or in textual repr: tokenContractAddress(uint256) - function tokenContractAddress(uint256 token) external view returns (address); - - /// @notice Returns collection helper contract address - /// @dev EVM selector for this function is: 0x1896cce6, - /// or in textual repr: collectionHelperAddress() - function collectionHelperAddress() external view returns (address); -} - -/// Data for creation token with uri. -struct TokenUri { - /// Id of new token. - uint256 id; - /// Uri of new token. - string uri; -} - -/// Token minting parameters -struct MintTokenData { - /// Minted token owner and number of pieces - OwnerPieces[] owners; - /// Minted token properties - Property[] properties; -} - -/// Token minting parameters -struct OwnerPieces { - /// Minted token owner - CrossAddress owner; - /// Number of token pieces - uint128 pieces; -} - -/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension -/// @dev See https://eips.ethereum.org/EIPS/eip-721 -/// @dev the ERC-165 identifier for this interface is 0x780e9d63 -interface ERC721Enumerable is Dummy, ERC165 { - /// @notice Enumerate valid RFTs - /// @param index A counter less than `totalSupply()` - /// @return The token identifier for the `index`th NFT, - /// (sort order not specified) - /// @dev EVM selector for this function is: 0x4f6ccce7, - /// or in textual repr: tokenByIndex(uint256) - function tokenByIndex(uint256 index) external view returns (uint256); - - /// Not implemented - /// @dev EVM selector for this function is: 0x2f745c59, - /// or in textual repr: tokenOfOwnerByIndex(address,uint256) - function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); - - /// @notice Count RFTs tracked by this contract - /// @return A count of valid RFTs tracked by this contract, where each one of - /// them has an assigned and queryable owner not equal to the zero address - /// @dev EVM selector for this function is: 0x18160ddd, - /// or in textual repr: totalSupply() - function totalSupply() external view returns (uint256); -} - -/// @dev inlined interface -interface ERC721Events { - event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); - event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); - event ApprovalForAll(address indexed owner, address indexed operator, bool approved); -} - -/// @title ERC-721 Non-Fungible Token Standard -/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md -/// @dev the ERC-165 identifier for this interface is 0x80ac58cd -interface ERC721 is Dummy, ERC165, ERC721Events { - /// @notice Count all RFTs assigned to an owner - /// @dev RFTs assigned to the zero address are considered invalid, and this - /// function throws for queries about the zero address. - /// @param owner An address for whom to query the balance - /// @return The number of RFTs owned by `owner`, possibly zero - /// @dev EVM selector for this function is: 0x70a08231, - /// or in textual repr: balanceOf(address) - function balanceOf(address owner) external view returns (uint256); - - /// @notice Find the owner of an RFT - /// @dev RFTs assigned to zero address are considered invalid, and queries - /// about them do throw. - /// Returns special 0xffffffffffffffffffffffffffffffffffffffff address for - /// the tokens that are partially owned. - /// @param tokenId The identifier for an RFT - /// @return The address of the owner of the RFT - /// @dev EVM selector for this function is: 0x6352211e, - /// or in textual repr: ownerOf(uint256) - function ownerOf(uint256 tokenId) external view returns (address); - - /// @dev Not implemented - /// @dev EVM selector for this function is: 0xb88d4fde, - /// or in textual repr: safeTransferFrom(address,address,uint256,bytes) - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory data - ) external; - - /// @dev Not implemented - /// @dev EVM selector for this function is: 0x42842e0e, - /// or in textual repr: safeTransferFrom(address,address,uint256) - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) external; - - /// @notice Transfer ownership of an RFT -- THE CALLER IS RESPONSIBLE - /// TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE - /// THEY MAY BE PERMANENTLY LOST - /// @dev Throws unless `msg.sender` is the current owner or an authorized - /// operator for this RFT. Throws if `from` is not the current owner. Throws - /// if `to` is the zero address. Throws if `tokenId` is not a valid RFT. - /// Throws if RFT pieces have multiple owners. - /// @param from The current owner of the NFT - /// @param to The new owner - /// @param tokenId The NFT to transfer - /// @dev EVM selector for this function is: 0x23b872dd, - /// or in textual repr: transferFrom(address,address,uint256) - function transferFrom( - address from, - address to, - uint256 tokenId - ) external; - - /// @dev Not implemented - /// @dev EVM selector for this function is: 0x095ea7b3, - /// or in textual repr: approve(address,uint256) - function approve(address approved, uint256 tokenId) external; - - /// @notice Sets or unsets the approval of a given operator. - /// The `operator` is allowed to transfer all token pieces of the `caller` on their behalf. - /// @param operator Operator - /// @param approved Should operator status be granted or revoked? - /// @dev EVM selector for this function is: 0xa22cb465, - /// or in textual repr: setApprovalForAll(address,bool) - function setApprovalForAll(address operator, bool approved) external; - - /// @dev Not implemented - /// @dev EVM selector for this function is: 0x081812fc, - /// or in textual repr: getApproved(uint256) - function getApproved(uint256 tokenId) external view returns (address); - - /// @notice Tells whether the given `owner` approves the `operator`. - /// @dev EVM selector for this function is: 0xe985e9c5, - /// or in textual repr: isApprovedForAll(address,address) - function isApprovedForAll(address owner, address operator) external view returns (bool); -} - -interface UniqueRefungible is - Dummy, - ERC165, - ERC721, - ERC721Enumerable, - ERC721UniqueExtensions, - ERC721UniqueMintable, - ERC721Burnable, - ERC721Metadata, - Collection, - TokenProperties -{} --- a/js-packages/tests/src/eth/api/UniqueRefungibleToken.sol +++ /dev/null @@ -1,181 +0,0 @@ -// SPDX-License-Identifier: OTHER -// This code is automatically generated - -pragma solidity >=0.8.0 <0.9.0; - -/// @dev common stubs holder -interface Dummy { - -} - -interface ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) external view returns (bool); -} - -/// @dev the ERC-165 identifier for this interface is 0x5755c3f2 -interface ERC1633 is Dummy, ERC165 { - /// @dev EVM selector for this function is: 0x80a54001, - /// or in textual repr: parentToken() - function parentToken() external view returns (address); - - /// @dev EVM selector for this function is: 0xd7f083f3, - /// or in textual repr: parentTokenId() - function parentTokenId() external view returns (uint256); -} - -/// @dev the ERC-165 identifier for this interface is 0xedd3a564 -interface ERC20UniqueExtensions is Dummy, ERC165 { - /// @dev Function to check the amount of tokens that an owner allowed to a spender. - /// @param owner crossAddress The address which owns the funds. - /// @param spender crossAddress The address which will spend the funds. - /// @return A uint256 specifying the amount of tokens still available for the spender. - /// @dev EVM selector for this function is: 0xe0af4bd7, - /// or in textual repr: allowanceCross((address,uint256),(address,uint256)) - function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) external view returns (uint256); - - // /// @dev Function that burns an amount of the token of a given account, - // /// deducting from the sender's allowance for said account. - // /// @param from The account whose tokens will be burnt. - // /// @param amount The amount that will be burnt. - // /// @dev EVM selector for this function is: 0x79cc6790, - // /// or in textual repr: burnFrom(address,uint256) - // function burnFrom(address from, uint256 amount) external returns (bool); - - /// @dev Function that burns an amount of the token of a given account, - /// deducting from the sender's allowance for said account. - /// @param from The account whose tokens will be burnt. - /// @param amount The amount that will be burnt. - /// @dev EVM selector for this function is: 0xbb2f5a58, - /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(CrossAddress memory from, uint256 amount) external returns (bool); - - /// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`. - /// Beware that changing an allowance with this method brings the risk that someone may use both the old - /// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this - /// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: - /// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - /// @param spender The crossaccount which will spend the funds. - /// @param amount The amount of tokens to be spent. - /// @dev EVM selector for this function is: 0x0ecd0ab0, - /// or in textual repr: approveCross((address,uint256),uint256) - function approveCross(CrossAddress memory spender, uint256 amount) external returns (bool); - - /// @notice Balance of account - /// @param owner An cross address for whom to query the balance - /// @return The number of fingibles owned by `owner`, possibly zero - /// @dev EVM selector for this function is: 0xec069398, - /// or in textual repr: balanceOfCross((address,uint256)) - function balanceOfCross(CrossAddress memory owner) external view returns (uint256); - - /// @dev Function that changes total amount of the tokens. - /// Throws if `msg.sender` doesn't owns all of the tokens. - /// @param amount New total amount of the tokens. - /// @dev EVM selector for this function is: 0xd2418ca7, - /// or in textual repr: repartition(uint256) - function repartition(uint256 amount) external returns (bool); - - /// @dev Transfer token for a specified address - /// @param to The crossaccount to transfer to. - /// @param amount The amount to be transferred. - /// @dev EVM selector for this function is: 0x2ada85ff, - /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(CrossAddress memory to, uint256 amount) external returns (bool); - - /// @dev Transfer tokens from one address to another - /// @param from The address which you want to send tokens from - /// @param to The address which you want to transfer to - /// @param amount the amount of tokens to be transferred - /// @dev EVM selector for this function is: 0xd5cf430b, - /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) - function transferFromCross( - CrossAddress memory from, - CrossAddress memory to, - uint256 amount - ) external returns (bool); -} - -/// Cross account struct -struct CrossAddress { - address eth; - uint256 sub; -} - -/// @dev inlined interface -interface ERC20Events { - event Transfer(address indexed from, address indexed to, uint256 value); - event Approval(address indexed owner, address indexed spender, uint256 value); -} - -/// @title Standard ERC20 token -/// -/// @dev Implementation of the basic standard token. -/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md -/// @dev the ERC-165 identifier for this interface is 0x942e8b22 -interface ERC20 is Dummy, ERC165, ERC20Events { - /// @return the name of the token. - /// @dev EVM selector for this function is: 0x06fdde03, - /// or in textual repr: name() - function name() external view returns (string memory); - - /// @return the symbol of the token. - /// @dev EVM selector for this function is: 0x95d89b41, - /// or in textual repr: symbol() - function symbol() external view returns (string memory); - - /// @dev Total number of tokens in existence - /// @dev EVM selector for this function is: 0x18160ddd, - /// or in textual repr: totalSupply() - function totalSupply() external view returns (uint256); - - /// @dev Not supported - /// @dev EVM selector for this function is: 0x313ce567, - /// or in textual repr: decimals() - function decimals() external view returns (uint8); - - /// @dev Gets the balance of the specified address. - /// @param owner The address to query the balance of. - /// @return An uint256 representing the amount owned by the passed address. - /// @dev EVM selector for this function is: 0x70a08231, - /// or in textual repr: balanceOf(address) - function balanceOf(address owner) external view returns (uint256); - - /// @dev Transfer token for a specified address - /// @param to The address to transfer to. - /// @param amount The amount to be transferred. - /// @dev EVM selector for this function is: 0xa9059cbb, - /// or in textual repr: transfer(address,uint256) - function transfer(address to, uint256 amount) external returns (bool); - - /// @dev Transfer tokens from one address to another - /// @param from address The address which you want to send tokens from - /// @param to address The address which you want to transfer to - /// @param amount uint256 the amount of tokens to be transferred - /// @dev EVM selector for this function is: 0x23b872dd, - /// or in textual repr: transferFrom(address,address,uint256) - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); - - /// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`. - /// Beware that changing an allowance with this method brings the risk that someone may use both the old - /// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this - /// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: - /// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - /// @param spender The address which will spend the funds. - /// @param amount The amount of tokens to be spent. - /// @dev EVM selector for this function is: 0x095ea7b3, - /// or in textual repr: approve(address,uint256) - function approve(address spender, uint256 amount) external returns (bool); - - /// @dev Function to check the amount of tokens that an owner allowed to a spender. - /// @param owner address The address which owns the funds. - /// @param spender address The address which will spend the funds. - /// @return A uint256 specifying the amount of tokens still available for the spender. - /// @dev EVM selector for this function is: 0xdd62ed3e, - /// or in textual repr: allowance(address,address) - function allowance(address owner, address spender) external view returns (uint256); -} - -interface UniqueRefungibleToken is Dummy, ERC165, ERC20, ERC20UniqueExtensions, ERC1633 {} --- a/js-packages/tests/src/eth/base.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; -import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; - - -describe('Contract calls', () => { - let donor: IKeyringPair; - - before(async function () { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Call of simple contract fee is less than 0.2 UNQ', async ({helper}) => { - const deployer = await helper.eth.createAccountWithBalance(donor); - const flipper = await helper.eth.deployFlipper(deployer); - - const cost = await helper.eth.calculateFee({Ethereum: deployer}, () => flipper.methods.flip().send({from: deployer})); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))).to.be.true; - }); - - itEth('Balance transfer fee is less than 0.2 UNQ', async ({helper}) => { - const userA = await helper.eth.createAccountWithBalance(donor); - const userB = helper.eth.createAccount(); - const cost = await helper.eth.calculateFee({Ethereum: userA}, () => helper.getWeb3().eth.sendTransaction({ - from: userA, - to: userB, - value: '1000000', - gas: helper.eth.DEFAULT_GAS, - })); - const balanceB = await helper.balance.getEthereum(userB); - expect(cost - balanceB < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))).to.be.true; - }); - - itEth('NFT transfer is close to 0.15 UNQ', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const [alice] = await helper.arrange.createAccounts([10n], donor); - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const {tokenId} = await collection.mintToken(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft', caller); - - const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, tokenId).send(caller)); - - const fee = Number(cost) / Number(helper.balance.getOneTokenNominal()); - const expectedFee = 0.15; - const tolerance = 0.001; - - expect(Math.abs(fee - expectedFee)).to.be.lessThan(tolerance); - }); -}); - -describe('ERC165 tests', () => { - // https://eips.ethereum.org/EIPS/eip-165 - - let erc721MetadataCompatibleNftCollectionId: number; - let simpleNftCollectionId: number; - let minter: string; - - const BASE_URI = 'base/'; - - async function checkInterface(helper: EthUniqueHelper, interfaceId: string, simpleResult: boolean, compatibleResult: boolean) { - const simple = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(simpleNftCollectionId), 'nft', minter); - const compatible = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(erc721MetadataCompatibleNftCollectionId), 'nft', minter); - - expect(await simple.methods.supportsInterface(interfaceId).call()).to.equal(simpleResult, `empty (not ERC721Metadata compatible) NFT collection returns not ${simpleResult}`); - expect(await compatible.methods.supportsInterface(interfaceId).call()).to.equal(compatibleResult, `ERC721Metadata compatible NFT collection returns not ${compatibleResult}`); - } - - before(async () => { - await usingEthPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - const [alice] = await helper.arrange.createAccounts([10n], donor); - ({collectionId: simpleNftCollectionId} = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'})); - minter = await helper.eth.createAccountWithBalance(donor); - ({collectionId: erc721MetadataCompatibleNftCollectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(minter, 'n', 'd', 'p', BASE_URI)); - }); - }); - - itEth('nonexistent interfaceID - 0xffffffff - always false', async ({helper}) => { - await checkInterface(helper, '0xffffffff', false, false); - }); - - itEth('ERC721 - 0x780e9d63 - support', async ({helper}) => { - await checkInterface(helper, '0x780e9d63', true, true); - }); - - itEth('ERC721Metadata - 0x5b5e139f - support', async ({helper}) => { - await checkInterface(helper, '0x5b5e139f', false, true); - }); - - itEth('ERC721Enumerable - 0x780e9d63 - support', async ({helper}) => { - await checkInterface(helper, '0x780e9d63', true, true); - }); - - itEth.skip('ERC721UniqueExtensions support', async ({helper}) => { - await checkInterface(helper, '0xb74c26b7', true, true); - }); - - itEth('ERC721Burnable - 0x42966c68 - support', async ({helper}) => { - await checkInterface(helper, '0x42966c68', true, true); - }); - - itEth('ERC165 - 0x01ffc9a7 - support', async ({helper}) => { - await checkInterface(helper, '0x01ffc9a7', true, true); - }); -}); --- a/js-packages/tests/src/eth/collectionAdmin.test.ts +++ /dev/null @@ -1,469 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect} from 'chai'; -import {Pallets} from '../util/index.js'; -import type {IEthCrossAccountId} from '@unique/playgrounds/src/types.js'; -import {usingEthPlaygrounds, itEth} from './util/index.js'; -import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; -import {CreateCollectionData} from './util/playgrounds/types.js'; - -async function recordEthFee(helper: EthUniqueHelper, userAddress: string, call: () => Promise) { - const before = await helper.balance.getSubstrate(helper.address.ethToSubstrate(userAddress)); - await call(); - await helper.wait.newBlocks(1); - const after = await helper.balance.getSubstrate(helper.address.ethToSubstrate(userAddress)); - - expect(after < before).to.be.true; - - return before - after; -} - -describe('Add collection admins', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {mode: 'ft' as const, requiredPallets: []}, - ].map(testCase => { - itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => { - // arrange - const owner = await helper.eth.createAccountWithBalance(donor); - const adminSub = await privateKey('//admin2'); - const adminEth = helper.eth.createAccount().toLowerCase(); - - const adminDeprecated = helper.eth.createAccount().toLowerCase(); - const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); - const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); - - const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true); - - // Check isOwnerOrAdminCross returns false: - expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.false; - expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.false; - expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.false; - - // Soft-deprecated: can addCollectionAdmin - await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send(); - // Can addCollectionAdminCross for substrate and ethereum address - await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send(); - await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send(); - - // 1. Expect api.rpc.unique.adminlist returns admins: - const adminListRpc = await helper.collection.getAdmins(collectionId); - expect(adminListRpc).to.has.length(3); - expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}, {Ethereum: adminDeprecated}]); - - // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist - let adminListEth = await collectionEvm.methods.collectionAdmins().call(); - adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element)); - expect(adminListRpc).to.be.like(adminListEth); - - // 3. check isOwnerOrAdminCross returns true: - expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true; - expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true; - expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.true; - }); - }); - - itEth('cross account admin can mint', async ({helper}) => { - // arrange: create collection and accounts - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', 'uri'); - const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase(); - const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); - const [adminSub] = await helper.arrange.createAccounts([100n], donor); - const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - - // cannot mint while not admin - await expect(collectionEvm.methods.mint(owner).send({from: adminEth})).to.be.rejected; - await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/); - - // admin (sub and eth) can mint token: - await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send(); - await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send(); - await collectionEvm.methods.mint(owner).send({from: adminEth}); - await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}}); - - expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2); - }); - - itEth('cannot add invalid cross account admin', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const [admin] = await helper.arrange.createAccounts([100n, 100n], donor); - - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - const adminCross = { - eth: helper.address.substrateToEth(admin.address), - sub: admin.addressRaw, - }; - await expect(collectionEvm.methods.addCollectionAdminCross(adminCross).send()).to.be.rejected; - }); - - itEth('can verify owner with methods.isOwnerOrAdmin[Cross]', async ({helper, privateKey}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const adminDeprecated = helper.eth.createAccount(); - const admin1Cross = helper.ethCrossAccount.fromKeyringPair(await privateKey('admin')); - const admin2Cross = helper.ethCrossAccount.fromAddress(helper.address.substrateToEth((await privateKey('admin3')).address)); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - - // Soft-deprecated: - expect(await collectionEvm.methods.isOwnerOrAdmin(adminDeprecated).call()).to.be.false; - expect(await collectionEvm.methods.isOwnerOrAdminCross(admin1Cross).call()).to.be.false; - expect(await collectionEvm.methods.isOwnerOrAdminCross(admin2Cross).call()).to.be.false; - - await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send(); - await collectionEvm.methods.addCollectionAdminCross(admin1Cross).send(); - await collectionEvm.methods.addCollectionAdminCross(admin2Cross).send(); - - // Soft-deprecated: isOwnerOrAdmin returns true - expect(await collectionEvm.methods.isOwnerOrAdmin(adminDeprecated).call()).to.be.true; - // Expect isOwnerOrAdminCross return true - expect(await collectionEvm.methods.isOwnerOrAdminCross(admin1Cross).call()).to.be.true; - expect(await collectionEvm.methods.isOwnerOrAdminCross(admin2Cross).call()).to.be.true; - }); - - // Soft-deprecated - itEth('(!negative tests!) Add admin by ADMIN is not allowed', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const admin = await helper.eth.createAccountWithBalance(donor); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - await collectionEvm.methods.addCollectionAdmin(admin).send(); - - const user = helper.eth.createAccount(); - await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: admin})) - .to.be.rejectedWith('NoPermission'); - - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); - expect(adminList.length).to.be.eq(1); - expect(adminList[0].asEthereum.toString().toLocaleLowerCase()) - .to.be.eq(admin.toLocaleLowerCase()); - }); - - // Soft-deprecated - itEth('(!negative tests!) Add admin by USER is not allowed', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const notAdmin = await helper.eth.createAccountWithBalance(donor); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - - const user = helper.eth.createAccount(); - await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: notAdmin})) - .to.be.rejectedWith('NoPermission'); - - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); - expect(adminList.length).to.be.eq(0); - }); - - itEth('(!negative tests!) Add [cross] admin by ADMIN is not allowed', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const [admin, notAdmin] = await helper.arrange.createAccounts([10n, 10n], donor); - const adminCross = helper.ethCrossAccount.fromKeyringPair(admin); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - await collectionEvm.methods.addCollectionAdminCross(adminCross).send(); - - const notAdminCross = helper.ethCrossAccount.fromKeyringPair(notAdmin); - await expect(collectionEvm.methods.addCollectionAdminCross(notAdminCross).call({from: adminCross.eth})) - .to.be.rejectedWith('NoPermission'); - - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); - expect(adminList.length).to.be.eq(1); - - const admin0Cross = helper.ethCrossAccount.fromKeyringPair(adminList[0]); - expect(admin0Cross.eth.toLocaleLowerCase()) - .to.be.eq(adminCross.eth.toLocaleLowerCase()); - }); - - itEth('(!negative tests!) Add [cross] admin by USER is not allowed', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const notAdmin0 = await helper.eth.createAccountWithBalance(donor); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - const [notAdmin1] = await helper.arrange.createAccounts([10n], donor); - const notAdmin1Cross = helper.ethCrossAccount.fromKeyringPair(notAdmin1); - await expect(collectionEvm.methods.addCollectionAdminCross(notAdmin1Cross).call({from: notAdmin0})) - .to.be.rejectedWith('NoPermission'); - - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); - expect(adminList.length).to.be.eq(0); - }); -}); - -describe('Remove collection admins', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - // Soft-deprecated - itEth('Remove admin by owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const newAdmin = helper.eth.createAccount(); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - await collectionEvm.methods.addCollectionAdmin(newAdmin).send(); - - { - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); - expect(adminList.length).to.be.eq(1); - expect(adminList[0].asEthereum.toString().toLocaleLowerCase()) - .to.be.eq(newAdmin.toLocaleLowerCase()); - } - - await collectionEvm.methods.removeCollectionAdmin(newAdmin).send(); - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); - expect(adminList.length).to.be.eq(0); - }); - - itEth('Remove [cross] admin by owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const [adminSub] = await helper.arrange.createAccounts([10n], donor); - const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase(); - const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); - const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); - - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send(); - await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send(); - - { - const adminList = await helper.collection.getAdmins(collectionId); - expect(adminList).to.deep.include({Substrate: adminSub.address}); - expect(adminList).to.deep.include({Ethereum: adminEth}); - } - - await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send(); - await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send(); - const adminList = await helper.collection.getAdmins(collectionId); - expect(adminList.length).to.be.eq(0); - - // Non admin cannot mint: - await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/); - await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected; - }); - - // Soft-deprecated - itEth('(!negative tests!) Remove admin by ADMIN is not allowed', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - - const admin0 = await helper.eth.createAccountWithBalance(donor); - await collectionEvm.methods.addCollectionAdmin(admin0).send(); - const admin1 = await helper.eth.createAccountWithBalance(donor); - await collectionEvm.methods.addCollectionAdmin(admin1).send(); - - await expect(collectionEvm.methods.removeCollectionAdmin(admin1).call({from: admin0})) - .to.be.rejectedWith('NoPermission'); - { - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); - expect(adminList.length).to.be.eq(2); - expect(adminList.toString().toLocaleLowerCase()) - .to.be.deep.contains(admin0.toLocaleLowerCase()) - .to.be.deep.contains(admin1.toLocaleLowerCase()); - } - }); - - // Soft-deprecated - itEth('(!negative tests!) Remove admin by USER is not allowed', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - - const admin = await helper.eth.createAccountWithBalance(donor); - await collectionEvm.methods.addCollectionAdmin(admin).send(); - const notAdmin = helper.eth.createAccount(); - - await expect(collectionEvm.methods.removeCollectionAdmin(admin).call({from: notAdmin})) - .to.be.rejectedWith('NoPermission'); - { - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); - expect(adminList[0].asEthereum.toString().toLocaleLowerCase()) - .to.be.eq(admin.toLocaleLowerCase()); - expect(adminList.length).to.be.eq(1); - } - }); - - itEth('(!negative tests!) Remove [cross] admin by ADMIN is not allowed', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const [admin1] = await helper.arrange.createAccounts([10n], donor); - const admin1Cross = helper.ethCrossAccount.fromKeyringPair(admin1); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - await collectionEvm.methods.addCollectionAdminCross(admin1Cross).send(); - - const [admin2] = await helper.arrange.createAccounts([10n], donor); - const admin2Cross = helper.ethCrossAccount.fromKeyringPair(admin2); - await collectionEvm.methods.addCollectionAdminCross(admin2Cross).send(); - - await expect(collectionEvm.methods.removeCollectionAdminCross(admin1Cross).call({from: admin2Cross.eth})) - .to.be.rejectedWith('NoPermission'); - - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); - expect(adminList.length).to.be.eq(2); - expect(adminList.toString().toLocaleLowerCase()) - .to.be.deep.contains(admin1.address.toLocaleLowerCase()) - .to.be.deep.contains(admin2.address.toLocaleLowerCase()); - }); - - itEth('(!negative tests!) Remove [cross] admin by USER is not allowed', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const [adminSub] = await helper.arrange.createAccounts([10n], donor); - const adminSubCross = helper.ethCrossAccount.fromKeyringPair(adminSub); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - await collectionEvm.methods.addCollectionAdminCross(adminSubCross).send(); - const notAdminEth = await helper.eth.createAccountWithBalance(donor); - - await expect(collectionEvm.methods.removeCollectionAdminCross(adminSubCross).call({from: notAdminEth})) - .to.be.rejectedWith('NoPermission'); - - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); - expect(adminList.length).to.be.eq(1); - expect(adminList[0].asSubstrate.toString().toLocaleLowerCase()) - .to.be.eq(adminSub.address.toLocaleLowerCase()); - }); -}); - -// Soft-deprecated -describe('Change owner tests', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Change owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const newOwner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - - await collectionEvm.methods.changeCollectionOwner(newOwner).send(); - - expect(await collectionEvm.methods.isOwnerOrAdmin(owner).call()).to.be.false; - expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.true; - }); - - itEth('change owner call fee', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const newOwner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.changeCollectionOwner(newOwner).send()); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - expect(cost > 0); - }); - - itEth('(!negative tests!) call setOwner by non owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const newOwner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - - await expect(collectionEvm.methods.changeCollectionOwner(newOwner).send({from: newOwner})).to.be.rejected; - expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.false; - }); -}); - -describe('Change substrate owner tests', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Change owner [cross]', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const ownerEth = await helper.eth.createAccountWithBalance(donor); - const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth); - const [ownerSub] = await helper.arrange.createAccounts([10n], donor); - const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub); - - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.false; - - // Can set ethereum owner: - await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossEth).send({from: owner}); - expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossEth).call()).to.be.true; - expect(await helper.collection.getData(collectionId)) - .to.have.property('normalizedOwner').that.is.eq(helper.address.ethToSubstrate(ownerEth)); - - // Can set Substrate owner: - await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossSub).send({from: ownerEth}); - expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.true; - expect(await helper.collection.getData(collectionId)) - .to.have.property('normalizedOwner').that.is.eq(helper.address.normalizeSubstrate(ownerSub.address)); - }); - - itEth.skip('change owner call fee', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const [newOwner] = await helper.arrange.createAccounts([10n], donor); - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.setOwnerSubstrate(newOwner.addressRaw).send()); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - expect(cost > 0); - }); - - itEth('(!negative tests!) call setOwner by non owner [cross]', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const otherReceiver = await helper.eth.createAccountWithBalance(donor); - const [newOwner] = await helper.arrange.createAccounts([10n], donor); - const newOwnerCross = helper.ethCrossAccount.fromKeyringPair(newOwner); - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - await expect(collectionEvm.methods.changeCollectionOwnerCross(newOwnerCross).send({from: otherReceiver})).to.be.rejected; - expect(await collectionEvm.methods.isOwnerOrAdminCross(newOwnerCross).call()).to.be.false; - }); -}); --- a/js-packages/tests/src/eth/collectionHelperAddress.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {COLLECTION_HELPER, Pallets} from '../util/index.js'; - -describe('[eth]CollectionHelperAddress test: ERC20/ERC721 ', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('NFT', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionAddress: nftCollectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC'); - const nftCollection = await helper.ethNativeContract.collection(nftCollectionAddress, 'nft', owner); - - expect((await nftCollection.methods.collectionHelperAddress().call()) - .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER); - }); - - itEth.ifWithPallets('RFT ', [Pallets.ReFungible], async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionAddress: rftCollectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC'); - - const rftCollection = await helper.ethNativeContract.collection(rftCollectionAddress, 'rft', owner); - expect((await rftCollection.methods.collectionHelperAddress().call()) - .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER); - }); - - itEth('FT', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', 18, 'absolutely anything', 'ROC'); - const collection = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - expect((await collection.methods.collectionHelperAddress().call()) - .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER); - }); - - itEth('[collectionHelpers] convert collectionId into address', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionId = 7; - const collectionAddress = helper.ethAddress.fromCollectionId(collectionId); - const helperContract = await helper.ethNativeContract.collectionHelpers(owner); - - expect(await helperContract.methods.collectionAddress(collectionId).call()).to.be.equal(collectionAddress); - expect(parseInt(await helperContract.methods.collectionId(collectionAddress).call())).to.be.equal(collectionId); - }); - -}); --- a/js-packages/tests/src/eth/collectionLimits.test.ts +++ /dev/null @@ -1,145 +0,0 @@ -import type {IKeyringPair} from '@polkadot/types/types'; -import {Pallets} from '../util/index.js'; -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import {CollectionLimitField, CreateCollectionData} from './util/playgrounds/types.js'; - - -describe('Can set collection limits', () => { - let donor: IKeyringPair; - - before(async () => { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - [ - {case: 'nft' as const}, - {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {case: 'ft' as const}, - ].map(testCase => - itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'FLO', testCase.case, 18)).send(); - const limits = { - accountTokenOwnershipLimit: 1000, - sponsoredDataSize: 1024, - sponsoredDataRateLimit: 30, - tokenLimit: 1000000, - sponsorTransferTimeout: 6, - sponsorApproveTimeout: 6, - ownerCanTransfer: 1, - ownerCanDestroy: 0, - transfersEnabled: 0, - }; - - const expectedLimits = { - accountTokenOwnershipLimit: 1000, - sponsoredDataSize: 1024, - sponsoredDataRateLimit: {blocks: 30}, - tokenLimit: 1000000, - sponsorTransferTimeout: 6, - sponsorApproveTimeout: 6, - ownerCanTransfer: true, - ownerCanDestroy: false, - transfersEnabled: false, - }; - - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); - await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: limits.accountTokenOwnershipLimit}}).send(); - await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataSize, value: {status: true, value: limits.sponsoredDataSize}}).send(); - await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: limits.sponsoredDataRateLimit}}).send(); - await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.TokenLimit, value: {status: true, value: limits.tokenLimit}}).send(); - await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsorTransferTimeout, value: {status: true, value: limits.sponsorTransferTimeout}}).send(); - await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsorApproveTimeout, value: {status: true, value: limits.sponsorApproveTimeout}}).send(); - await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanTransfer, value: {status: true, value: limits.ownerCanTransfer}}).send(); - await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanDestroy, value: {status: true, value: limits.ownerCanDestroy}}).send(); - await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.TransferEnabled, value: {status: true, value: limits.transfersEnabled}}).send(); - - // Check limits from sub: - const data = (await helper.rft.getData(collectionId))!; - expect(data.raw.limits).to.deep.eq(expectedLimits); - expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits); - // Check limits from eth: - const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner}); - expect(limitsEvm).to.have.length(9); - expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]); - expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]); - expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]); - expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]); - expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]); - expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]); - expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]); - expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]); - expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]); - })); -}); - -describe('Cannot set invalid collection limits', () => { - let donor: IKeyringPair; - - before(async () => { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - [ - {case: 'nft' as const}, - {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {case: 'ft' as const}, - ].map(testCase => - itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { - const invalidLimits = { - accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER), - transfersEnabled: 3, - }; - - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'ISNI', testCase.case, 18)).send(); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); - - // Cannot set non-existing limit - await expect(collectionEvm.methods - .setCollectionLimit({field: 9, value: {status: true, value: 1}}) - .call()).to.be.rejectedWith('Returned error: VM Exception while processing transaction: revert value not convertible into enum "CollectionLimitField"'); - - // Cannot disable limits - await expect(collectionEvm.methods - .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: false, value: 0}}) - .call()).to.be.rejectedWith('user can\'t disable limits'); - - await expect(collectionEvm.methods - .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: invalidLimits.accountTokenOwnershipLimit}}) - .call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`); - - await expect(collectionEvm.methods - .setCollectionLimit({field: CollectionLimitField.TransferEnabled, value: {status: true, value: 3}}) - .call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`); - - expect(() => collectionEvm.methods - .setCollectionLimit({field: CollectionLimitField.SponsoredDataSize, value: {status: true, value: -1}}).send()).to.throw('value out-of-bounds'); - })); - - [ - {case: 'nft' as const, requiredPallets: []}, - {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {case: 'ft' as const, requiredPallets: []}, - ].map(testCase => - itEth.ifWithPallets(`Non-owner and non-admin cannot set collection limits for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const nonOwner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'FLO', testCase.case, 18)).send(); - - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); - await expect(collectionEvm.methods - .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) - .call({from: nonOwner})) - .to.be.rejectedWith('NoPermission'); - - await expect(collectionEvm.methods - .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) - .send({from: nonOwner})) - .to.be.rejected; - })); -}); --- a/js-packages/tests/src/eth/collectionProperties.test.ts +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; -import {Pallets} from '../util/index.js'; -import type {IProperty, ITokenPropertyPermission} from '@unique/playgrounds/src/types.js'; -import type {IKeyringPair} from '@polkadot/types/types'; - -describe('EVM collection properties', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await _helper.arrange.createAccounts([50n], donor); - }); - }); - - // Soft-deprecated: setCollectionProperty - [ - {method: 'setCollectionProperties', mode: 'nft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, - {method: 'setCollectionProperties', mode: 'rft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, - {method: 'setCollectionProperties', mode: 'ft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, - {method: 'setCollectionProperty', mode: 'nft' as const, methodParams: ['testKey', Buffer.from('testValue')], expectedProps: [{key: 'testKey', value: 'testValue'}]}, - ].map(testCase => - itEth.ifWithPallets(`Collection properties can be set: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []}); - await collection.addAdmin(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'setCollectionProperty'); - - // collectionProperties returns an empty array if no properties: - expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like([]); - expect(await collectionEvm.methods.collectionProperties(['NonExistingKey']).call()).to.be.like([]); - - await collectionEvm.methods[testCase.method](...testCase.methodParams).send({from: caller}); - - const raw = (await collection.getData())?.raw; - expect(raw.properties).to.deep.equal(testCase.expectedProps); - - // collectionProperties returns properties: - expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value))); - })); - - itEth('Cannot set invalid properties', async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []}); - await collection.addAdmin(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft', caller); - - await expect(contract.methods.setCollectionProperties([{key: '', value: Buffer.from('val1')}]).send({from: caller})).to.be.rejected; - await expect(contract.methods.setCollectionProperties([{key: 'déjà vu', value: Buffer.from('hmm...')}]).send({from: caller})).to.be.rejected; - await expect(contract.methods.setCollectionProperties([{key: 'a'.repeat(257), value: Buffer.from('val3')}]).send({from: caller})).to.be.rejected; - // TODO add more expects - const raw = (await collection.getData())?.raw; - expect(raw.properties).to.deep.equal([]); - }); - - - // Soft-deprecated: deleteCollectionProperty - [ - {method: 'deleteCollectionProperties', mode: 'nft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]}, - {method: 'deleteCollectionProperties', mode: 'rft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]}, - {method: 'deleteCollectionProperties', mode: 'ft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]}, - {method: 'deleteCollectionProperty', mode: 'nft' as const, methodParams: ['testKey1'], expectedProps: [{key: 'testKey2', value: 'testValue2'}, {key: 'testKey3', value: 'testValue3'}]}, - ].map(testCase => - itEth.ifWithPallets(`Collection properties can be deleted: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => { - const properties = [ - {key: 'testKey1', value: 'testValue1'}, - {key: 'testKey2', value: 'testValue2'}, - {key: 'testKey3', value: 'testValue3'}]; - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties}); - - await collection.addAdmin(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'deleteCollectionProperty'); - - await contract.methods[testCase.method](...testCase.methodParams).send({from: caller}); - - const raw = (await collection.getData())?.raw; - - expect(raw.properties.length).to.equal(testCase.expectedProps.length); - expect(raw.properties).to.deep.equal(testCase.expectedProps); - })); - - [ - {method: 'deleteCollectionProperties', methodParams: [['testKey2']]}, - {method: 'deleteCollectionProperty', methodParams: ['testKey2']}, - ].map(testCase => - itEth(`cannot ${testCase.method}() of non-owned collections`, async ({helper}) => { - const properties = [ - {key: 'testKey1', value: 'testValue1'}, - {key: 'testKey2', value: 'testValue2'}, - ]; - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'deleteCollectionProperty'); - - await expect(collectionEvm.methods[testCase.method](...testCase.methodParams).send({from: caller})).to.be.rejected; - expect(await collection.getProperties()).to.deep.eq(properties); - })); - - itEth('Can be read', async({helper}) => { - const caller = helper.eth.createAccount(); - const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: [{key: 'testKey', value: 'testValue'}]}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft', caller); - - const value = await contract.methods.collectionProperty('testKey').call(); - expect(value).to.equal(helper.getWeb3().utils.toHex('testValue')); - }); -}); - -describe('Supports ERC721Metadata', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - [ - {case: 'nft' as const}, - {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`ERC721Metadata property can be set for ${testCase.case} collection`, testCase.requiredPallets || [], async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const bruh = await helper.eth.createAccountWithBalance(donor); - - const BASE_URI = 'base/'; - const SUFFIX = 'suffix1'; - const URI = 'uri1'; - - const collectionHelpers = await helper.ethNativeContract.collectionHelpers(caller); - const creatorMethod = testCase.case === 'rft' ? 'createRFTCollection' : 'createNFTCollection'; - - const {collectionId, collectionAddress} = await helper.eth[creatorMethod](caller, 'n', 'd', 'p'); - const bruhCross = helper.ethCrossAccount.fromAddress(bruh); - - const contract = await helper.ethNativeContract.collectionById(collectionId, testCase.case, caller); - await contract.methods.addCollectionAdminCross(bruhCross).send(); // to check that admin will work too - - const collection1 = helper.nft.getCollectionObject(collectionId); - const data1 = await collection1.getData(); - expect(data1?.raw.flags.erc721metadata).to.be.false; - expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.false; - - await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, BASE_URI) - .send({from: bruh}); - - expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.true; - - const collection2 = helper.nft.getCollectionObject(collectionId); - const data2 = await collection2.getData(); - expect(data2?.raw.flags.erc721metadata).to.be.true; - - const propertyPermissions = data2?.raw.tokenPropertyPermissions; - expect(propertyPermissions?.length).to.equal(2); - - expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => tpp.key === 'URI' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner)).to.be.not.null; - - expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => tpp.key === 'URISuffix' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner)).to.be.not.null; - - expect(data2?.raw.properties?.find((property: IProperty) => property.key === 'baseURI' && property.value === BASE_URI)).to.be.not.null; - - const token1Result = await contract.methods.mint(bruh).send(); - const tokenId1 = token1Result.events.Transfer.returnValues.tokenId; - - expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI); - - await contract.methods.setProperties(tokenId1, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send(); - expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX); - - await contract.methods.setProperties(tokenId1, [{key: 'URI', value: Buffer.from(URI)}]).send(); - expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(URI); - - await contract.methods.deleteProperties(tokenId1, ['URI']).send(); - expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX); - - const token2Result = await contract.methods.mintWithTokenURI(bruh, URI).send(); - const tokenId2 = token2Result.events.Transfer.returnValues.tokenId; - - expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(URI); - - await contract.methods.deleteProperties(tokenId2, ['URI']).send(); - expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI); - - await contract.methods.setProperties(tokenId2, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send(); - expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI + SUFFIX); - })); -}); --- a/js-packages/tests/src/eth/collectionSponsoring.test.ts +++ /dev/null @@ -1,955 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../util/index.js'; -import {itEth, expect} from './util/index.js'; -import {CollectionLimitField, TokenPermissionField} from './util/playgrounds/types.js'; - -describe('evm nft collection sponsoring', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let nominal: bigint; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([100n], donor); - nominal = helper.balance.getOneTokenNominal(); - }); - }); - - // TODO: move to substrate tests - itEth('sponsors mint transactions', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}}); - await collection.setSponsor(alice, alice.address); - await collection.confirmSponsorship(alice); - - const minter = helper.eth.createAccount(); - expect(await helper.balance.getEthereum(minter)).to.equal(0n); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', minter); - - await collection.addToAllowList(alice, {Ethereum: minter}); - - const result = await contract.methods.mint(minter).send(); - - const events = helper.eth.normalizeEvents(result.events); - expect(events).to.be.deep.equal([ - { - address: collectionAddress, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: minter, - tokenId: '1', - }, - }, - ]); - }); - - // TODO: Temprorary off. Need refactor - // itWeb3('Set substrate sponsor', async ({api, web3, privateKeyWrapper}) => { - // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - // const collectionHelpers = evmCollectionHelpers(web3, owner); - // let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send(); - // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); - // const sponsor = privateKeyWrapper('//Alice'); - // const collectionEvm = evmCollection(web3, owner, collectionIdAddress); - - // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - // result = await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner}); - // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true; - - // const confirmTx = await api.tx.unique.confirmSponsorship(collectionId); - // await submitTransactionAsync(sponsor, confirmTx); - // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - - // const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner}); - // expect(bigIntToSub(api, BigInt(sponsorTuple[1]))).to.be.eq(sponsor.address); - // }); - - [ - 'setCollectionSponsorCross', - 'setCollectionSponsor', // Soft-deprecated - ].map(testCase => - itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner); - - let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)}); - const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner, testCase === 'setCollectionSponsor'); - - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner}); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true; - - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); - expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - - await collectionEvm.methods.removeCollectionSponsor().send({from: owner}); - - sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); - expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000'); - })); - - [ - 'setCollectionSponsorCross', - 'setCollectionSponsor', // Soft-deprecated - ].map(testCase => - itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsorEth = await helper.eth.createAccountWithBalance(donor); - const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); - - const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', ''); - - const collectionSub = helper.nft.getCollectionObject(collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor'); - - // Set collection sponsor: - await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner}); - let sponsorship = (await collectionSub.getData())!.raw.sponsorship; - expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); - // Account cannot confirm sponsorship if it is not set as a sponsor - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - // Sponsor can confirm sponsorship: - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); - sponsorship = (await collectionSub.getData())!.raw.sponsorship; - expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); - - // Create user with no balance: - const user = helper.ethCrossAccount.createAccount(); - const nextTokenId = await collectionEvm.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - - // Set collection permissions: - const oldPermissions = (await collectionSub.getData())!.raw.permissions; - expect(oldPermissions.mintMode).to.be.false; - expect(oldPermissions.access).to.be.equal('Normal'); - - await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); - await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner}); - await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); - await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: 30}}).send(); - - const newPermissions = (await collectionSub.getData())!.raw.permissions; - expect(newPermissions.mintMode).to.be.true; - expect(newPermissions.access).to.be.equal('AllowList'); - - // Set token permissions - await collectionEvm.methods.setTokenPropertyPermissions([ - ['key', [ - [TokenPermissionField.TokenOwner, true], - ], - ], - ]).send({from: owner}); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); - const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); - - // User can mint token without balance: - { - const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth}); - const event = helper.eth.normalizeEvents(result.events) - .find(event => event.event === 'Transfer'); - - expect(event).to.be.deep.equal({ - address: collectionAddress, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user.eth, - tokenId: '1', - }, - }); - - // await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value1')}]).send({from: user.eth}); - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); - const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); - - expect(await collectionEvm.methods.properties(nextTokenId, []).call()) - .to.be.like([ - [ - 'key', - '0x' + Buffer.from('Value').toString('hex'), - ], - ]); - expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); - expect(userBalanceAfter).to.be.eq(userBalanceBefore); - expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; - } - })); - - itEth('Can sponsor [set token properties] via access list', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsorEth = await helper.eth.createAccountWithBalance(donor); - const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); - - const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', ''); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, false); - - // Set collection sponsor: - await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner}); - - // Sponsor can confirm sponsorship: - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); - - // Create user with no balance: - const user = helper.ethCrossAccount.createAccount(); - const nextTokenId = await collectionEvm.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - - // Set collection permissions: - await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); - await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner}); - await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); - await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: 30}}).send(); - - // Set token permissions - await collectionEvm.methods.setTokenPropertyPermissions([ - ['key', [ - [TokenPermissionField.TokenOwner, true], - ], - ], - ]).send({from: owner}); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); - const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); - - // User can mint token without balance: - { - const result = await collectionEvm.methods.mintCross(user, []).send({from: user.eth}); - const event = helper.eth.normalizeEvents(result.events) - .find(event => event.event === 'Transfer'); - - expect(event).to.be.deep.equal({ - address: collectionAddress, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user.eth, - tokenId: '1', - }, - }); - - await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth}); - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); - const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); - - expect(await collectionEvm.methods.properties(nextTokenId, []).call()) - .to.be.like([ - [ - 'key', - '0x' + Buffer.from('Value').toString('hex'), - ], - ]); - expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); - expect(userBalanceAfter).to.be.eq(userBalanceBefore); - expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; - } - }); - - // TODO: Temprorary off. Need refactor - // itWeb3('Sponsoring collection from substrate address via access list', async ({api, web3, privateKeyWrapper}) => { - // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - // const collectionHelpers = evmCollectionHelpers(web3, owner); - // const result = await collectionHelpers.methods.createERC721MetadataCompatibleNFTCollection('Sponsor collection', '1', '1', '').send(); - // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); - // const sponsor = privateKeyWrapper('//Alice'); - // const collectionEvm = evmCollection(web3, owner, collectionIdAddress); - - // await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner}); - - // const confirmTx = await api.tx.unique.confirmSponsorship(collectionId); - // await submitTransactionAsync(sponsor, confirmTx); - - // const user = createEthAccount(web3); - // const nextTokenId = await collectionEvm.methods.nextTokenId().call(); - // expect(nextTokenId).to.be.equal('1'); - - // await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); - // await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner}); - // await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); - - // const ownerBalanceBefore = await ethBalanceViaSub(api, owner); - // const sponsorBalanceBefore = (await getBalance(api, [sponsor.address]))[0]; - - // { - // const nextTokenId = await collectionEvm.methods.nextTokenId().call(); - // expect(nextTokenId).to.be.equal('1'); - // const result = await collectionEvm.methods.mintWithTokenURI( - // user, - // nextTokenId, - // 'Test URI', - // ).send({from: user}); - // const events = normalizeEvents(result.events); - - // expect(events).to.be.deep.equal([ - // { - // address: collectionIdAddress, - // event: 'Transfer', - // args: { - // from: '0x0000000000000000000000000000000000000000', - // to: user, - // tokenId: nextTokenId, - // }, - // }, - // ]); - - // const ownerBalanceAfter = await ethBalanceViaSub(api, owner); - // const sponsorBalanceAfter = (await getBalance(api, [sponsor.address]))[0]; - - // expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); - // expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); - // expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; - // } - // }); - - [ - 'setCollectionSponsorCross', - 'setCollectionSponsor', // Soft-deprecated - ].map(testCase => - itEth(`[${testCase}] Check that transaction via EVM spend money from sponsor address`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', ''); - - const collectionSub = helper.nft.getCollectionObject(collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor'); - // Set collection sponsor: - await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send(); - let collectionData = (await collectionSub.getData())!; - expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - collectionData = (await collectionSub.getData())!; - expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - - const user = helper.eth.createAccount(); - const userCross = helper.ethCrossAccount.fromAddress(user); - await collectionEvm.methods.addCollectionAdminCross(userCross).send(); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - - const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); - const tokenId = mintingResult.events.Transfer.returnValues.tokenId; - - const event = helper.eth.normalizeEvents(mintingResult.events) - .find(event => event.event === 'Transfer'); - const address = helper.ethAddress.fromCollectionId(collectionId); - - expect(event).to.be.deep.equal({ - address, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: '1', - }, - }); - expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI'); - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; - })); - - itEth('Can reassign collection sponsor', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsorEth = await helper.eth.createAccountWithBalance(donor); - const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); - const [sponsorSub] = await helper.arrange.createAccounts([100n], donor); - const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub); - - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', ''); - const collectionSub = helper.nft.getCollectionObject(collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - // Set and confirm sponsor: - await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner}); - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); - - // Can reassign sponsor: - await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner}); - const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship; - expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address}); - }); -}); - -describe('evm RFT collection sponsoring', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let nominal: bigint; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([100n], donor); - nominal = helper.balance.getOneTokenNominal(); - }); - }); - - [ - 'mintCross', - 'mintWithTokenURI', - ].map(testCase => - itEth(`[${testCase}] sponsors mint transactions`, async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}, tokenPropertyPermissions: [ - {key: 'URI', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}, - ]}); - - const owner = await helper.eth.createAccountWithBalance(donor); - await collection.setSponsor(alice, alice.address); - await collection.confirmSponsorship(alice); - - const minter = helper.eth.createAccount(); - const minterCross = helper.ethCrossAccount.fromAddress(minter); - expect(await helper.balance.getEthereum(minter)).to.equal(0n); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', minter, true); - - await collection.addToAllowList(alice, {Ethereum: minter}); - await collection.addAdmin(alice, {Ethereum: owner}); - const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner); - await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, 'base/') - .send(); - - let mintingResult; - let tokenId; - switch (testCase) { - case 'mintCross': - mintingResult = await contract.methods.mintCross(minterCross, []).send(); - break; - case 'mintWithTokenURI': - mintingResult = await contract.methods.mintWithTokenURI(minter, 'Test URI').send(); - tokenId = mintingResult.events.Transfer.returnValues.tokenId; - expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); - break; - } - - const events = helper.eth.normalizeEvents(mintingResult.events); - expect(events).to.deep.include({ - address: collectionAddress, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: minter, - tokenId: '1', - }, - }); - })); - - [ - 'setCollectionSponsorCross', - 'setCollectionSponsor', // Soft-deprecated - ].map(testCase => - itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner); - - let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)}); - const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'rft', owner, testCase === 'setCollectionSponsor'); - - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner}); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true; - - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - - await collectionEvm.methods.removeCollectionSponsor().send({from: owner}); - - const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); - expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000'); - })); - - [ - 'setCollectionSponsorCross', - 'setCollectionSponsor', // Soft-deprecated - ].map(testCase => - itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsorEth = await helper.eth.createAccountWithBalance(donor); - const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); - - const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Sponsor collection', '1', '1', ''); - - const collectionSub = helper.rft.getCollectionObject(collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor'); - - // Set collection sponsor: - await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner}); - let sponsorship = (await collectionSub.getData())!.raw.sponsorship; - expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); - // Account cannot confirm sponsorship if it is not set as a sponsor - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - // Sponsor can confirm sponsorship: - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); - sponsorship = (await collectionSub.getData())!.raw.sponsorship; - expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); - - // Create user with no balance: - const user = helper.eth.createAccount(); - const userCross = helper.ethCrossAccount.fromAddress(user); - const nextTokenId = await collectionEvm.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - - // Set collection permissions: - const oldPermissions = (await collectionSub.getData())!.raw.permissions; - expect(oldPermissions.mintMode).to.be.false; - expect(oldPermissions.access).to.be.equal('Normal'); - - await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); - await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner}); - await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); - - const newPermissions = (await collectionSub.getData())!.raw.permissions; - expect(newPermissions.mintMode).to.be.true; - expect(newPermissions.access).to.be.equal('AllowList'); - - // Set token permissions - await collectionEvm.methods.setTokenPropertyPermissions([ - ['URI', [ - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, true], - ], - ], - ]).send({from: owner}); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); - const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - - // User can mint token without balance: - { - const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); - const events = helper.eth.normalizeEvents(result.events); - - expect(events).to.deep.include({ - address: collectionAddress, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: '1', - }, - }); - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); - const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - - expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); - expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); - expect(userBalanceAfter).to.be.eq(userBalanceBefore); - expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; - } - })); - - [ - 'setCollectionSponsorCross', - 'setCollectionSponsor', // Soft-deprecated - ].map(testCase => - itEth(`[${testCase}] Check that collection admin EVM transaction spend money from sponsor eth address`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); - - const collectionSub = helper.rft.getCollectionObject(collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor'); - // Set collection sponsor: - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false; - await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send(); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true; - let collectionData = (await collectionSub.getData())!; - expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true; - - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - collectionData = (await collectionSub.getData())!; - expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false; - const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); - const sponsorSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.ethToSubstrate(sponsor)); - const actualSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))); - expect(actualSubAddress).to.be.equal(sponsorSubAddress); - - const user = helper.eth.createAccount(); - const userCross = helper.ethCrossAccount.fromAddress(user); - await collectionEvm.methods.addCollectionAdminCross(userCross).send(); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - - const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); - const tokenId = mintingResult.events.Transfer.returnValues.tokenId; - - const events = helper.eth.normalizeEvents(mintingResult.events); - const address = helper.ethAddress.fromCollectionId(collectionId); - - expect(events).to.deep.include({ - address, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: '1', - }, - }); - expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI'); - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; - })); - - itEth('Check that collection admin EVM transaction spend money from sponsor sub address', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = alice; - const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor); - - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); - - const collectionSub = helper.rft.getCollectionObject(collectionId); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false); - // Set collection sponsor: - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false; - await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send(); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true; - let collectionData = (await collectionSub.getData())!; - expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(sponsor.address); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - await collectionSub.confirmSponsorship(sponsor); - collectionData = (await collectionSub.getData())!; - expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(sponsor.address); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false; - const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); - expect(BigInt(sponsorStruct.sub)).to.be.equal(BigInt('0x' + Buffer.from(sponsor.addressRaw).toString('hex'))); - - const user = helper.eth.createAccount(); - const userCross = helper.ethCrossAccount.fromAddress(user); - await collectionEvm.methods.addCollectionAdminCross(userCross).send(); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address); - - const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); - const tokenId = mintingResult.events.Transfer.returnValues.tokenId; - - const events = helper.eth.normalizeEvents(mintingResult.events); - - expect(events).to.deep.include({ - address: collectionAddress, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: '1', - }, - }); - expect(await collectionEvm.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; - }); - - itEth('Sponsoring collection from substrate address via access list', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const user = helper.eth.createAccount(); - const userCross = helper.ethCrossAccount.fromAddress(user); - const sponsor = alice; - const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor); - - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false); - - await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner}); - - const collectionSub = helper.rft.getCollectionObject(collectionId); - await collectionSub.confirmSponsorship(sponsor); - - const nextTokenId = await collectionEvm.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - - await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); - - await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner}); - await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); - - // Set token permissions - await collectionEvm.methods.setTokenPropertyPermissions([ - ['URI', [ - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, true], - ], - ], - ]).send({from: owner}); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address); - const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - - { - const nextTokenId = await collectionEvm.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - const mintingResult = await collectionEvm.methods.mintWithTokenURI( - user, - 'Test URI', - ).send({from: user}); - - const events = helper.eth.normalizeEvents(mintingResult.events); - - - expect(events).to.deep.include({ - address: collectionAddress, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: nextTokenId, - }, - }); - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address); - const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - - expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); - expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); - expect(userBalanceAfter).to.be.eq(userBalanceBefore); - expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; - } - }); - - itEth('Can reassign collection sponsor', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsorEth = await helper.eth.createAccountWithBalance(donor); - const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); - const [sponsorSub] = await helper.arrange.createAccounts([100n], donor); - const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub); - - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); - const collectionSub = helper.rft.getCollectionObject(collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - - // Set and confirm sponsor: - await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner}); - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); - - // Can reassign sponsor: - await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner}); - const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship; - expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address}); - }); - - [ - 'transfer', - 'transferCross', - 'transferFrom', - 'transferFromCross', - ].map(testCase => - itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - - await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send(); - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - - const user = await helper.eth.createAccountWithBalance(donor); - const userCross = helper.ethCrossAccount.fromAddress(user); - await collectionEvm.methods.addCollectionAdminCross(userCross).send(); - - const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); - const tokenId = result.events.Transfer.returnValues.tokenId; - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - - switch (testCase) { - case 'transfer': - await collectionEvm.methods.transfer(receiver, tokenId).send({from: user}); - break; - case 'transferCross': - await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user}); - break; - case 'transferFrom': - await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user}); - break; - case 'transferFromCross': - await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user}); - break; - } - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; - const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - expect(userBalanceAfter).to.be.eq(userBalanceBefore); - })); -}); - -describe('evm RFT token sponsoring', () => { - let donor: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - donor = await privateKey({url: import.meta.url}); - }); - }); - - [ - 'transfer', - 'transferCross', - 'transferFrom', - 'transferFromCross', - ].map(testCase => - itEth(`[${testCase}] Check that token piece transfer via EVM spend money from sponsor address`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - - await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send(); - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - - const user = await helper.eth.createAccountWithBalance(donor); - const userCross = helper.ethCrossAccount.fromAddress(user); - await collectionEvm.methods.addCollectionAdminCross(userCross).send(); - - const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); - const tokenId = result.events.Transfer.returnValues.tokenId; - - const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user); - await tokenContract.methods.repartition(2).send(); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - - switch (testCase) { - case 'transfer': - await tokenContract.methods.transfer(receiver, 1).send(); - break; - case 'transferCross': - await tokenContract.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), 1).send(); - break; - case 'transferFrom': - await tokenContract.methods.transferFrom(user, receiver, 1).send(); - break; - case 'transferFromCross': - await tokenContract.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), 1).send(); - break; - } - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; - const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - expect(userBalanceAfter).to.be.eq(userBalanceBefore); - })); - - [ - 'approve', - 'approveCross', - ].map(testCase => - itEth(`[${testCase}] Check that approve via EVM spend money from sponsor address`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', ''); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - - await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send(); - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - - const user = await helper.eth.createAccountWithBalance(donor); - const userCross = helper.ethCrossAccount.fromAddress(user); - await collectionEvm.methods.addCollectionAdminCross(userCross).send(); - - const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); - const tokenId = result.events.Transfer.returnValues.tokenId; - - const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user); - await tokenContract.methods.repartition(2).send(); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - - switch (testCase) { - case 'approve': - await tokenContract.methods.approve(receiver, 1).send(); - break; - case 'approveCross': - await tokenContract.methods.approveCross(helper.ethCrossAccount.fromAddress(receiver), 1).send(); - break; - } - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; - const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - expect(userBalanceAfter).to.be.eq(userBalanceBefore); - })); -}); - --- a/js-packages/tests/src/eth/contractSponsoring.test.ts +++ /dev/null @@ -1,579 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; -import {itEth, expect, SponsoringMode, usingEthPlaygrounds} from './util/index.js'; -import {usingPlaygrounds} from '../util/index.js'; -import type {CompiledContract} from './util/playgrounds/types.js'; - -describe('Sponsoring EVM contracts', () => { - let donor: IKeyringPair; - let nominal: bigint; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - nominal = helper.balance.getOneTokenNominal(); - }); - }); - - itEth('Self sponsoring can be set by the address that deployed the contract', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const flipper = await helper.eth.deployFlipper(owner); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - - // 1. owner can set selfSponsoring: - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; - const result = await helpers.methods.selfSponsoredEnable(flipper.options.address).send({from: owner}); - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; - - // 1.1 Can get sponsor using methods.sponsor: - const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call(); - expect(actualSponsorOpt.status).to.be.true; - const actualSponsor = actualSponsorOpt.value; - expect(actualSponsor.eth).to.eq(flipper.options.address); - expect(actualSponsor.sub).to.eq('0'); - - // 2. Events should be: - const ethEvents = helper.eth.helper.eth.normalizeEvents(result.events); - expect(ethEvents).to.be.deep.equal([ - { - address: flipper.options.address, - event: 'ContractSponsorSet', - args: { - contractAddress: flipper.options.address, - sponsor: flipper.options.address, - }, - }, - { - address: flipper.options.address, - event: 'ContractSponsorshipConfirmed', - args: { - contractAddress: flipper.options.address, - sponsor: flipper.options.address, - }, - }, - ]); - }); - - itEth('Self sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const notOwner = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; - await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission'); - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; - }); - - itEth('Sponsoring can be set by the address that has deployed the contract', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false; - await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner}); - expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true; - }); - - itEth('Sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const notOwner = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false; - await expect(helpers.methods.setSponsoringMode(notOwner, SponsoringMode.Allowlisted).call({from: notOwner})).to.be.rejectedWith('NoPermission'); - expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false; - }); - - itEth('Sponsor can be set by the address that deployed the contract', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - // 1. owner can set a sponsor: - expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false; - const result = await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); - expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true; - - // 2. Events should be: - const events = helper.eth.normalizeEvents(result.events); - expect(events).to.be.deep.equal([ - { - address: flipper.options.address, - event: 'ContractSponsorSet', - args: { - contractAddress: flipper.options.address, - sponsor: sponsor, - }, - }, - ]); - }); - - itEth('Sponsor cannot be set by the address that did not deployed the contract', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const notOwner = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false; - await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).call({from: notOwner})).to.be.rejectedWith('NoPermission'); - expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false; - }); - - itEth('Sponsorship can be confirmed by the address that pending as sponsor', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; - await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); - - // 1. sponsor can confirm sponsorship: - const result = await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; - - // 1.1 Can get sponsor using methods.sponsor: - const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call(); - expect(actualSponsorOpt.status).to.be.true; - const actualSponsor = actualSponsorOpt.value; - expect(actualSponsor.eth).to.eq(sponsor); - expect(actualSponsor.sub).to.eq('0'); - - // 2. Events should be: - const events = helper.eth.normalizeEvents(result.events); - expect(events).to.be.deep.equal([ - { - address: flipper.options.address, - event: 'ContractSponsorshipConfirmed', - args: { - contractAddress: flipper.options.address, - sponsor: sponsor, - }, - }, - ]); - }); - - itEth('Sponsorship can not be confirmed by the address that not pending as sponsor', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const notSponsor = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; - await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected; - await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPermission'); - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; - }); - - itEth('Sponsorship can not be confirmed by the address that not set as sponsor', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const notSponsor = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; - await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPendingSponsor'); - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; - }); - - itEth('Sponsor can be removed by the address that deployed the contract', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; - await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); - await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; - // 1. Can remove sponsor: - const result = await helpers.methods.removeSponsor(flipper.options.address).send(); - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; - - // 2. Events should be: - const events = helper.eth.normalizeEvents(result.events); - expect(events).to.be.deep.equal([ - { - address: flipper.options.address, - event: 'ContractSponsorRemoved', - args: { - contractAddress: flipper.options.address, - }, - }, - ]); - - // TODO: why call method reverts? - // const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call(); - // expect(actualSponsor.eth).to.eq(sponsor); - // expect(actualSponsor.sub).to.eq('0'); - }); - - itEth('Sponsor can not be removed by the address that did not deployed the contract', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const notOwner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; - await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); - await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; - - await expect(helpers.methods.removeSponsor(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission'); - await expect(helpers.methods.removeSponsor(flipper.options.address).send({from: notOwner})).to.be.rejected; - expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; - }); - - itEth('In generous mode, non-allowlisted user transaction will be sponsored', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); - await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); - - await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner}); - await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); - - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - const callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller)); - - await flipper.methods.flip().send({from: caller}); - expect(await flipper.methods.getValue().call()).to.be.true; - - // Balance should be taken from sponsor instead of caller - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; - expect(callerBalanceAfter).to.be.eq(callerBalanceBefore); - }); - - itEth('In generous mode, non-allowlisted user transaction will be self sponsored', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - await helpers.methods.selfSponsoredEnable(flipper.options.address).send(); - - await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner}); - await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); - - await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address); - - const contractBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address)); - const callerBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller)); - - await flipper.methods.flip().send({from: caller}); - expect(await flipper.methods.getValue().call()).to.be.true; - - // Balance should be taken from sponsor instead of caller - const contractBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address)); - const callerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller)); - expect(contractBalanceAfter < contractBalanceBefore).to.be.true; - expect(callerBalanceAfter).to.be.eq(callerBalanceBefore); - }); - - [ - {balance: 0n, label: '0'}, - {balance: 10n, label: '10'}, - ].map(testCase => { - itEth(`Allow-listed address that has ${testCase.label} UNQ can call a contract. Sponsor balance should decrease`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const caller = helper.eth.createAccount(); - await helper.eth.transferBalanceFromSubstrate(donor, caller, testCase.balance); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); - await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner}); - - await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner}); - await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); - - await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); - await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); - - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - expect(sponsorBalanceBefore > 0n).to.be.true; - - await flipper.methods.flip().send({from: caller}); - expect(await flipper.methods.getValue().call()).to.be.true; - - // Balance should be taken from flipper instead of caller - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; - // Caller's balance does not change: - const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller)); - expect(callerBalanceAfter).to.eq(testCase.balance * nominal); - }); - }); - - itEth('Non-allow-listed address can call a contract. Sponsor balance should not decrease', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const caller = helper.eth.createAccount(); - const contractHelpers = await helper.ethNativeContract.contractHelpers(owner); - - // Deploy flipper and send some tokens: - const flipper = await helper.eth.deployFlipper(owner); - await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address); - expect(await flipper.methods.getValue().call()).to.be.false; - // flipper address has some tokens: - const originalFlipperBalance = await helper.balance.getEthereum(flipper.options.address); - expect(originalFlipperBalance > 0n).to.be.true; - - // Set Allowlisted sponsoring mode. caller is not in allow list: - await contractHelpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); - await contractHelpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner}); - await contractHelpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); - - // 1. Caller has no UNQ and is not in allow list. So he cannot flip: - await expect(flipper.methods.flip().send({from: caller})).to.be.rejectedWith(/Returned error: insufficient funds for gas \* price \+ value/); - expect(await flipper.methods.getValue().call()).to.be.false; - - // Flipper's balance does not change: - const balanceAfter = await helper.balance.getEthereum(flipper.options.address); - expect(balanceAfter).to.be.equal(originalFlipperBalance); - }); - - itEth('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - const originalCallerBalance = await helper.balance.getEthereum(caller); - await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); - await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner}); - - await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner}); - await helpers.methods.setSponsoringRateLimit(flipper.options.address, 10).send({from: owner}); - - await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); - await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); - - const originalFlipperBalance = await helper.balance.getEthereum(sponsor); - expect(originalFlipperBalance > 0n).to.be.true; - - await flipper.methods.flip().send({from: caller}); - expect(await flipper.methods.getValue().call()).to.be.true; - expect(await helper.balance.getEthereum(caller)).to.be.equal(originalCallerBalance); - - const newFlipperBalance = await helper.balance.getEthereum(sponsor); - expect(newFlipperBalance).to.be.not.equal(originalFlipperBalance); - - await flipper.methods.flip().send({from: caller}); - // todo:playgrounds fails rarely (expected 99893341659775672580n to equal 99912598679356033129n) (again, 99893341659775672580n) - expect(await helper.balance.getEthereum(sponsor)).to.be.equal(newFlipperBalance); - expect(await helper.balance.getEthereum(caller)).to.be.not.equal(originalCallerBalance); - }); - - // TODO: Find a way to calculate default rate limit - itEth('Default rate limit equal 7200', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - expect(await helpers.methods.sponsoringRateLimit(flipper.options.address).call()).to.be.equal('7200'); - }); - - itEth('Gas price boundaries', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); - await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); - - await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner}); - await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); - - let sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - let callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller)); - - let expectValue = await flipper.methods.getValue().call(); - - const flip = async (gasPrice: bigint, shouldPass = true) => { - await flipper.methods.flip().send({from: caller, gasPrice: gasPrice}); - expectValue = !expectValue; - expect(await flipper.methods.getValue().call()).to.be.eq(expectValue); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.eq(shouldPass); - expect(callerBalanceAfter === callerBalanceBefore).to.be.eq(shouldPass); - sponsorBalanceBefore = sponsorBalanceAfter; - callerBalanceBefore = callerBalanceAfter; - }; - - const gasPrice = BigInt((await helper.eth.getGasPrice())!); - await flip(gasPrice); - await flip(gasPrice * 2n); - await flip(gasPrice * 21n / 10n); - await flip(gasPrice * 22n / 10n, false); - }); -}); - -describe('Sponsoring Fee Limit', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let testContract: CompiledContract; - - async function compileTestContract(helper: EthUniqueHelper) { - if(!testContract) { - testContract = await helper.ethContract.compile( - 'TestContract', - ` - // SPDX-License-Identifier: MIT - pragma solidity ^0.8.0; - - contract TestContract { - event Result(bool); - - function test(uint32 cycles) public { - uint256 counter = 0; - while(true) { - counter ++; - if (counter > cycles){ - break; - } - } - emit Result(true); - } - } - `, - ); - } - return testContract; - } - - async function deployTestContract(helper: EthUniqueHelper, owner: string) { - const compiled = await compileTestContract(helper); - return await helper.ethContract.deployByAbi(owner, compiled.abi, compiled.object); - } - - before(async () => { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([100n], donor); - }); - }); - - itEth('Default fee limit', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('115792089237316195423570985008687907853269984665640564039457584007913129639935'); - }); - - itEth('Set fee limit', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - await helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send(); - expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('100'); - }); - - itEth('Negative test - set fee limit by non-owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const stranger = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - const flipper = await helper.eth.deployFlipper(owner); - - await expect(helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send({from: stranger})).to.be.rejected; - }); - - itEth('Negative test - check that eth transactions exceeding fee limit are not executed', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const user = await helper.eth.createAccountWithBalance(donor); - const helpers = helper.ethNativeContract.contractHelpers(owner); - - const testContract = await deployTestContract(helper, owner); - - await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner}); - await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner}); - - await helpers.methods.setSponsor(testContract.options.address, sponsor).send(); - await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor}); - - const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice()); - - await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send(); - - const originalUserBalance = await helper.balance.getEthereum(user); - await testContract.methods.test(100).send({from: user, gas: 2_000_000, maxFeePerGas: gasPrice.toString()}); - expect(await helper.balance.getEthereum(user)).to.be.equal(originalUserBalance); - - await testContract.methods.test(100).send({from: user, gas: 2_100_000, maxFeePerGas: gasPrice.toString()}); - expect(await helper.balance.getEthereum(user)).to.not.be.equal(originalUserBalance); - }); - - itEth('Negative test - check that evm.call transactions exceeding fee limit are not executed', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(owner); - - const testContract = await deployTestContract(helper, owner); - - await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner}); - await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner}); - - await helpers.methods.setSponsor(testContract.options.address, sponsor).send(); - await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor}); - - const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice()); - - await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send(); - - const originalAliceBalance = await helper.balance.getSubstrate(alice.address); - - await helper.eth.sendEVM( - alice, - testContract.options.address, - testContract.methods.test(100).encodeABI(), - '0', - 2_000_000, - ); - // expect((await api.query.system.account(alice.address)).data.free.toBigInt()).to.be.equal(originalAliceBalance); - expect(await helper.balance.getSubstrate(alice.address)).to.be.equal(originalAliceBalance); - - await helper.eth.sendEVM( - alice, - testContract.options.address, - testContract.methods.test(100).encodeABI(), - '0', - 2_100_000, - ); - expect(await helper.balance.getSubstrate(alice.address)).to.not.be.equal(originalAliceBalance); - }); -}); --- a/js-packages/tests/src/eth/createCollection.test.ts +++ /dev/null @@ -1,1449 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {evmToAddress} from '@polkadot/util-crypto'; -import {Pallets, requirePalletsOrSkip} from '../util/index.js'; -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import {CREATE_COLLECTION_DATA_DEFAULTS, CollectionLimitField, CollectionMode, CreateCollectionData, TokenPermissionField, emptyAddress} from './util/playgrounds/types.js'; -import {CollectionFlag} from '@unique/playgrounds/src/types.js'; -import type {IEthCrossAccountId, TCollectionMode} from '@unique/playgrounds/src/types.js'; - -const DECIMALS = 18; -const CREATE_COLLECTION_DATA_DEFAULTS_ARRAY = [ - [], // properties - [], // tokenPropertyPermissions - [], // adminList - [false, false, []], // nestingSettings - [], // limits - emptyAddress, // pendingSponsor - [0], // flags -]; - -type ElementOf = A extends readonly (infer T)[] ? T : never; -function* cartesian>, R extends Array>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf}]> { - if(args.length === 0) { - yield internalRest as any; - return; - } - for(const value of args[0]) { - yield* cartesian([...internalRest, value], ...args.slice(1)) as any; - } -} - -describe('Create collection from EVM', () => { - let donor: IKeyringPair; - let nominal: bigint; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - nominal = helper.balance.getOneTokenNominal(); - }); - }); - - describe('Fungible collection', () => { - before(async function() { - await usingEthPlaygrounds((helper) => { - requirePalletsOrSkip(this, helper, [Pallets.Fungible]); - return Promise.resolve(); - }); - }); - - itEth('Collection address exist', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; - const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); - - expect(await collectionHelpers - .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) - .to.be.false; - - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send(); - expect(await collectionHelpers - .methods.isCollectionExist(collectionAddress).call()) - .to.be.true; - - // check collectionOwner: - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); - const collectionOwner = await collectionEvm.methods.collectionOwner().call(); - expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true)); - }); - - itEth('destroyCollection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send(); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - - const result = await collectionHelper.methods - .destroyCollection(collectionAddress) - .send({from: owner}); - - const events = helper.eth.normalizeEvents(result.events); - - expect(events).to.be.deep.equal([ - { - address: collectionHelper.options.address, - event: 'CollectionDestroyed', - args: { - collectionId: collectionAddress, - }, - }, - ]); - - expect(await collectionHelper.methods - .isCollectionExist(collectionAddress) - .call()).to.be.false; - expect(await helper.collection.getData(collectionId)).to.be.null; - }); - - itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - { - const MAX_NAME_LENGTH = 64; - const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); - const description = 'A'; - const tokenPrefix = 'A'; - - await expect(collectionHelper.methods - .createCollection([ - collectionName, - description, - tokenPrefix, - CollectionMode.Fungible, - DECIMALS, - ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, - ]) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); - } - { - const MAX_DESCRIPTION_LENGTH = 256; - const collectionName = 'A'; - const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); - const tokenPrefix = 'A'; - await expect(collectionHelper.methods - .createCollection([ - collectionName, - description, - tokenPrefix, - CollectionMode.Fungible, - DECIMALS, - ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, - ]) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); - } - { - const MAX_TOKEN_PREFIX_LENGTH = 16; - const collectionName = 'A'; - const description = 'A'; - const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); - await expect(collectionHelper.methods - .createCollection([ - collectionName, - description, - tokenPrefix, - CollectionMode.Fungible, - DECIMALS, - ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, - ]) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); - } - }); - - itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - const expects = [0n, 1n, 30n].map(async value => { - await expect(collectionHelper.methods - .createCollection([ - 'Peasantry', - 'absolutely anything', - 'TWIW', - CollectionMode.Fungible, - DECIMALS, - ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, - ]) - .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); - }); - await Promise.all(expects); - }); - }); - - describe('Nonfungible collection', () => { - before(async function() { - await usingEthPlaygrounds((helper) => { - requirePalletsOrSkip(this, helper, [Pallets.NFT]); - return Promise.resolve(); - }); - }); - - itEth('Create collection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const name = 'CollectionEVM'; - const description = 'Some description'; - const prefix = 'token prefix'; - - // todo:playgrounds this might fail when in async environment. - const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; - const {collectionId, collectionAddress, events} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'nft')).send(); - - expect(events).to.be.deep.equal([ - { - address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F', - event: 'CollectionCreated', - args: { - owner: owner, - collectionId: collectionAddress, - }, - }, - ]); - - const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; - - const collection = helper.nft.getCollectionObject(collectionId); - const data = (await collection.getData())!; - - // Parallel test safety - expect(collectionCountAfter - collectionCountBefore).to.be.gte(1); - expect(collectionId).to.be.eq(collectionCountAfter); - expect(data.name).to.be.eq(name); - expect(data.description).to.be.eq(description); - expect(data.raw.tokenPrefix).to.be.eq(prefix); - expect(data.raw.mode).to.be.eq('NFT'); - - const options = await collection.getOptions(); - - expect(options.tokenPropertyPermissions).to.be.empty; - }); - - // this test will occasionally fail when in async environment. - itEth('Check collection address exist', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1; - const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId); - const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); - - expect(await collectionHelpers.methods - .isCollectionExist(expectedCollectionAddress) - .call()).to.be.false; - - await collectionHelpers.methods - .createCollection([ - 'A', - 'A', - 'A', - CollectionMode.Nonfungible, - 0, - ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, - ]) - .send({value: Number(2n * helper.balance.getOneTokenNominal())}); - - expect(await collectionHelpers.methods - .isCollectionExist(expectedCollectionAddress) - .call()).to.be.true; - }); - }); - - describe('Create RFT collection from EVM', () => { - before(async function() { - await usingEthPlaygrounds((helper) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - return Promise.resolve(); - }); - }); - - itEth('Create collection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const name = 'CollectionEVM'; - const description = 'Some description'; - const prefix = 'token prefix'; - - const {collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'rft')).send(); - const data = (await helper.rft.getData(collectionId))!; - const collection = helper.rft.getCollectionObject(collectionId); - - expect(data.name).to.be.eq(name); - expect(data.description).to.be.eq(description); - expect(data.raw.tokenPrefix).to.be.eq(prefix); - expect(data.raw.mode).to.be.eq('ReFungible'); - - const options = await collection.getOptions(); - - expect(options.tokenPropertyPermissions).to.be.empty; - }); - - itEth('Create collection with properties & get description', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const name = 'CollectionEVM'; - const description = 'Some description'; - const prefix = 'token prefix'; - const baseUri = 'BaseURI'; - - const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri); - const contract = helper.ethNativeContract.collection(collectionAddress, 'nft'); - - const collection = helper.rft.getCollectionObject(collectionId); - const data = (await collection.getData())!; - - expect(data.name).to.be.eq(name); - expect(data.description).to.be.eq(description); - expect(data.raw.tokenPrefix).to.be.eq(prefix); - expect(data.raw.mode).to.be.eq('ReFungible'); - - expect(await contract.methods.description().call()).to.deep.equal(description); - - const options = await collection.getOptions(); - expect(options.tokenPropertyPermissions).to.be.deep.equal([ - { - key: 'URI', - permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, - }, - { - key: 'URISuffix', - permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, - }, - ]); - }); - - itEth('Set sponsorship', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const ss58Format = helper.chain.getChainProperties().ss58Format; - const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Sponsor', 'absolutely anything', 'ENVY', 'rft')).send(); - - const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - await collection.methods.setCollectionSponsorCross(sponsorCross).send(); - - let data = (await helper.rft.getData(collectionId))!; - expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - - await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor); - await sponsorCollection.methods.confirmCollectionSponsorship().send(); - - data = (await helper.rft.getData(collectionId))!; - expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - }); - - itEth('Collection address exist', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; - const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); - - expect(await collectionHelpers - .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) - .to.be.false; - - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'rft')).send(); - expect(await collectionHelpers - .methods.isCollectionExist(collectionAddress).call()) - .to.be.true; - - // check collectionOwner: - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); - const collectionOwner = await collectionEvm.methods.collectionOwner().call(); - expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true)); - }); - - itEth('destroyCollection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'OLF', 'rft')).send(); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - - await expect(collectionHelper.methods - .destroyCollection(collectionAddress) - .send({from: owner})).to.be.fulfilled; - - expect(await collectionHelper.methods - .isCollectionExist(collectionAddress) - .call()).to.be.false; - expect(await helper.collection.getData(collectionId)).to.be.null; - }); - - itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - { - const MAX_NAME_LENGTH = 64; - const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); - const description = 'A'; - const tokenPrefix = 'A'; - - await expect(collectionHelper.methods - .createCollection([ - collectionName, - description, - tokenPrefix, - CollectionMode.Refungible, - DECIMALS, - ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, - ]) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); - } - { - const MAX_DESCRIPTION_LENGTH = 256; - const collectionName = 'A'; - const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); - const tokenPrefix = 'A'; - await expect(collectionHelper.methods - .createCollection([ - collectionName, - description, - tokenPrefix, - CollectionMode.Refungible, - DECIMALS, - ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, - ]) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); - } - { - const MAX_TOKEN_PREFIX_LENGTH = 16; - const collectionName = 'A'; - const description = 'A'; - const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); - await expect(collectionHelper.methods - .createCollection([ - collectionName, - description, - tokenPrefix, - CollectionMode.Refungible, - DECIMALS, - ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, - ]) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); - } - }); - - itEth('(!negative test!) Create collection (no funds)', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - await expect(collectionHelper.methods - .createCollection([ - 'Peasantry', - 'absolutely anything', - 'TWIW', - CollectionMode.Refungible, - 0, - ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY, - ]) - .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); - }); - }); - - describe('Sponsoring', () => { - itEth('Сan remove collection sponsor', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor); - - const {collectionAddress} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'Sponsor collection', - description: '1', - tokenPrefix: '1', - collectionMode: 'nft', - pendingSponsor: sponsorCross, - }, - ).send(); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true; - - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); - expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - - await collectionEvm.methods.removeCollectionSponsor().send({from: owner}); - - sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner}); - expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000'); - }); - - itEth('Can sponsor from evm address via access list', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsorEth = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddr(sponsorEth); - - const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'Sponsor collection', - description: '1', - tokenPrefix: '1', - collectionMode: 'nft', - pendingSponsor: sponsorCross, - limits: [{field: CollectionLimitField.SponsoredDataRateLimit, value: 30n}], - tokenPropertyPermissions: [{key: 'key', permissions: [{code: TokenPermissionField.TokenOwner, value: true}]}], - }, - '', - ); - - const collectionSub = helper.nft.getCollectionObject(collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - let sponsorship = (await collectionSub.getData())!.raw.sponsorship; - expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); - // Account cannot confirm sponsorship if it is not set as a sponsor - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - // Sponsor can confirm sponsorship: - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); - sponsorship = (await collectionSub.getData())!.raw.sponsorship; - expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); - - // Create user with no balance: - const user = helper.ethCrossAccount.createAccount(); - const nextTokenId = await collectionEvm.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - - // Set collection permissions: - const oldPermissions = (await collectionSub.getData())!.raw.permissions; - expect(oldPermissions.mintMode).to.be.false; - expect(oldPermissions.access).to.be.equal('Normal'); - - await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); - await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner}); - await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); - - const newPermissions = (await collectionSub.getData())!.raw.permissions; - expect(newPermissions.mintMode).to.be.true; - expect(newPermissions.access).to.be.equal('AllowList'); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); - const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); - - // User can mint token without balance: - { - const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth}); - const event = helper.eth.normalizeEvents(result.events) - .find(event => event.event === 'Transfer'); - - expect(event).to.be.deep.equal({ - address: collectionAddress, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user.eth, - tokenId: '1', - }, - }); - - // await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value1')}]).send({from: user.eth}); - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); - const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth)); - - expect(await collectionEvm.methods.properties(nextTokenId, []).call()) - .to.be.like([ - [ - 'key', - '0x' + Buffer.from('Value').toString('hex'), - ], - ]); - expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); - expect(userBalanceAfter).to.be.eq(userBalanceBefore); - expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; - } - }); - - itEth('Check that transaction via EVM spend money from sponsor address', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor); - const user = helper.eth.createAccount(); - const userCross = helper.ethCrossAccount.fromAddress(user); - - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'Sponsor collection', - description: '1', - tokenPrefix: '1', - collectionMode: 'nft', - pendingSponsor: sponsorCross, - adminList: [userCross], - }, - '', - ); - - const collectionSub = helper.nft.getCollectionObject(collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - // Set collection sponsor: - let collectionData = (await collectionSub.getData())!; - expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - collectionData = (await collectionSub.getData())!; - expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - - const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); - const tokenId = mintingResult.events.Transfer.returnValues.tokenId; - - const event = helper.eth.normalizeEvents(mintingResult.events) - .find(event => event.event === 'Transfer'); - const address = helper.ethAddress.fromCollectionId(collectionId); - - expect(event).to.be.deep.equal({ - address, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: '1', - }, - }); - expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI'); - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; - }); - - itEth('Can reassign collection sponsor', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsorEth = await helper.eth.createAccountWithBalance(donor); - const sponsorCrossEth = helper.ethCrossAccount.fromAddr(sponsorEth); - const [sponsorSub] = await helper.arrange.createAccounts([100n], donor); - const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub); - - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'Sponsor collection', - description: '1', - tokenPrefix: '1', - collectionMode: 'nft', - pendingSponsor: sponsorCrossEth, - }, - '', - ); - const collectionSub = helper.nft.getCollectionObject(collectionId); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - // Set and confirm sponsor: - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); - - // Can reassign sponsor: - await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner}); - const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship; - expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address}); - }); - - [ - 'transfer', - 'transferCross', - 'transferFrom', - 'transferFromCross', - ].map(testCase => - itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor); - const user = await helper.eth.createAccountWithBalance(donor); - const userCross = helper.ethCrossAccount.fromAddress(user); - - const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'Sponsor collection', - description: '1', - tokenPrefix: '1', - collectionMode: 'rft', - pendingSponsor: sponsorCross, - adminList: [userCross], - }, - '', - ); - const receiver = await helper.eth.createAccountWithBalance(donor); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - - const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); - const tokenId = result.events.Transfer.returnValues.tokenId; - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - - switch (testCase) { - case 'transfer': - await collectionEvm.methods.transfer(receiver, tokenId).send({from: user}); - break; - case 'transferCross': - await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user}); - break; - case 'transferFrom': - await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user}); - break; - case 'transferFromCross': - await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user}); - break; - } - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; - const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); - expect(userBalanceAfter).to.be.eq(userBalanceBefore); - })); - }); - - describe('Collection admins', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {mode: 'ft' as const, requiredPallets: []}, - ].map(testCase => { - itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => { - // arrange - const owner = await helper.eth.createAccountWithBalance(donor); - const adminSub = await privateKey('//admin2'); - const adminEth = helper.eth.createAccount().toLowerCase(); - - const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); - const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); - - const {collectionAddress, collectionId} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'Mint collection', - description: 'a', - tokenPrefix: 'b', - collectionMode: testCase.mode, - adminList: [adminCrossSub, adminCrossEth], - }, - ).send(); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true); - - // 1. Expect api.rpc.unique.adminlist returns admins: - const adminListRpc = await helper.collection.getAdmins(collectionId); - expect(adminListRpc).to.has.length(2); - expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}]); - - // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist - let adminListEth = await collectionEvm.methods.collectionAdmins().call(); - adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element)); - expect(adminListRpc).to.be.like(adminListEth); - - // 3. check isOwnerOrAdminCross returns true: - expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true; - expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true; - }); - }); - - itEth('cross account admin can mint', async ({helper}) => { - // arrange: create collection and accounts - const owner = await helper.eth.createAccountWithBalance(donor); - const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase(); - const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); - const [adminSub] = await helper.arrange.createAccounts([100n], donor); - const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'Mint collection', - description: 'a', - tokenPrefix: 'b', - collectionMode: 'nft', - adminList: [adminCrossSub, adminCrossEth], - }, - 'uri', - ); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - - // admin (sub and eth) can mint token: - await collectionEvm.methods.mint(owner).send({from: adminEth}); - await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}}); - - expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2); - }); - - itEth('cannot add invalid cross account admin', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const [admin] = await helper.arrange.createAccounts([100n, 100n], donor); - - const adminCross = { - eth: helper.address.substrateToEth(admin.address), - sub: admin.addressRaw, - }; - - await expect(helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: 'nft', - adminList: [adminCross], - }, - ).call()).to.be.rejected; - }); - - itEth('Remove [cross] admin by owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const [adminSub] = await helper.arrange.createAccounts([10n], donor); - const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase(); - const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); - const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); - - const {collectionAddress, collectionId} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: 'nft', - adminList: [adminCrossSub, adminCrossEth], - }, - ).send(); - - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - { - const adminList = await helper.collection.getAdmins(collectionId); - expect(adminList).to.deep.include({Substrate: adminSub.address}); - expect(adminList).to.deep.include({Ethereum: adminEth}); - } - - await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send(); - await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send(); - const adminList = await helper.collection.getAdmins(collectionId); - expect(adminList.length).to.be.eq(0); - - // Non admin cannot mint: - await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/); - await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected; - }); - }); - - describe('Collection limits', () => { - describe('Can set collection limits', () => { - [ - {case: 'nft' as const}, - {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {case: 'ft' as const}, - ].map(testCase => - itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const limits = { - accountTokenOwnershipLimit: 1000n, - sponsoredDataSize: 1024n, - sponsoredDataRateLimit: 30n, - tokenLimit: 1000000n, - sponsorTransferTimeout: 6n, - sponsorApproveTimeout: 6n, - ownerCanTransfer: 1n, - ownerCanDestroy: 0n, - transfersEnabled: 0n, - }; - - const {collectionId, collectionAddress} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'Limits', - description: 'absolutely anything', - tokenPrefix: 'FLO', - collectionMode: testCase.case, - limits: [ - {field: CollectionLimitField.AccountTokenOwnership, value: limits.accountTokenOwnershipLimit}, - {field: CollectionLimitField.SponsoredDataSize, value: limits.sponsoredDataSize}, - {field: CollectionLimitField.SponsoredDataRateLimit, value: limits.sponsoredDataRateLimit}, - {field: CollectionLimitField.TokenLimit, value: limits.tokenLimit}, - {field: CollectionLimitField.SponsorTransferTimeout, value: limits.sponsorTransferTimeout}, - {field: CollectionLimitField.SponsorApproveTimeout, value: limits.sponsorApproveTimeout}, - {field: CollectionLimitField.OwnerCanTransfer, value: limits.ownerCanTransfer}, - {field: CollectionLimitField.OwnerCanDestroy, value: limits.ownerCanDestroy}, - {field: CollectionLimitField.TransferEnabled, value: limits.transfersEnabled}, - ], - }, - ).send(); - - const expectedLimits = { - accountTokenOwnershipLimit: 1000, - sponsoredDataSize: 1024, - sponsoredDataRateLimit: {blocks: 30}, - tokenLimit: 1000000, - sponsorTransferTimeout: 6, - sponsorApproveTimeout: 6, - ownerCanTransfer: true, - ownerCanDestroy: false, - transfersEnabled: false, - }; - - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); - - // Check limits from sub: - const data = (await helper.rft.getData(collectionId))!; - expect(data.raw.limits).to.deep.eq(expectedLimits); - expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits); - // Check limits from eth: - const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner}); - expect(limitsEvm).to.have.length(9); - expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]); - expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]); - expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]); - expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]); - expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]); - expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]); - expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]); - expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]); - expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]); - })); - }); - - describe('(!negative test!) Cannot set invalid collection limits', () => { - [ - {case: 'nft' as const}, - {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {case: 'ft' as const}, - ].map(testCase => - itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { - const invalidLimits = { - accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER), - transfersEnabled: 3, - }; - - const createCollectionData = { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'Limits', - description: 'absolutely anything', - tokenPrefix: 'ISNI', - collectionMode: testCase.case, - }; - - const owner = await helper.eth.createAccountWithBalance(donor); - await expect(helper.eth.createCollection( - owner, - { - ...createCollectionData, - limits: [{field: 9 as CollectionLimitField, value: 1n}], - }, - ).call()).to.be.rejectedWith('value not convertible into enum "CollectionLimitField"'); - - await expect(helper.eth.createCollection( - owner, - { - ...createCollectionData, - limits: [{field: CollectionLimitField.AccountTokenOwnership, value: invalidLimits.accountTokenOwnershipLimit}], - }, - ).call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`); - - await expect(helper.eth.createCollection( - owner, - { - ...createCollectionData, - limits: [{field: CollectionLimitField.TransferEnabled, value: 3n}], - }, - ).call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`); - - await expect(helper.eth.createCollection( - owner, - { - ...createCollectionData, - limits: [{field: CollectionLimitField.SponsoredDataSize, value: -1n}], - }, - ).call()).to.be.rejectedWith('value out-of-bounds'); - })); - }); - }); - - describe('Collection properties', () => { - - [ - {mode: 'nft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, - {mode: 'rft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, - {mode: 'ft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, - ].map(testCase => - itEth.ifWithPallets(`Collection properties can be set for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const callerCross = helper.ethCrossAccount.fromAddress(caller); - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId, collectionAddress} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'name', - description: 'test', - tokenPrefix: 'test', - collectionMode: testCase.mode, - adminList: [callerCross], - properties: testCase.methodParams, - }, - ).send(); - - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller); - - const raw = (await helper[testCase.mode].getData(collectionId))?.raw; - expect(raw.properties).to.deep.equal(testCase.expectedProps); - - // collectionProperties returns properties: - expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value))); - })); - - [ - {mode: 'nft' as const}, - {mode: 'rft' as const}, - {mode: 'ft' as const}, - ].map(testCase => - itEth.ifWithPallets(`Collection properties can be deleted for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const callerCross = helper.ethCrossAccount.fromAddress(caller); - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId, collectionAddress} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'name', - description: 'test', - tokenPrefix: 'test', - collectionMode: testCase.mode, - adminList: [callerCross], - properties:[ - {key: 'testKey1', value: Buffer.from('testValue1')}, - {key: 'testKey2', value: Buffer.from('testValue2')}, - {key: 'testKey3', value: Buffer.from('testValue3')}], - }, - ).send(); - - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller); - - await collectionEvm.methods.deleteCollectionProperties(['testKey1', 'testKey2']).send({from: caller}); - - const raw = (await helper[testCase.mode].getData(collectionId))?.raw; - - expect(raw.properties.length).to.equal(1); - expect(raw.properties).to.deep.equal([{key: 'testKey3', value: 'testValue3'}]); - })); - - itEth('(!negative test!) Cannot set invalid properties', async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const callerCross = helper.ethCrossAccount.fromAddress(caller); - const owner = await helper.eth.createAccountWithBalance(donor); - const createCollectionData = { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'name', - description: 'test', - tokenPrefix: 'test', - collectionMode: 'nft' as TCollectionMode, - adminList: [callerCross], - }; - await expect(helper.eth.createCollection( - owner, - { - ...createCollectionData, - properties: [{key: '', value: Buffer.from('val1')}], - }, - ).call()).to.be.rejected; - - await expect(helper.eth.createCollection( - owner, - { - ...createCollectionData, - properties: [{key: 'déjà vu', value: Buffer.from('hmm...')}], - }, - ).call()).to.be.rejected; - - await expect(helper.eth.createCollection( - owner, - { - ...createCollectionData, - properties: [{key: 'a'.repeat(257), value: Buffer.from('val3')}], - }, - ).call()).to.be.rejected; - }); - - itEth('(!negative test!) cannot delete properties of non-owned collections', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'name', - description: 'test', - tokenPrefix: 'test', - collectionMode: 'nft', - properties:[ - {key: 'testKey1', value: Buffer.from('testValue1')}, - {key: 'testKey2', value: Buffer.from('testValue2')}], - }, - ).send(); - - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); - - await expect(collectionEvm.methods.deleteCollectionProperties(['testKey2']).send({from: caller})).to.be.rejected; - }); - }); - - describe('Token property permissions', () => { - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.ethCrossAccount.createAccountWithBalance(donor); - for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) { - const {collectionId, collectionAddress} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: testCase.mode, - adminList: [caller], - tokenPropertyPermissions: [ - { - key: 'testKey', - permissions: [ - {code: TokenPermissionField.Mutable, value: mutable}, - {code: TokenPermissionField.TokenOwner, value: tokenOwner}, - {code: TokenPermissionField.CollectionAdmin, value: collectionAdmin}, - ], - }, - ], - }, - ).send(); - const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - - expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{ - key: 'testKey', - permission: {mutable, collectionAdmin, tokenOwner}, - }]); - - expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([ - ['testKey', [ - [TokenPermissionField.Mutable.toString(), mutable], - [TokenPermissionField.TokenOwner.toString(), tokenOwner], - [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]], - ], - ]); - } - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions`, testCase.requiredPallets, async({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId, collectionAddress} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: testCase.mode, - tokenPropertyPermissions: [ - { - key: 'testKey_0', - permissions: [ - {code: TokenPermissionField.Mutable, value: true}, - {code: TokenPermissionField.TokenOwner, value: true}, - {code: TokenPermissionField.CollectionAdmin, value: true}], - }, - { - key: 'testKey_1', - permissions: [ - {code: TokenPermissionField.Mutable, value: true}, - {code: TokenPermissionField.TokenOwner, value: false}, - {code: TokenPermissionField.CollectionAdmin, value: true}], - }, - { - key: 'testKey_2', - permissions: [ - {code: TokenPermissionField.Mutable, value: false}, - {code: TokenPermissionField.TokenOwner, value: true}, - {code: TokenPermissionField.CollectionAdmin, value: false}], - }, - ], - }, - ).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - - expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([ - { - key: 'testKey_0', - permission: {mutable: true, tokenOwner: true, collectionAdmin: true}, - }, - { - key: 'testKey_1', - permission: {mutable: true, tokenOwner: false, collectionAdmin: true}, - }, - { - key: 'testKey_2', - permission: {mutable: false, tokenOwner: true, collectionAdmin: false}, - }, - ]); - - expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([ - ['testKey_0', [ - [TokenPermissionField.Mutable.toString(), true], - [TokenPermissionField.TokenOwner.toString(), true], - [TokenPermissionField.CollectionAdmin.toString(), true]], - ], - ['testKey_1', [ - [TokenPermissionField.Mutable.toString(), true], - [TokenPermissionField.TokenOwner.toString(), false], - [TokenPermissionField.CollectionAdmin.toString(), true]], - ], - ['testKey_2', [ - [TokenPermissionField.Mutable.toString(), false], - [TokenPermissionField.TokenOwner.toString(), true], - [TokenPermissionField.CollectionAdmin.toString(), false]], - ], - ]); - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`Can be deleted for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createCollection( - caller, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: testCase.mode, - adminList: [receiver], - tokenPropertyPermissions: [ - { - key: 'testKey', - permissions: [ - {code: TokenPermissionField.Mutable, value: true}, - {code: TokenPermissionField.CollectionAdmin, value: true}], - }, - { - key: 'testKey_1', - permissions: [ - {code: TokenPermissionField.Mutable, value: true}, - {code: TokenPermissionField.CollectionAdmin, value: true}], - }, - ], - }, - ).send(); - - const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller); - const tokenId = (await collection.methods.mintCross(receiver, [{key: 'testKey', value: Buffer.from('testValue')}, {key: 'testKey_1', value: Buffer.from('testValue_1')}]).send()).events.Transfer.returnValues.tokenId; - expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(2); - - await collection.methods.deleteProperties(tokenId, ['testKey', 'testKey_1']).send({from: caller}); - expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(0); - })); - }); - - describe('Nesting', () => { - itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: 'nft', - nestingSettings: {token_owner: true, collection_admin: false, restricted: []}, - }, - ).send(); - - const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - // Create a token to be nested to - const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner}); - const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId; - const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId); - - // Create a nested token - const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner}); - const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId; - expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress); - - // Create a token to be nested and nest - const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner}); - const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId; - - await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner}); - expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress); - - // Unnest token back - await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner}); - expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner); - }); - - itEth('NFT: collectionNesting()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress: unnestedCollectionAddress} = await helper.eth.createCollection( - owner, - new CreateCollectionData('A', 'B', 'C', 'nft'), - ).send(); - - const unnestedContract = helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner); - expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]); - - const {collectionAddress} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: 'nft', - nestingSettings: {token_owner: true, collection_admin: false, restricted: [unnestedCollectionAddress.toString()]}, - }, - ).send(); - - const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress.toString()]]); - await contract.methods.setCollectionNesting([false, false, []]).send({from: owner}); - expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]); - }); - - itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionId, collectionAddress} = await helper.eth.createCollection( - owner, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: 'nft', - nestingSettings: {token_owner: false, collection_admin: false, restricted: []}, - }, - ).send(); - - const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - // Create a token to nest into - const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner}); - const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; - const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId); - - // Create a token to nest - const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner}); - const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId; - - // Try to nest - await expect(contract.methods - .transfer(targetNftTokenAddress, nftTokenId) - .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest'); - }); - }); - - describe('Flags', () => { - const createCollectionData = { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: 'nft' as TCollectionMode, - }; - - itEth('NFT: use numbers for flags', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - { - const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 0}).send(); - expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false}); - } - - { - const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 64}).send(); - expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true}); - } - }); - - itEth('NFT: can\'t set foreign flag number', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - { - await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 128}).call({from: owner})).to.be.rejectedWith(/internal flags were used/); - } - - { - await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 192}).call({from: owner})).to.be.rejectedWith(/internal flags were used/); - } - }); - - itEth('NFT: use enum for flags', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - { - const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata]}).send(); - expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true}); - } - }); - - itEth('NFT: foreign flag enum is ignored', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - { - await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/); - } - - { - await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata | CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/); - } - }); - }); -}); --- a/js-packages/tests/src/eth/createFTCollection.seqtest.ts +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {Pallets, requirePalletsOrSkip} from '../util/index.js'; -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; - -const DECIMALS = 18; - -describe('Create FT collection from EVM', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.Fungible]); - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Create collection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const name = 'CollectionEVM'; - const description = 'Some description'; - const prefix = 'token prefix'; - - // todo:playgrounds this might fail when in async environment. - const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; - - const {collectionId} = await helper.eth.createFungibleCollection(owner, name, DECIMALS, description, prefix); - - const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; - const data = (await helper.ft.getData(collectionId))!; - - expect(collectionCountAfter - collectionCountBefore).to.be.eq(1); - expect(collectionId).to.be.eq(collectionCountAfter); - expect(data.name).to.be.eq(name); - expect(data.description).to.be.eq(description); - expect(data.raw.tokenPrefix).to.be.eq(prefix); - expect(data.raw.mode).to.be.deep.eq({Fungible: DECIMALS.toString()}); - }); - - // todo:playgrounds this test will fail when in async environment. - itEth('Check collection address exist', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1; - const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId); - const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); - - expect(await collectionHelpers.methods - .isCollectionExist(expectedCollectionAddress) - .call()).to.be.false; - - - await helper.eth.createFungibleCollection(owner, 'A', DECIMALS, 'A', 'A'); - - - expect(await collectionHelpers.methods - .isCollectionExist(expectedCollectionAddress) - .call()).to.be.true; - }); -}); --- a/js-packages/tests/src/eth/createFTCollection.test.ts +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {evmToAddress} from '@polkadot/util-crypto'; -import {Pallets, requirePalletsOrSkip} from '../util/index.js'; -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import {CollectionLimitField} from './util/playgrounds/types.js'; - -const DECIMALS = 18; - -describe('Create FT collection from EVM', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.Fungible]); - donor = await privateKey({url: import.meta.url}); - }); - }); - - // TODO move sponsorship tests to another file: - // Soft-deprecated - itEth('[eth] Set sponsorship', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const ss58Format = helper.chain.getChainProperties().ss58Format; - const description = 'absolutely anything'; - - const {collectionId, collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', DECIMALS, description, 'ENVY'); - - const collection = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); - await collection.methods.setCollectionSponsor(sponsor).send(); - - let data = (await helper.rft.getData(collectionId))!; - expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - - await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true); - await sponsorCollection.methods.confirmCollectionSponsorship().send(); - - data = (await helper.rft.getData(collectionId))!; - expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - }); - - itEth('[cross] Set sponsorship', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const ss58Format = helper.chain.getChainProperties().ss58Format; - const description = 'absolutely anything'; - const {collectionId, collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', DECIMALS, description, 'ENVY'); - - const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - await collection.methods.setCollectionSponsorCross(sponsorCross).send(); - - let data = (await helper.rft.getData(collectionId))!; - expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - - await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor); - await sponsorCollection.methods.confirmCollectionSponsorship().send(); - - data = (await helper.rft.getData(collectionId))!; - expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - expect(await collection.methods.description().call()).to.deep.equal(description); - }); - - itEth('Collection address exist', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; - const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); - - expect(await collectionHelpers - .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) - .to.be.false; - - const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT'); - expect(await collectionHelpers - .methods.isCollectionExist(collectionAddress).call()) - .to.be.true; - - // check collectionOwner: - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); - const collectionOwner = await collectionEvm.methods.collectionOwner().call(); - expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true)); - }); - - itEth('destroyCollection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT'); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - - const result = await collectionHelper.methods - .destroyCollection(collectionAddress) - .send({from: owner}); - - const events = helper.eth.normalizeEvents(result.events); - - expect(events).to.be.deep.equal([ - { - address: collectionHelper.options.address, - event: 'CollectionDestroyed', - args: { - collectionId: collectionAddress, - }, - }, - ]); - - expect(await collectionHelper.methods - .isCollectionExist(collectionAddress) - .call()).to.be.false; - expect(await helper.collection.getData(collectionId)).to.be.null; - }); -}); - -describe('(!negative tests!) Create FT collection from EVM', () => { - let donor: IKeyringPair; - let nominal: bigint; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.Fungible]); - donor = await privateKey({url: import.meta.url}); - nominal = helper.balance.getOneTokenNominal(); - }); - }); - - itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - { - const MAX_NAME_LENGTH = 64; - const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); - const description = 'A'; - const tokenPrefix = 'A'; - - await expect(collectionHelper.methods - .createFTCollection(collectionName, DECIMALS, description, tokenPrefix) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); - } - { - const MAX_DESCRIPTION_LENGTH = 256; - const collectionName = 'A'; - const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); - const tokenPrefix = 'A'; - await expect(collectionHelper.methods - .createFTCollection(collectionName, DECIMALS, description, tokenPrefix) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); - } - { - const MAX_TOKEN_PREFIX_LENGTH = 16; - const collectionName = 'A'; - const description = 'A'; - const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); - await expect(collectionHelper.methods - .createFTCollection(collectionName, DECIMALS, description, tokenPrefix) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); - } - }); - - itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - const expects = [0n, 1n, 30n].map(async value => { - await expect(collectionHelper.methods - .createFTCollection('Peasantry', DECIMALS, 'absolutely anything', 'TWIW') - .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); - }); - await Promise.all(expects); - }); - - // Soft-deprecated - itEth('(!negative test!) [eth] Check owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const peasant = helper.eth.createAccount(); - const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE'); - const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant, true); - const EXPECTED_ERROR = 'NoPermission'; - { - const sponsor = await helper.eth.createAccountWithBalance(donor); - await expect(peasantCollection.methods - .setCollectionSponsor(sponsor) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor, true); - await expect(sponsorCollection.methods - .confirmCollectionSponsorship() - .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - } - { - await expect(peasantCollection.methods - .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - } - }); - - itEth('(!negative test!) [cross] Check owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const peasant = helper.eth.createAccount(); - const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE'); - const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant); - const EXPECTED_ERROR = 'NoPermission'; - { - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - await expect(peasantCollection.methods - .setCollectionSponsorCross(sponsorCross) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor); - await expect(sponsorCollection.methods - .confirmCollectionSponsorship() - .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - } - { - await expect(peasantCollection.methods - .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - } - }); -}); --- a/js-packages/tests/src/eth/createNFTCollection.seqtest.ts +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; - - -describe('Create NFT collection from EVM', () => { - let donor: IKeyringPair; - - before(async function () { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Create collection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const name = 'CollectionEVM'; - const description = 'Some description'; - const prefix = 'token prefix'; - - // todo:playgrounds this might fail when in async environment. - const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; - const {collectionId, collectionAddress, events} = await helper.eth.createNFTCollection(owner, name, description, prefix); - - expect(events).to.be.deep.equal([ - { - address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F', - event: 'CollectionCreated', - args: { - owner: owner, - collectionId: collectionAddress, - }, - }, - ]); - - const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; - - const collection = helper.nft.getCollectionObject(collectionId); - const data = (await collection.getData())!; - - expect(collectionCountAfter - collectionCountBefore).to.be.eq(1); - expect(collectionId).to.be.eq(collectionCountAfter); - expect(data.name).to.be.eq(name); - expect(data.description).to.be.eq(description); - expect(data.raw.tokenPrefix).to.be.eq(prefix); - expect(data.raw.mode).to.be.eq('NFT'); - - const options = await collection.getOptions(); - - expect(options.tokenPropertyPermissions).to.be.empty; - }); - - // this test will occasionally fail when in async environment. - itEth('Check collection address exist', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1; - const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId); - const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); - - expect(await collectionHelpers.methods - .isCollectionExist(expectedCollectionAddress) - .call()).to.be.false; - - await collectionHelpers.methods - .createNFTCollection('A', 'A', 'A') - .send({value: Number(2n * helper.balance.getOneTokenNominal())}); - - expect(await collectionHelpers.methods - .isCollectionExist(expectedCollectionAddress) - .call()).to.be.true; - }); -}); --- a/js-packages/tests/src/eth/createNFTCollection.test.ts +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {evmToAddress} from '@polkadot/util-crypto'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import {CollectionLimitField} from './util/playgrounds/types.js'; - - -describe('Create NFT collection from EVM', () => { - let donor: IKeyringPair; - - before(async function () { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Create collection with properties & get desctription', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const name = 'CollectionEVM'; - const description = 'Some description'; - const prefix = 'token prefix'; - const baseUri = 'BaseURI'; - - const {collectionId, collectionAddress, events} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, name, description, prefix, baseUri); - const contract = helper.ethNativeContract.collection(collectionAddress, 'nft'); - - expect(events).to.be.deep.equal([ - { - address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F', - event: 'CollectionCreated', - args: { - owner: owner, - collectionId: collectionAddress, - }, - }, - ]); - - const collection = helper.nft.getCollectionObject(collectionId); - const data = (await collection.getData())!; - - expect(data.name).to.be.eq(name); - expect(data.description).to.be.eq(description); - expect(data.raw.tokenPrefix).to.be.eq(prefix); - expect(data.raw.mode).to.be.eq('NFT'); - - expect(await contract.methods.description().call()).to.deep.equal(description); - - const options = await collection.getOptions(); - expect(options.tokenPropertyPermissions).to.be.deep.equal([ - { - key: 'URI', - permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, - }, - { - key: 'URISuffix', - permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, - }, - ]); - }); - - // Soft-deprecated - itEth('[eth] Set sponsorship', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const ss58Format = helper.chain.getChainProperties().ss58Format; - const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC'); - - const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - await collection.methods.setCollectionSponsor(sponsor).send(); - - let data = (await helper.nft.getData(collectionId))!; - expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - - await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true); - await sponsorCollection.methods.confirmCollectionSponsorship().send(); - - data = (await helper.nft.getData(collectionId))!; - expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - }); - - itEth('[cross] Set sponsorship & get description', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const ss58Format = helper.chain.getChainProperties().ss58Format; - const description = 'absolutely anything'; - const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', description, 'ROC'); - - const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - await collection.methods.setCollectionSponsorCross(sponsorCross).send(); - - let data = (await helper.nft.getData(collectionId))!; - expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - - await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor); - await sponsorCollection.methods.confirmCollectionSponsorship().send(); - - data = (await helper.nft.getData(collectionId))!; - expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - - expect(await sponsorCollection.methods.description().call()).to.deep.equal(description); - }); - - itEth('Collection address exist', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; - const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); - - expect(await collectionHelpers - .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) - .to.be.false; - - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Exister', 'absolutely anything', 'EVC'); - expect(await collectionHelpers - .methods.isCollectionExist(collectionAddress).call()) - .to.be.true; - - // check collectionOwner: - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); - const collectionOwner = await collectionEvm.methods.collectionOwner().call(); - expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true)); - }); -}); - -describe('(!negative tests!) Create NFT collection from EVM', () => { - let donor: IKeyringPair; - let nominal: bigint; - - before(async function () { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - nominal = helper.balance.getOneTokenNominal(); - }); - }); - - itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - { - const MAX_NAME_LENGTH = 64; - const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); - const description = 'A'; - const tokenPrefix = 'A'; - - await expect(collectionHelper.methods - .createNFTCollection(collectionName, description, tokenPrefix) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); - - } - { - const MAX_DESCRIPTION_LENGTH = 256; - const collectionName = 'A'; - const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); - const tokenPrefix = 'A'; - await expect(collectionHelper.methods - .createNFTCollection(collectionName, description, tokenPrefix) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); - } - { - const MAX_TOKEN_PREFIX_LENGTH = 16; - const collectionName = 'A'; - const description = 'A'; - const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); - await expect(collectionHelper.methods - .createNFTCollection(collectionName, description, tokenPrefix) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); - } - }); - - itEth('(!negative test!) Create collection (no funds)', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - await expect(collectionHelper.methods - .createNFTCollection('Peasantry', 'absolutely anything', 'CVE') - .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); - }); - - // Soft-deprecated - itEth('(!negative test!) [eth] Check owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const malfeasant = helper.eth.createAccount(); - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR'); - const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant, true); - const EXPECTED_ERROR = 'NoPermission'; - { - const sponsor = await helper.eth.createAccountWithBalance(donor); - await expect(malfeasantCollection.methods - .setCollectionSponsor(sponsor) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true); - await expect(sponsorCollection.methods - .confirmCollectionSponsorship() - .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - } - { - await expect(malfeasantCollection.methods - .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - } - }); - - itEth('(!negative test!) [cross] Check owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const malfeasant = helper.eth.createAccount(); - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR'); - const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant); - const EXPECTED_ERROR = 'NoPermission'; - { - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - await expect(malfeasantCollection.methods - .setCollectionSponsorCross(sponsorCross) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor); - await expect(sponsorCollection.methods - .confirmCollectionSponsorship() - .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - } - { - await expect(malfeasantCollection.methods - .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - } - }); - - itEth('destroyCollection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF'); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - - - const result = await collectionHelper.methods - .destroyCollection(collectionAddress) - .send({from: owner}); - - const events = helper.eth.normalizeEvents(result.events); - - expect(events).to.be.deep.equal([ - { - address: collectionHelper.options.address, - event: 'CollectionDestroyed', - args: { - collectionId: collectionAddress, - }, - }, - ]); - - expect(await collectionHelper.methods - .isCollectionExist(collectionAddress) - .call()).to.be.false; - expect(await helper.collection.getData(collectionId)).to.be.null; - }); -}); --- a/js-packages/tests/src/eth/createRFTCollection.test.ts +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {evmToAddress} from '@polkadot/util-crypto'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {Pallets, requirePalletsOrSkip} from '../util/index.js'; -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import {CollectionLimitField} from './util/playgrounds/types.js'; - - -describe('Create RFT collection from EVM', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Create collection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const name = 'CollectionEVM'; - const description = 'Some description'; - const prefix = 'token prefix'; - - const {collectionId} = await helper.eth.createRFTCollection(owner, name, description, prefix); - const data = (await helper.rft.getData(collectionId))!; - const collection = helper.rft.getCollectionObject(collectionId); - - expect(data.name).to.be.eq(name); - expect(data.description).to.be.eq(description); - expect(data.raw.tokenPrefix).to.be.eq(prefix); - expect(data.raw.mode).to.be.eq('ReFungible'); - - const options = await collection.getOptions(); - - expect(options.tokenPropertyPermissions).to.be.empty; - }); - - - - itEth('Create collection with properties & get description', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const name = 'CollectionEVM'; - const description = 'Some description'; - const prefix = 'token prefix'; - const baseUri = 'BaseURI'; - - const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri); - const contract = helper.ethNativeContract.collection(collectionAddress, 'nft'); - - const collection = helper.rft.getCollectionObject(collectionId); - const data = (await collection.getData())!; - - expect(data.name).to.be.eq(name); - expect(data.description).to.be.eq(description); - expect(data.raw.tokenPrefix).to.be.eq(prefix); - expect(data.raw.mode).to.be.eq('ReFungible'); - - expect(await contract.methods.description().call()).to.deep.equal(description); - - const options = await collection.getOptions(); - expect(options.tokenPropertyPermissions).to.be.deep.equal([ - { - key: 'URI', - permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, - }, - { - key: 'URISuffix', - permission: {mutable: true, collectionAdmin: true, tokenOwner: false}, - }, - ]); - }); - - // Soft-deprecated - itEth('[eth] Set sponsorship', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const ss58Format = helper.chain.getChainProperties().ss58Format; - const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY'); - - const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, true); - await collection.methods.setCollectionSponsor(sponsor).send(); - - let data = (await helper.rft.getData(collectionId))!; - expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - - await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true); - await sponsorCollection.methods.confirmCollectionSponsorship().send(); - - data = (await helper.rft.getData(collectionId))!; - expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - }); - - itEth('[cross] Set sponsorship', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const ss58Format = helper.chain.getChainProperties().ss58Format; - const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY'); - - const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - await collection.methods.setCollectionSponsorCross(sponsorCross).send(); - - let data = (await helper.rft.getData(collectionId))!; - expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - - await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor); - await sponsorCollection.methods.confirmCollectionSponsorship().send(); - - data = (await helper.rft.getData(collectionId))!; - expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); - }); - - itEth('Collection address exist', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; - const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); - - expect(await collectionHelpers - .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) - .to.be.false; - - const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Exister', 'absolutely anything', 'WIWT'); - expect(await collectionHelpers - .methods.isCollectionExist(collectionAddress).call()) - .to.be.true; - - // check collectionOwner: - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); - const collectionOwner = await collectionEvm.methods.collectionOwner().call(); - expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true)); - }); -}); - -describe('(!negative tests!) Create RFT collection from EVM', () => { - let donor: IKeyringPair; - let nominal: bigint; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - donor = await privateKey({url: import.meta.url}); - nominal = helper.balance.getOneTokenNominal(); - }); - }); - - itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - { - const MAX_NAME_LENGTH = 64; - const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); - const description = 'A'; - const tokenPrefix = 'A'; - - await expect(collectionHelper.methods - .createRFTCollection(collectionName, description, tokenPrefix) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); - } - { - const MAX_DESCRIPTION_LENGTH = 256; - const collectionName = 'A'; - const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); - const tokenPrefix = 'A'; - await expect(collectionHelper.methods - .createRFTCollection(collectionName, description, tokenPrefix) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); - } - { - const MAX_TOKEN_PREFIX_LENGTH = 16; - const collectionName = 'A'; - const description = 'A'; - const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); - await expect(collectionHelper.methods - .createRFTCollection(collectionName, description, tokenPrefix) - .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); - } - }); - - itEth('(!negative test!) Create collection (no funds)', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - await expect(collectionHelper.methods - .createRFTCollection('Peasantry', 'absolutely anything', 'TWIW') - .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); - }); - - // Soft-deprecated - itEth('(!negative test!) [eth] Check owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const peasant = helper.eth.createAccount(); - const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE'); - const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant, true); - const EXPECTED_ERROR = 'NoPermission'; - { - const sponsor = await helper.eth.createAccountWithBalance(donor); - await expect(peasantCollection.methods - .setCollectionSponsor(sponsor) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true); - await expect(sponsorCollection.methods - .confirmCollectionSponsorship() - .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - } - { - await expect(peasantCollection.methods - .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - } - }); - - itEth('(!negative test!) [cross] Check owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const peasant = helper.eth.createAccount(); - const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE'); - const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant); - const EXPECTED_ERROR = 'NoPermission'; - { - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - await expect(peasantCollection.methods - .setCollectionSponsorCross(sponsorCross) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor); - await expect(sponsorCollection.methods - .confirmCollectionSponsorship() - .call()).to.be.rejectedWith('ConfirmSponsorshipFail'); - } - { - await expect(peasantCollection.methods - .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) - .call()).to.be.rejectedWith(EXPECTED_ERROR); - } - }); - - itEth('destroyCollection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress, collectionId} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'OLF'); - const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - - await expect(collectionHelper.methods - .destroyCollection(collectionAddress) - .send({from: owner})).to.be.fulfilled; - - expect(await collectionHelper.methods - .isCollectionExist(collectionAddress) - .call()).to.be.false; - expect(await helper.collection.getData(collectionId)).to.be.null; - }); -}); --- a/js-packages/tests/src/eth/crossTransfer.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {itEth, usingEthPlaygrounds} from './util/index.js'; -import {CrossAccountId} from '@unique/playgrounds/src/unique.js'; -import type {IKeyringPair} from '@polkadot/types/types'; - -describe('Token transfer between substrate address and EVM address. Fungible', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - }); - }); - - itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => { - const bobCA = CrossAccountId.fromKeyring(bob).toICrossAccountId(); - const charlieCA = CrossAccountId.fromKeyring(charlie).toICrossAccountId(); - const charlieCAEth = CrossAccountId.fromKeyring(charlie).toEthereum().toICrossAccountId(); - - const collection = await helper.ft.mintCollection(alice); - await collection.setLimits(alice, {ownerCanTransfer: true}); - - await collection.mint(alice, 200n); - await collection.transfer(alice, charlieCAEth, 200n); - await collection.transferFrom(alice, charlieCAEth, charlieCA, 50n); - await collection.transfer(charlie, bobCA, 50n); - }); - - itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => { - const aliceProxy = await helper.eth.createAccountWithBalance(donor); - const bobProxy = await helper.eth.createAccountWithBalance(donor); - - const collection = await helper.ft.mintCollection(alice); - await collection.setLimits(alice, {ownerCanTransfer: true}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'ft', aliceProxy); - - await collection.mint(alice, 200n, {Ethereum: aliceProxy}); - await contract.methods.transfer(bobProxy, 50).send({from: aliceProxy}); - await collection.transferFrom(alice, {Ethereum: bobProxy}, CrossAccountId.fromKeyring(bob).toICrossAccountId(), 50n); - await collection.transfer(bob, CrossAccountId.fromKeyring(alice).toICrossAccountId(), 50n); - }); -}); - -describe('Token transfer between substrate address and EVM address. NFT', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - }); - }); - - itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => { - const charlieEth = CrossAccountId.fromKeyring(charlie, 'Ethereum').toICrossAccountId(); - - const collection = await helper.nft.mintCollection(alice); - await collection.setLimits(alice, {ownerCanTransfer: true}); - const token = await collection.mintToken(alice); - await token.transfer(alice, charlieEth); - await token.transferFrom(alice, charlieEth, CrossAccountId.fromKeyring(charlie).toICrossAccountId()); - await token.transfer(charlie, CrossAccountId.fromKeyring(bob).toICrossAccountId()); - }); - - itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => { - const aliceProxy = await helper.eth.createAccountWithBalance(donor); - const bobProxy = await helper.eth.createAccountWithBalance(donor); - - const collection = await helper.nft.mintCollection(alice); - await collection.setLimits(alice, {ownerCanTransfer: true}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft', aliceProxy); - - const token = await collection.mintToken(alice); - await token.transfer(alice, {Ethereum: aliceProxy}); - await contract.methods.transfer(bobProxy, 1).send({from: aliceProxy}); - await token.transferFrom(alice, {Ethereum: bobProxy}, {Substrate: bob.address}); - await token.transfer(bob, {Substrate: charlie.address}); - }); -}); --- a/js-packages/tests/src/eth/destroyCollection.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {Pallets} from '../util/index.js'; -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import type {TCollectionMode} from '@unique/playgrounds/src/types.js'; -import {CreateCollectionData} from './util/playgrounds/types.js'; - -describe('Destroy Collection from EVM', function() { - let donor: IKeyringPair; - const testCases = [ - {case: 'rft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'rft'], requiredPallets: [Pallets.ReFungible]}, - {case: 'nft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'nft'], requiredPallets: [Pallets.NFT]}, - {case: 'ft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'ft', 18], requiredPallets: [Pallets.Fungible]}, - ]; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - testCases.map((testCase) => - itEth.ifWithPallets(`Cannot burn non-owned or non-existing collection ${testCase.case}`, testCase.requiredPallets, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const signer = await helper.eth.createAccountWithBalance(donor); - - const unexistedCollection = helper.ethAddress.fromCollectionId(1000000); - - const collectionHelpers = await helper.ethNativeContract.collectionHelpers(signer); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData(...testCase.params as [string, string, string, TCollectionMode, number?])).send(); - // cannot burn collec - await expect(collectionHelpers.methods - .destroyCollection(collectionAddress) - .send({from: signer})).to.be.rejected; - - await expect(collectionHelpers.methods - .destroyCollection(unexistedCollection) - .send({from: signer})).to.be.rejected; - - expect(await collectionHelpers.methods - .isCollectionExist(unexistedCollection) - .call()).to.be.false; - - expect(await collectionHelpers.methods - .isCollectionExist(collectionAddress) - .call()).to.be.true; - })); -}); --- a/js-packages/tests/src/eth/ethFeesAreCorrect.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; - -describe('Eth fees are correct', () => { - let donor: IKeyringPair; - let minter: IKeyringPair; - let alice: IKeyringPair; - - before(async () => { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [minter, alice] = await helper.arrange.createAccounts([100n, 200n], donor); - }); - }); - - - itEth('web3 fees are the same as evm.call fees', async ({helper}) => { - const collection = await helper.nft.mintCollection(minter, {}); - - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const aliceEth = helper.address.substrateToEth(alice.address); - - const {tokenId: tokenA} = await collection.mintToken(minter, {Ethereum: owner}); - const {tokenId: tokenB} = await collection.mintToken(minter, {Ethereum: aliceEth}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - const balanceBeforeWeb3Transfer = await helper.balance.getEthereum(owner); - await contract.methods.transfer(receiver, tokenA).send({from: owner, maxFeePerGas: ((await helper.eth.getGasPrice())!).toString()}); - const balanceAfterWeb3Transfer = await helper.balance.getEthereum(owner); - const web3Diff = balanceBeforeWeb3Transfer - balanceAfterWeb3Transfer; - - const encodedCall = contract.methods.transfer(receiver, tokenB) - .encodeABI(); - - const balanceBeforeEvmCall = await helper.balance.getSubstrate(alice.address); - await helper.eth.sendEVM( - alice, - collectionAddress, - encodedCall, - '0', - ); - const balanceAfterEvmCall = await helper.balance.getSubstrate(alice.address); - const evmCallDiff = balanceBeforeEvmCall - balanceAfterEvmCall; - - expect(web3Diff).to.be.equal(evmCallDiff); - }); -}); --- a/js-packages/tests/src/eth/events.test.ts +++ /dev/null @@ -1,548 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {expect} from 'chai'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {itEth, usingEthPlaygrounds} from './util/index.js'; -import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; -import type {IEvent, TCollectionMode} from '@unique/playgrounds/src/types.js'; -import {Pallets, requirePalletsOrSkip} from '../util/index.js'; -import {CollectionLimitField, TokenPermissionField, CreateCollectionData} from './util/playgrounds/types.js'; -import type {NormalizedEvent} from './util/playgrounds/types.js'; - -let donor: IKeyringPair; - -before(async function () { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); -}); - -function clearEvents(ethEvents: NormalizedEvent[] | null, subEvents: IEvent[]) { - if(ethEvents !== null) { - ethEvents.splice(0); - } - subEvents.splice(0); -} - -async function testCollectionCreatedAndDestroy(helper: EthUniqueHelper, mode: TCollectionMode) { - const owner = await helper.eth.createAccountWithBalance(donor); - const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionCreated', 'CollectionDestroyed']}]); - const {collectionAddress, events: ethEvents} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); - - await helper.wait.newBlocks(1); - { - expect(ethEvents).to.containSubset([ - { - event: 'CollectionCreated', - args: { - owner: owner, - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'CollectionCreated'}]); - clearEvents(ethEvents, subEvents); - } - { - const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); - const result = await collectionHelper.methods.destroyCollection(collectionAddress).send({from:owner}); - await helper.wait.newBlocks(1); - expect(result.events).to.containSubset({ - CollectionDestroyed: { - returnValues: { - collectionId: collectionAddress, - }, - }, - }); - expect(subEvents).to.containSubset([{method: 'CollectionDestroyed'}]); - } - unsubscribe(); -} - -async function testCollectionPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); - const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); - - const ethEvents: any = []; - collectionHelper.events.allEvents((_: any, event: any) => { - ethEvents.push(event); - }); - const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionPropertySet', 'CollectionPropertyDeleted']}]); - { - await collection.methods.setCollectionProperties([{key: 'A', value: [0,1,2,3]}]).send({from:owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'CollectionPropertySet'}]); - clearEvents(ethEvents, subEvents); - } - { - await collection.methods.deleteCollectionProperties(['A']).send({from:owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'CollectionPropertyDeleted'}]); - } - unsubscribe(); -} - -async function testPropertyPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); - const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); - const ethEvents: any = []; - collectionHelper.events.allEvents((_: any, event: any) => { - ethEvents.push(event); - }); - const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['PropertyPermissionSet']}]); - await collection.methods.setTokenPropertyPermissions([ - ['A', [ - [TokenPermissionField.Mutable, true], - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, true]], - ], - ]).send({from: owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'PropertyPermissionSet'}]); - unsubscribe(); -} - -async function testAllowListAddressAddedAndRemoved(helper: EthUniqueHelper, mode: TCollectionMode) { - const owner = await helper.eth.createAccountWithBalance(donor); - const user = helper.ethCrossAccount.createAccount(); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); - const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); - const ethEvents: any[] = []; - collectionHelper.events.allEvents((_: any, event: any) => { - ethEvents.push(event); - }); - - const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['AllowListAddressAdded', 'AllowListAddressRemoved']}]); - { - await collection.methods.addToCollectionAllowListCross(user).send({from: owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'AllowListAddressAdded'}]); - clearEvents(ethEvents, subEvents); - } - { - await collection.methods.removeFromCollectionAllowListCross(user).send({from: owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'AllowListAddressRemoved'}]); - } - unsubscribe(); -} - -async function testCollectionAdminAddedAndRemoved(helper: EthUniqueHelper, mode: TCollectionMode) { - const owner = await helper.eth.createAccountWithBalance(donor); - const user = helper.ethCrossAccount.createAccount(); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); - const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); - const ethEvents: any = []; - collectionHelper.events.allEvents((_: any, event: any) => { - ethEvents.push(event); - }); - const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionAdminAdded', 'CollectionAdminRemoved']}]); - { - await collection.methods.addCollectionAdminCross(user).send({from: owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'CollectionAdminAdded'}]); - clearEvents(ethEvents, subEvents); - } - { - await collection.methods.removeCollectionAdminCross(user).send({from: owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'CollectionAdminRemoved'}]); - } - unsubscribe(); -} - -async function testCollectionLimitSet(helper: EthUniqueHelper, mode: TCollectionMode) { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); - const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); - const ethEvents: any = []; - collectionHelper.events.allEvents((_: any, event: any) => { - ethEvents.push(event); - }); - const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionLimitSet']}]); - { - await collection.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanTransfer, value: {status: true, value: 0}}).send({from: owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'CollectionLimitSet'}]); - } - unsubscribe(); -} - -async function testCollectionOwnerChanged(helper: EthUniqueHelper, mode: TCollectionMode) { - const owner = await helper.eth.createAccountWithBalance(donor); - const newOwner = helper.ethCrossAccount.createAccount(); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); - const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); - const ethEvents: any = []; - collectionHelper.events.allEvents((_: any, event: any) => { - ethEvents.push(event); - }); - const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionOwnerChanged']}]); - { - await collection.methods.changeCollectionOwnerCross(newOwner).send({from: owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'CollectionOwnerChanged'}]); - } - unsubscribe(); -} - -async function testCollectionPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); - const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); - const ethEvents: any = []; - collectionHelper.events.allEvents((_: any, event: any) => { - ethEvents.push(event); - }); - const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionPermissionSet']}]); - { - await collection.methods.setCollectionMintMode(true).send({from: owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]); - clearEvents(ethEvents, subEvents); - } - { - await collection.methods.setCollectionAccess(1).send({from: owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]); - } - unsubscribe(); -} - -async function testCollectionSponsorSetAndConfirmedAndThenRemoved(helper: EthUniqueHelper, mode: TCollectionMode) { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.ethCrossAccount.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); - const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner); - const ethEvents: any = []; - collectionHelper.events.allEvents((_: any, event: any) => { - ethEvents.push(event); - }); - const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{ - section: 'common', names: ['CollectionSponsorSet', 'SponsorshipConfirmed', 'CollectionSponsorRemoved', - ]}]); - { - await collection.methods.setCollectionSponsorCross(sponsor).send({from: owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([{ - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }]); - expect(subEvents).to.containSubset([{method: 'CollectionSponsorSet'}]); - clearEvents(ethEvents, subEvents); - } - { - await collection.methods.confirmCollectionSponsorship().send({from: sponsor.eth}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'SponsorshipConfirmed'}]); - clearEvents(ethEvents, subEvents); - } - { - await collection.methods.removeCollectionSponsor().send({from: owner}); - await helper.wait.newBlocks(1); - expect(ethEvents).to.containSubset([ - { - event: 'CollectionChanged', - returnValues: { - collectionId: collectionAddress, - }, - }, - ]); - expect(subEvents).to.containSubset([{method: 'CollectionSponsorRemoved'}]); - } - unsubscribe(); -} - -async function testTokenPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner); - const result = await collection.methods.mint(owner).send({from: owner}); - const tokenId = result.events.Transfer.returnValues.tokenId; - await collection.methods.setTokenPropertyPermissions([ - ['A', [ - [TokenPermissionField.Mutable, true], - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, true]], - ], - ]).send({from: owner}); - - const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['TokenPropertySet', 'TokenPropertyDeleted']}]); - { - const result = await collection.methods.setProperties(tokenId, [{key: 'A', value: [1,2,3]}]).send({from: owner}); - await helper.wait.newBlocks(1); - expect(result.events.TokenChanged).to.be.like({ - event: 'TokenChanged', - returnValues: { - tokenId: tokenId, - }, - }); - expect(subEvents).to.containSubset([{method: 'TokenPropertySet'}]); - clearEvents(null, subEvents); - } - { - const result = await collection.methods.deleteProperties(tokenId, ['A']).send({from: owner}); - await helper.wait.newBlocks(1); - expect(result.events.TokenChanged).to.be.like({ - event: 'TokenChanged', - returnValues: { - tokenId: tokenId, - }, - }); - expect(subEvents).to.containSubset([{method: 'TokenPropertyDeleted'}]); - } - unsubscribe(); -} - -describe('[FT] Sync sub & eth events', () => { - const mode: TCollectionMode = 'ft'; - - itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => { - await testCollectionCreatedAndDestroy(helper, mode); - }); - - itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => { - await testCollectionPropertySetAndDeleted(helper, mode); - }); - - itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => { - await testAllowListAddressAddedAndRemoved(helper, mode); - }); - - itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => { - await testCollectionAdminAddedAndRemoved(helper, mode); - }); - - itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => { - await testCollectionLimitSet(helper, mode); - }); - - itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => { - await testCollectionOwnerChanged(helper, mode); - }); - - itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => { - await testCollectionPermissionSet(helper, mode); - }); - - itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => { - await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode); - }); -}); - -describe('[NFT] Sync sub & eth events', () => { - const mode: TCollectionMode = 'nft'; - - itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => { - await testCollectionCreatedAndDestroy(helper, mode); - }); - - itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => { - await testCollectionPropertySetAndDeleted(helper, mode); - }); - - itEth('CollectionChanged event for PropertyPermissionSet', async ({helper}) => { - await testPropertyPermissionSet(helper, mode); - }); - - itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => { - await testAllowListAddressAddedAndRemoved(helper, mode); - }); - - itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => { - await testCollectionAdminAddedAndRemoved(helper, mode); - }); - - itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => { - await testCollectionLimitSet(helper, mode); - }); - - itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => { - await testCollectionOwnerChanged(helper, mode); - }); - - itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => { - await testCollectionPermissionSet(helper, mode); - }); - - itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => { - await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode); - }); - - itEth('CollectionChanged event for TokenPropertySet, TokenPropertyDeleted', async ({helper}) => { - await testTokenPropertySetAndDeleted(helper, mode); - }); -}); - -describe('[RFT] Sync sub & eth events', () => { - const mode: TCollectionMode = 'rft'; - - before(async function() { - await usingEthPlaygrounds((helper) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - return Promise.resolve(); - }); - }); - - itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => { - await testCollectionCreatedAndDestroy(helper, mode); - }); - - itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => { - await testCollectionPropertySetAndDeleted(helper, mode); - }); - - itEth('CollectionChanged event for PropertyPermissionSet', async ({helper}) => { - await testPropertyPermissionSet(helper, mode); - }); - - itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => { - await testAllowListAddressAddedAndRemoved(helper, mode); - }); - - itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => { - await testCollectionAdminAddedAndRemoved(helper, mode); - }); - - itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => { - await testCollectionLimitSet(helper, mode); - }); - - itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => { - await testCollectionOwnerChanged(helper, mode); - }); - - itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => { - await testCollectionPermissionSet(helper, mode); - }); - - itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => { - await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode); - }); - - itEth('CollectionChanged event for TokenPropertySet, TokenPropertyDeleted', async ({helper}) => { - await testTokenPropertySetAndDeleted(helper, mode); - }); -}); --- a/js-packages/tests/src/eth/evmCoder.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itEth, expect, usingEthPlaygrounds} from './util/index.js'; - -const getContractSource = (collectionAddress: string, contractAddress: string): string => ` - // SPDX-License-Identifier: MIT - pragma solidity ^0.8.0; - interface ITest { - function ztestzzzzzzz() external returns (uint256 n); - } - contract Test { - event Result(bool, uint256); - function test1() public { - try - ITest(${collectionAddress}).ztestzzzzzzz() - returns (uint256 n) { - // enters - emit Result(true, n); // => [true, BigNumber { value: "43648854190028290368124427828690944273759144372138548774646036134290060795932" }] - } catch { - emit Result(false, 0); - } - } - function test2() public { - try - ITest(${contractAddress}).ztestzzzzzzz() - returns (uint256 n) { - emit Result(true, n); - } catch { - // enters - emit Result(false, 0); // => [ false, BigNumber { value: "0" } ] - } - } - function test3() public { - ITest(${collectionAddress}).ztestzzzzzzz(); - } - } - `; - - -describe('Evm Coder tests', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Call non-existing function', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.eth.createNFTCollection(owner, 'EVMCODER', '', 'TEST'); - const contract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, '0x1bfed5D614b886b9Ab2eA4CBAc22A96B7EC29c9c')); - const testContract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, contract.options.address)); - { - const result = await testContract.methods.test1().send(); - expect(result.events.Result.returnValues).to.deep.equal({ - '0': false, - '1': '0', - }); - } - { - const result = await testContract.methods.test2().send(); - expect(result.events.Result.returnValues).to.deep.equal({ - '0': false, - '1': '0', - }); - } - { - await expect(testContract.methods.test3().call()) - .to.be.rejectedWith(/unrecognized selector: 0xd9f02b36$/g); - } - }); -}); --- a/js-packages/tests/src/eth/fractionalizer/Fractionalizer.sol +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: Apache License -pragma solidity >=0.8.0; -import {CollectionHelpers} from "../api/CollectionHelpers.sol"; -import {ContractHelpers} from "../api/ContractHelpers.sol"; -import {UniqueRefungibleToken} from "../api/UniqueRefungibleToken.sol"; -import {UniqueRefungible, CrossAddress} from "../api/UniqueRefungible.sol"; -import {UniqueNFT} from "../api/UniqueNFT.sol"; - -/// @dev Fractionalization contract. It stores mappings between NFT and RFT tokens, -/// stores allowlist of NFT tokens available for fractionalization, has methods -/// for fractionalization and defractionalization of NFT tokens. -contract Fractionalizer { - struct Token { - address _collection; - uint256 _tokenId; - } - address rftCollection; - mapping(address => bool) nftCollectionAllowList; - mapping(address => mapping(uint256 => uint256)) public nft2rftMapping; - mapping(address => Token) public rft2nftMapping; - //use constant to reduce gas cost - bytes32 constant refungibleCollectionType = keccak256(bytes("ReFungible")); - - receive() external payable onlyOwner {} - - /// @dev Method modifier to only allow contract owner to call it. - modifier onlyOwner() { - address contracthelpersAddress = 0x842899ECF380553E8a4de75bF534cdf6fBF64049; - ContractHelpers contractHelpers = ContractHelpers(contracthelpersAddress); - address contractOwner = contractHelpers.contractOwner(address(this)); - require(msg.sender == contractOwner, "Only owner can"); - _; - } - - /// @dev This emits when RFT collection setting is changed. - event RFTCollectionSet(address _collection); - - /// @dev This emits when NFT collection is allowed or disallowed. - event AllowListSet(address _collection, bool _status); - - /// @dev This emits when NFT token is fractionalized by contract. - event Fractionalized(address _collection, uint256 _tokenId, address _rftToken, uint128 _amount); - - /// @dev This emits when NFT token is defractionalized by contract. - event Defractionalized(address _rftToken, address _nftCollection, uint256 _nftTokenId); - - /// Set RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens - /// would be created in this collection. - /// @dev Throws if RFT collection is already configured for this contract. - /// Throws if collection of wrong type (NFT, Fungible) is provided instead - /// of RFT collection. - /// Throws if `msg.sender` is not owner or admin of provided RFT collection. - /// Can only be called by contract owner. - /// @param _collection address of RFT collection. - function setRFTCollection(address _collection) external onlyOwner { - require(rftCollection == address(0), "RFT collection is already set"); - UniqueRefungible refungibleContract = UniqueRefungible(_collection); - string memory collectionType = refungibleContract.uniqueCollectionType(); - - // compare hashed to reduce gas cost - require( - keccak256(bytes(collectionType)) == refungibleCollectionType, - "Wrong collection type. Collection is not refungible." - ); - require( - refungibleContract.isOwnerOrAdminCross(CrossAddress({eth: address(this), sub: uint256(0)})), - "Fractionalizer contract should be an admin of the collection" - ); - rftCollection = _collection; - emit RFTCollectionSet(rftCollection); - } - - /// Creates and sets RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens - /// would be created in this collection. - /// @dev Throws if RFT collection is already configured for this contract. - /// Can only be called by contract owner. - /// @param _name name for created RFT collection. - /// @param _description description for created RFT collection. - /// @param _tokenPrefix token prefix for created RFT collection. - function createAndSetRFTCollection( - string calldata _name, - string calldata _description, - string calldata _tokenPrefix - ) external payable onlyOwner { - require(rftCollection == address(0), "RFT collection is already set"); - address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F; - rftCollection = CollectionHelpers(collectionHelpers).createRFTCollection{value: msg.value}( - _name, - _description, - _tokenPrefix - ); - emit RFTCollectionSet(rftCollection); - } - - /// Allow or disallow NFT collection tokens from being fractionalized by this contract. - /// @dev Can only be called by contract owner. - /// @param collection NFT token address. - /// @param status `true` to allow and `false` to disallow NFT token. - function setNftCollectionIsAllowed(address collection, bool status) external onlyOwner { - nftCollectionAllowList[collection] = status; - emit AllowListSet(collection, status); - } - - /// Fractionilize NFT token. - /// @dev Takes NFT token from `msg.sender` and transfers RFT token to `msg.sender` - /// instead. Creates new RFT token if provided NFT token never was fractionalized - /// by this contract or existing RFT token if it was. - /// Throws if RFT collection isn't configured for this contract. - /// Throws if fractionalization of provided NFT token is not allowed - /// Throws if `msg.sender` is not owner of provided NFT token - /// @param _collection NFT collection address - /// @param _token id of NFT token to be fractionalized - /// @param _pieces number of pieces new RFT token would have - function nft2rft( - address _collection, - uint256 _token, - uint128 _pieces - ) external { - require(rftCollection != address(0), "RFT collection is not set"); - UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection); - require( - nftCollectionAllowList[_collection] == true, - "Fractionalization of this collection is not allowed by admin" - ); - require(UniqueNFT(_collection).ownerOf(_token) == msg.sender, "Only token owner could fractionalize it"); - UniqueNFT(_collection).transferFrom(msg.sender, address(this), _token); - uint256 rftTokenId; - address rftTokenAddress; - UniqueRefungibleToken rftTokenContract; - if (nft2rftMapping[_collection][_token] == 0) { - rftTokenId = rftCollectionContract.mint(address(this)); - rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId); - nft2rftMapping[_collection][_token] = rftTokenId; - rft2nftMapping[rftTokenAddress] = Token(_collection, _token); - - rftTokenContract = UniqueRefungibleToken(rftTokenAddress); - } else { - rftTokenId = nft2rftMapping[_collection][_token]; - rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId); - rftTokenContract = UniqueRefungibleToken(rftTokenAddress); - } - rftTokenContract.repartition(_pieces); - rftTokenContract.transfer(msg.sender, _pieces); - emit Fractionalized(_collection, _token, rftTokenAddress, _pieces); - } - - /// Defrationalize NFT token. - /// @dev Takes RFT token from `msg.sender` and transfers corresponding NFT token - /// to `msg.sender` instead. - /// Throws if RFT collection isn't configured for this contract. - /// Throws if provided RFT token is no from configured RFT collection. - /// Throws if RFT token was not created by this contract. - /// Throws if `msg.sender` isn't owner of all RFT token pieces. - /// @param _collection RFT collection address - /// @param _token id of RFT token - function rft2nft(address _collection, uint256 _token) external { - require(rftCollection != address(0), "RFT collection is not set"); - require(rftCollection == _collection, "Wrong RFT collection"); - UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection); - address rftTokenAddress = rftCollectionContract.tokenContractAddress(_token); - Token memory nftToken = rft2nftMapping[rftTokenAddress]; - require(nftToken._collection != address(0), "No corresponding NFT token found"); - UniqueRefungibleToken rftTokenContract = UniqueRefungibleToken(rftTokenAddress); - require( - rftTokenContract.balanceOf(msg.sender) == rftTokenContract.totalSupply(), - "Not all pieces are owned by the caller" - ); - rftCollectionContract.transferFrom(msg.sender, address(this), _token); - UniqueNFT(nftToken._collection).transferFrom(address(this), msg.sender, nftToken._tokenId); - emit Defractionalized(rftTokenAddress, nftToken._collection, nftToken._tokenId); - } -} --- a/js-packages/tests/src/eth/fractionalizer/fractionalizer.test.ts +++ /dev/null @@ -1,453 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - - -import {readFile} from 'fs/promises'; - -import type {IKeyringPair} from '@polkadot/types/types'; -import {evmToAddress} from '@polkadot/util-crypto'; - -import {Contract} from 'web3-eth-contract'; - -import {usingEthPlaygrounds, expect, itEth} from '../util/index.js'; -import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js'; -import type {CompiledContract} from '../util/playgrounds/types.js'; -import {requirePalletsOrSkip, Pallets, makeNames} from '../../util/index.js'; - -const {dirname} = makeNames(import.meta.url); - -let compiledFractionalizer: CompiledContract; - -const compileContract = async (helper: EthUniqueHelper): Promise => { - if(!compiledFractionalizer) { - compiledFractionalizer = await helper.ethContract.compile('Fractionalizer', (await readFile(`${dirname}/Fractionalizer.sol`)).toString(), [ - {solPath: 'api/CollectionHelpers.sol', fsPath: `${dirname}/../api/CollectionHelpers.sol`}, - {solPath: 'api/ContractHelpers.sol', fsPath: `${dirname}/../api/ContractHelpers.sol`}, - {solPath: 'api/UniqueRefungibleToken.sol', fsPath: `${dirname}/../api/UniqueRefungibleToken.sol`}, - {solPath: 'api/UniqueRefungible.sol', fsPath: `${dirname}/../api/UniqueRefungible.sol`}, - {solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}, - ]); - } - return compiledFractionalizer; -}; - - -const deployContract = async (helper: EthUniqueHelper, owner: string): Promise => { - const compiled = await compileContract(helper); - return await helper.ethContract.deployByAbi(owner, compiled.abi, compiled.object); -}; - - -const initContract = async (helper: EthUniqueHelper, owner: string): Promise<{contract: Contract, rftCollectionAddress: string}> => { - const fractionalizer = await deployContract(helper, owner); - const amount = 10n * helper.balance.getOneTokenNominal(); - const web3 = helper.getWeb3(); - await web3.eth.sendTransaction({from: owner, to: fractionalizer.options.address, value: `${amount}`, gas: helper.eth.DEFAULT_GAS}); - const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({value: Number(2n * helper.balance.getOneTokenNominal())}); - const rftCollectionAddress = result.events.RFTCollectionSet.returnValues._collection; - return {contract: fractionalizer, rftCollectionAddress}; -}; - -const mintRFTToken = async (helper: EthUniqueHelper, owner: string, fractionalizer: Contract, amount: bigint): Promise<{ - nftCollectionAddress: string, nftTokenId: number, rftTokenAddress: string -}> => { - const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); - const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); - const mintResult = await nftContract.methods.mint(owner).send({from: owner}); - const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; - - await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); - await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner}); - const result = await fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, amount).send({from: owner}); - const {_collection, _tokenId, _rftToken} = result.events.Fractionalized.returnValues; - return { - nftCollectionAddress: _collection, - nftTokenId: _tokenId, - rftTokenAddress: _rftToken, - }; -}; - - -describe('Fractionalizer contract usage', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Set RFT collection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const fractionalizer = await deployContract(helper, owner); - const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); - const rftContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner); - - const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address); - await rftContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner}); - const result = await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner}); - expect(result.events).to.be.like({ - RFTCollectionSet: { - returnValues: { - _collection: rftCollection.collectionAddress, - }, - }, - }); - }); - - itEth('Mint RFT collection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const fractionalizer = await deployContract(helper, owner); - await helper.balance.transferToSubstrate(donor, evmToAddress(fractionalizer.options.address), 10n * helper.balance.getOneTokenNominal()); - - const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({from: owner, value: Number(2n * helper.balance.getOneTokenNominal())}); - expect(result.events).to.be.like({ - RFTCollectionSet: {}, - }); - expect(result.events.RFTCollectionSet.returnValues._collection).to.be.ok; - }); - - itEth('Set Allowlist', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {contract: fractionalizer} = await initContract(helper, owner); - const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); - - const result1 = await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); - expect(result1.events).to.be.like({ - AllowListSet: { - returnValues: { - _collection: nftCollection.collectionAddress, - _status: true, - }, - }, - }); - const result2 = await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, false).send({from: owner}); - expect(result2.events).to.be.like({ - AllowListSet: { - returnValues: { - _collection: nftCollection.collectionAddress, - _status: false, - }, - }, - }); - }); - - itEth('NFT to RFT', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); - const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); - const mintResult = await nftContract.methods.mint(owner).send({from: owner}); - const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; - - const {contract: fractionalizer} = await initContract(helper, owner); - - await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); - await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner}); - const result = await fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).send({from: owner}); - expect(result.events).to.be.like({ - Fractionalized: { - returnValues: { - _collection: nftCollection.collectionAddress, - _tokenId: nftTokenId, - _amount: '100', - }, - }, - }); - const rftTokenAddress = result.events.Fractionalized.returnValues._rftToken; - - // FIXME: should work without the caller - const rftTokenContract = helper.ethNativeContract.rftToken(rftTokenAddress); - expect(await rftTokenContract.methods.balanceOf(owner).call()).to.equal('100'); - }); - - itEth('RFT to NFT', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner); - const {rftTokenAddress, nftCollectionAddress, nftTokenId} = await mintRFTToken(helper, owner, fractionalizer, 100n); - - const {collectionId, tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress); - const refungibleAddress = helper.ethAddress.fromCollectionId(collectionId); - expect(rftCollectionAddress).to.be.equal(refungibleAddress); - const refungibleTokenContract = await helper.ethNativeContract.rftToken(rftTokenAddress, owner); - await refungibleTokenContract.methods.approve(fractionalizer.options.address, 100).send({from: owner}); - const result = await fractionalizer.methods.rft2nft(refungibleAddress, tokenId).send({from: owner}); - expect(result.events).to.be.like({ - Defractionalized: { - returnValues: { - _rftToken: rftTokenAddress, - _nftCollection: nftCollectionAddress, - _nftTokenId: nftTokenId, - }, - }, - }); - }); - - itEth('Test fractionalizer NFT <-> RFT mapping ', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner); - const {rftTokenAddress, nftCollectionAddress, nftTokenId} = await mintRFTToken(helper, owner, fractionalizer, 100n); - - const {collectionId, tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress); - const refungibleAddress = helper.ethAddress.fromCollectionId(collectionId); - expect(rftCollectionAddress).to.be.equal(refungibleAddress); - const refungibleTokenContract = await helper.ethNativeContract.rftToken(rftTokenAddress, owner); - await refungibleTokenContract.methods.approve(fractionalizer.options.address, 100).send({from: owner}); - - const rft2nft = await fractionalizer.methods.rft2nftMapping(rftTokenAddress).call(); - expect(rft2nft).to.be.like({ - _collection: nftCollectionAddress, - _tokenId: nftTokenId, - }); - - const nft2rft = await fractionalizer.methods.nft2rftMapping(nftCollectionAddress, nftTokenId).call(); - expect(nft2rft).to.be.eq(tokenId.toString()); - }); -}); - - - -describe('Negative Integration Tests for fractionalizer', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('call setRFTCollection twice', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); - const refungibleContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner); - - const fractionalizer = await deployContract(helper, owner); - const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address); - await refungibleContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner}); - await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner}); - - await expect(fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).call()) - .to.be.rejectedWith(/RFT collection is already set$/g); - }); - - itEth('call setRFTCollection with NFT collection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); - const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); - - const fractionalizer = await deployContract(helper, owner); - const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address); - await nftContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner}); - - await expect(fractionalizer.methods.setRFTCollection(nftCollection.collectionAddress).call()) - .to.be.rejectedWith(/Wrong collection type. Collection is not refungible.$/g); - }); - - itEth('call setRFTCollection while not collection admin', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const fractionalizer = await deployContract(helper, owner); - const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); - - await expect(fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).call()) - .to.be.rejectedWith(/Fractionalizer contract should be an admin of the collection$/g); - }); - - itEth('call setRFTCollection after createAndSetRFTCollection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const fractionalizer = await deployContract(helper, owner); - await helper.balance.transferToSubstrate(donor, evmToAddress(fractionalizer.options.address), 10n * helper.balance.getOneTokenNominal()); - - const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({from: owner, value: Number(2n * helper.balance.getOneTokenNominal())}); - const collectionIdAddress = result.events.RFTCollectionSet.returnValues._collection; - - await expect(fractionalizer.methods.setRFTCollection(collectionIdAddress).call()) - .to.be.rejectedWith(/RFT collection is already set$/g); - }); - - itEth('call nft2rft without setting RFT collection for contract', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); - const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); - const mintResult = await nftContract.methods.mint(owner).send({from: owner}); - const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; - - const fractionalizer = await deployContract(helper, owner); - - await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call()) - .to.be.rejectedWith(/RFT collection is not set$/g); - }); - - itEth('call nft2rft while not owner of NFT token', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const nftOwner = await helper.eth.createAccountWithBalance(donor); - - const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); - const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); - const mintResult = await nftContract.methods.mint(owner).send({from: owner}); - const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; - await nftContract.methods.transfer(nftOwner, 1).send({from: owner}); - - - const {contract: fractionalizer} = await initContract(helper, owner); - await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); - - await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call({from: owner})) - .to.be.rejectedWith(/Only token owner could fractionalize it$/g); - }); - - itEth('call nft2rft while not in list of allowed accounts', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); - const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); - const mintResult = await nftContract.methods.mint(owner).send({from: owner}); - const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; - - const {contract: fractionalizer} = await initContract(helper, owner); - - await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner}); - await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call()) - .to.be.rejectedWith(/Fractionalization of this collection is not allowed by admin$/g); - }); - - itEth('call nft2rft while fractionalizer doesnt have approval for nft token', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); - const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); - const mintResult = await nftContract.methods.mint(owner).send({from: owner}); - const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; - - const {contract: fractionalizer} = await initContract(helper, owner); - - await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); - await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call()) - .to.be.rejectedWith(/ApprovedValueTooLow$/g); - }); - - itEth('call rft2nft without setting RFT collection for contract', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const fractionalizer = await deployContract(helper, owner); - const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); - const refungibleContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner); - const mintResult = await refungibleContract.methods.mint(owner).send({from: owner}); - const rftTokenId = mintResult.events.Transfer.returnValues.tokenId; - - await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call({from: owner})) - .to.be.rejectedWith(/RFT collection is not set$/g); - }); - - itEth('call rft2nft for RFT token that is not from configured RFT collection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {contract: fractionalizer} = await initContract(helper, owner); - const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); - const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner); - const mintResult = await refungibleContract.methods.mint(owner).send({from: owner}); - const rftTokenId = mintResult.events.Transfer.returnValues.tokenId; - - await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call()) - .to.be.rejectedWith(/Wrong RFT collection$/g); - }); - - itEth('call rft2nft for RFT token that was not minted by fractionalizer contract', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT'); - const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner); - - const fractionalizer = await deployContract(helper, owner); - - const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address); - await refungibleContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner}); - await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner}); - - const mintResult = await refungibleContract.methods.mint(owner).send({from: owner}); - const rftTokenId = mintResult.events.Transfer.returnValues.tokenId; - - await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call()) - .to.be.rejectedWith(/No corresponding NFT token found$/g); - }); - - itEth('call rft2nft without owning all RFT pieces', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - - const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner); - const {rftTokenAddress} = await mintRFTToken(helper, owner, fractionalizer, 100n); - - const {tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress); - const refungibleTokenContract = helper.ethNativeContract.rftToken(rftTokenAddress, owner); - await refungibleTokenContract.methods.transfer(receiver, 50).send({from: owner}); - await refungibleTokenContract.methods.approve(fractionalizer.options.address, 50).send({from: receiver}); - await expect(fractionalizer.methods.rft2nft(rftCollectionAddress, tokenId).call({from: receiver})) - .to.be.rejectedWith(/Not all pieces are owned by the caller$/g); - }); - - itEth('send QTZ/UNQ to contract from non owner', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const payer = await helper.eth.createAccountWithBalance(donor); - - const fractionalizer = await deployContract(helper, owner); - const amount = 10n * helper.balance.getOneTokenNominal(); - const web3 = helper.getWeb3(); - await expect(web3.eth.sendTransaction({from: payer, to: fractionalizer.options.address, value: `${amount}`, gas: helper.eth.DEFAULT_GAS})).to.be.rejected; - }); - - itEth('fractionalize NFT with NFT transfers disallowed', async ({helper}) => { - const nftCollection = await helper.nft.mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const owner = await helper.eth.createAccountWithBalance(donor); - const nftToken = await nftCollection.mintToken(donor, {Ethereum: owner}); - await helper.executeExtrinsic(donor, 'api.tx.unique.setTransfersEnabledFlag', [nftCollection.collectionId, false], true); - const nftCollectionAddress = helper.ethAddress.fromCollectionId(nftCollection.collectionId); - const {contract: fractionalizer} = await initContract(helper, owner); - await fractionalizer.methods.setNftCollectionIsAllowed(nftCollectionAddress, true).send({from: owner}); - - const nftContract = await helper.ethNativeContract.collection(nftCollectionAddress, 'nft', owner); - await nftContract.methods.approve(fractionalizer.options.address, nftToken.tokenId).send({from: owner}); - await expect(fractionalizer.methods.nft2rft(nftCollectionAddress, nftToken.tokenId, 100).call()) - .to.be.rejectedWith(/TransferNotAllowed$/g); - }); - - itEth('fractionalize NFT with RFT transfers disallowed', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const rftCollection = await helper.rft.mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'}); - const rftCollectionAddress = helper.ethAddress.fromCollectionId(rftCollection.collectionId); - const fractionalizer = await deployContract(helper, owner); - await rftCollection.addAdmin(donor, {Ethereum: fractionalizer.options.address}); - - await fractionalizer.methods.setRFTCollection(rftCollectionAddress).send({from: owner}); - await helper.executeExtrinsic(donor, 'api.tx.unique.setTransfersEnabledFlag', [rftCollection.collectionId, false], true); - - const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT'); - const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner); - const mintResult = await nftContract.methods.mint(owner).send({from: owner}); - const nftTokenId = mintResult.events.Transfer.returnValues.tokenId; - - await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner}); - await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner}); - - await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100n).call()) - .to.be.rejectedWith(/TransferNotAllowed$/g); - }); -}); --- a/js-packages/tests/src/eth/fungible.test.ts +++ /dev/null @@ -1,643 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import type {IKeyringPair} from '@polkadot/types/types'; - -describe('Fungible: Plain calls', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let owner: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice, owner] = await helper.arrange.createAccounts([30n, 20n], donor); - }); - }); - - [ - 'substrate' as const, - 'ethereum' as const, - ].map(testCase => { - itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => { - // 1. Create receiver depending on the test case: - const receiverEth = helper.eth.createAccount(); - const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); - const receiverSub = owner; - const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(owner); - - const ethOwner = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.ft.mintCollection(alice); - await collection.addAdmin(alice, {Ethereum: ethOwner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', ethOwner); - - // 2. Mint tokens: - const result = await collectionEvm.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, 100).send(); - - const event = result.events.Transfer; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(receiverSub.address)); - expect(event.returnValues.value).to.equal('100'); - - // 3. Get balance depending on the test case: - let balance; - if(testCase === 'ethereum') balance = await collection.getBalance({Ethereum: receiverEth}); - else if(testCase === 'substrate') balance = await collection.getBalance({Substrate: receiverSub.address}); - // 3.1 Check balance: - expect(balance).to.eq(100n); - }); - }); - - itEth('Can perform mintBulk()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const bulkSize = 3; - const receivers = [...new Array(bulkSize)].map(() => helper.eth.createAccount()); - const collection = await helper.ft.mintCollection(alice); - await collection.addAdmin(alice, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - const result = await contract.methods.mintBulk(Array.from({length: bulkSize}, (_, i) => ( - [receivers[i], (i + 1) * 10] - ))).send(); - const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.value - b.returnValues.value); - for(let i = 0; i < bulkSize; i++) { - const event = events[i]; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.equal(receivers[i]); - expect(event.returnValues.value).to.equal(String(10 * (i + 1))); - } - }); - - // Soft-deprecated - itEth('Can perform burn()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.ft.mintCollection(alice); - await collection.addAdmin(alice, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true); - await contract.methods.mint(receiver, 100).send(); - - const result = await contract.methods.burnFrom(receiver, 49).send({from: receiver}); - - const event = result.events.Transfer; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal(receiver); - expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.value).to.equal('49'); - - const balance = await contract.methods.balanceOf(receiver).call(); - expect(balance).to.equal('51'); - }); - - itEth('Can perform approve()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = helper.eth.createAccount(); - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - { - const result = await contract.methods.approve(spender, 100).send({from: owner}); - - const event = result.events.Approval; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.owner).to.be.equal(owner); - expect(event.returnValues.spender).to.be.equal(spender); - expect(event.returnValues.value).to.be.equal('100'); - } - - { - const allowance = await contract.methods.allowance(owner, spender).call(); - expect(+allowance).to.equal(100); - } - { - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const spenderCross = helper.ethCrossAccount.fromAddress(spender); - const allowance = await contract.methods.allowanceCross(ownerCross, spenderCross).call(); - expect(+allowance).to.equal(100); - } - }); - - itEth('Can perform approveCross()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = helper.eth.createAccount(); - const spenderSub = (await helper.arrange.createAccounts([1n], donor))[0]; - const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender); - const spenderCrossSub = helper.ethCrossAccount.fromKeyringPair(spenderSub); - - - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - { - const result = await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner}); - const event = result.events.Approval; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.owner).to.be.equal(owner); - expect(event.returnValues.spender).to.be.equal(spender); - expect(event.returnValues.value).to.be.equal('100'); - } - - { - const allowance = await contract.methods.allowance(owner, spender).call(); - expect(+allowance).to.equal(100); - } - - - { - const result = await contract.methods.approveCross(spenderCrossSub, 100).send({from: owner}); - const event = result.events.Approval; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.owner).to.be.equal(owner); - expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(spenderSub.address)); - expect(event.returnValues.value).to.be.equal('100'); - } - - { - const allowance = await collection.getApprovedTokens({Ethereum: owner}, {Substrate: spenderSub.address}); - expect(allowance).to.equal(100n); - } - - { - //TO-DO expect with future allowanceCross(owner, spenderCrossEth).call() - } - }); - - itEth('Non-owner and non admin cannot approveCross', async ({helper}) => { - const nonOwner = await helper.eth.createAccountWithBalance(donor); - const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner); - const owner = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.ft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}); - await collection.mint(alice, 100n, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - await expect(collectionEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned'); - }); - - - itEth('Can perform burnFromCross()', async ({helper}) => { - const sender = await helper.eth.createAccountWithBalance(donor); - - const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0); - - await collection.mint(owner, 200n, {Substrate: owner.address}); - await collection.approveTokens(owner, {Ethereum: sender}, 100n); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'ft'); - - const fromBalanceBefore = await collection.getBalance({Substrate: owner.address}); - - const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner); - const result = await contract.methods.burnFromCross(ownerCross, 49).send({from: sender}); - const events = result.events; - - expect(events).to.be.like({ - Transfer: { - address: helper.ethAddress.fromCollectionId(collection.collectionId), - event: 'Transfer', - returnValues: { - from: helper.address.substrateToEth(owner.address), - to: '0x0000000000000000000000000000000000000000', - value: '49', - }, - }, - Approval: { - address: helper.ethAddress.fromCollectionId(collection.collectionId), - returnValues: { - owner: helper.address.substrateToEth(owner.address), - spender: sender, - value: '51', - }, - event: 'Approval', - }, - }); - - const fromBalanceAfter = await collection.getBalance({Substrate: owner.address}); - expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(49n); - }); - - itEth('Can perform transferFrom()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - await contract.methods.approve(spender, 100).send(); - - { - const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender}); - - let event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(owner); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.value).to.be.equal('49'); - - event = result.events.Approval; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.owner).to.be.equal(owner); - expect(event.returnValues.spender).to.be.equal(spender); - expect(event.returnValues.value).to.be.equal('51'); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(49); - } - - { - const balance = await contract.methods.balanceOf(owner).call(); - expect(+balance).to.equal(151); - } - }); - - itEth('Can perform transferCross()', async ({helper}) => { - const sender = await helper.eth.createAccountWithBalance(donor); - const receiverEth = await helper.eth.createAccountWithBalance(donor); - const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); - const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(donor); - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n, {Ethereum: sender}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', sender); - - { - // Can transferCross to ethereum address: - const result = await collectionEvm.methods.transferCross(receiverCrossEth, 50).send({from: sender}); - // Check events: - const event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(sender); - expect(event.returnValues.to).to.be.equal(receiverEth); - expect(event.returnValues.value).to.be.equal('50'); - // Sender's balance decreased: - const ownerBalance = await collectionEvm.methods.balanceOf(sender).call(); - expect(+ownerBalance).to.equal(150); - // Receiver's balance increased: - const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); - expect(+receiverBalance).to.equal(50); - } - - { - // Can transferCross to substrate address: - const result = await collectionEvm.methods.transferCross(receiverCrossSub, 50).send({from: sender}); - // Check events: - const event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(sender); - expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(donor.address)); - expect(event.returnValues.value).to.be.equal('50'); - // Sender's balance decreased: - const senderBalance = await collection.getBalance({Ethereum: sender}); - expect(senderBalance).to.equal(100n); - // Receiver's balance increased: - const balance = await collection.getBalance({Substrate: donor.address}); - expect(balance).to.equal(50n); - } - }); - - ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} incorrect amount`, async ({helper}) => { - const sender = await helper.eth.createAccountWithBalance(donor); - const receiverEth = await helper.eth.createAccountWithBalance(donor); - const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); - const BALANCE = 200n; - const BALANCE_TO_TRANSFER = BALANCE + 100n; - - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, BALANCE, {Ethereum: sender}); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', sender); - - // 1. Cannot transfer more than have - const receiver = testCase === 'transfer' ? receiverEth : receiverCrossEth; - await expect(collectionEvm.methods[testCase](receiver, BALANCE_TO_TRANSFER).send({from: sender})).to.be.rejected; - // 2. Zero transfer allowed (EIP-20): - await collectionEvm.methods[testCase](receiver, 0n).send({from: sender}); - })); - - - itEth('Can perform transfer()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - { - const result = await contract.methods.transfer(receiver, 50).send({from: owner}); - - const event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(owner); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.value).to.be.equal('50'); - } - - { - const balance = await contract.methods.balanceOf(owner).call(); - expect(+balance).to.equal(150); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(50); - } - }); - - itEth('Can perform transferFromCross()', async ({helper}) => { - const sender = await helper.eth.createAccountWithBalance(donor); - - const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0); - - const receiver = helper.eth.createAccount(); - - await collection.mint(owner, 200n, {Substrate: owner.address}); - await collection.approveTokens(owner, {Ethereum: sender}, 100n); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'ft'); - - const from = helper.ethCrossAccount.fromKeyringPair(owner); - const to = helper.ethCrossAccount.fromAddress(receiver); - - const fromBalanceBefore = await collection.getBalance({Substrate: owner.address}); - const toBalanceBefore = await collection.getBalance({Ethereum: receiver}); - - const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender}); - - expect(result.events).to.be.like({ - Transfer: { - address, - event: 'Transfer', - returnValues: { - from: helper.address.substrateToEth(owner.address), - to: receiver, - value: '51', - }, - }, - Approval: { - address, - event: 'Approval', - returnValues: { - owner: helper.address.substrateToEth(owner.address), - spender: sender, - value: '49', - }, - }}); - - const fromBalanceAfter = await collection.getBalance({Substrate: owner.address}); - expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(51n); - const toBalanceAfter = await collection.getBalance({Ethereum: receiver}); - expect(toBalanceAfter - toBalanceBefore).to.be.eq(51n); - }); - - itEth('Check balanceOfCross()', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {}); - const owner = await helper.ethCrossAccount.createAccountWithBalance(donor); - const other = await helper.ethCrossAccount.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth); - - expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0'); - expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0'); - - await collection.mint(alice, 100n, {Ethereum: owner.eth}); - expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100'); - expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0'); - - await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth}); - expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50'); - expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50'); - - await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth}); - expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0'); - expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100'); - - await collectionEvm.methods.transferCross(owner, 100n).send({from: other.eth}); - expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100'); - expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0'); - }); -}); - -describe('Fungible: Fees', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([20n], donor); - }); - }); - - itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = helper.eth.createAccount(); - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner})); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - }); - - itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - await contract.methods.approve(spender, 100).send({from: owner}); - - const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender})); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - }); - - itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner})); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - }); -}); - -describe('Fungible: Substrate calls', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let owner: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice, owner] = await helper.arrange.createAccounts([20n, 20n], donor); - }); - }); - - itEth('Events emitted for approve()', async ({helper}) => { - const receiver = helper.eth.createAccount(); - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft'); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - await collection.approveTokens(alice, {Ethereum: receiver}, 100n); - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.event).to.be.equal('Approval'); - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.spender).to.be.equal(receiver); - expect(event.returnValues.value).to.be.equal('100'); - }); - - itEth('Events emitted for transferFrom()', async ({helper}) => { - const [bob] = await helper.arrange.createAccounts([10n], donor); - const receiver = helper.eth.createAccount(); - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n); - await collection.approveTokens(alice, {Substrate: bob.address}, 100n); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft'); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - await collection.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n); - if(events.length == 0) await helper.wait.newBlocks(1); - let event = events[0]; - - expect(event.event).to.be.equal('Transfer'); - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.value).to.be.equal('51'); - - event = events[1]; - expect(event.event).to.be.equal('Approval'); - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address)); - expect(event.returnValues.value).to.be.equal('49'); - }); - - itEth('Events emitted for transfer()', async ({helper}) => { - const receiver = helper.eth.createAccount(); - const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft'); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - await collection.transfer(alice, {Ethereum:receiver}, 51n); - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.event).to.be.equal('Transfer'); - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.value).to.be.equal('51'); - }); - - itEth('Events emitted for transferFromCross()', async ({helper}) => { - const sender = await helper.eth.createAccountWithBalance(donor); - - const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0); - - const receiver = helper.eth.createAccount(); - - await collection.mint(owner, 200n, {Substrate: owner.address}); - await collection.approveTokens(owner, {Ethereum: sender}, 100n); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'ft'); - - const from = helper.ethCrossAccount.fromKeyringPair(owner); - const to = helper.ethCrossAccount.fromAddress(receiver); - - const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender}); - - expect(result.events).to.be.like({ - Transfer: { - address, - event: 'Transfer', - returnValues: { - from: helper.address.substrateToEth(owner.address), - to: receiver, - value: '51', - }, - }, - Approval: { - address, - event: 'Approval', - returnValues: { - owner: helper.address.substrateToEth(owner.address), - spender: sender, - value: '49', - }, - }}); - }); -}); --- a/js-packages/tests/src/eth/getCode.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {COLLECTION_HELPER, CONTRACT_HELPER} from '../util/index.js'; - -describe('RPC eth_getCode', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - [ - {address: COLLECTION_HELPER}, - {address: CONTRACT_HELPER}, - ].map(testCase => { - itEth(`returns value for native contract: ${testCase.address}`, async ({helper}) => { - const contractCodeSub = (await helper.callRpc('api.rpc.eth.getCode', [testCase.address])).toJSON(); - const contractCodeEth = (await helper.getWeb3().eth.getCode(testCase.address)); - - expect(contractCodeSub).to.has.length.greaterThan(4); - expect(contractCodeEth).to.has.length.greaterThan(4); - }); - }); - - itEth('returns value for custom contract', async ({helper}) => { - const signer = await helper.eth.createAccountWithBalance(donor); - const flipper = await helper.eth.deployFlipper(signer); - - const contractCodeSub = (await helper.callRpc('api.rpc.eth.getCode', [flipper.options.address])).toJSON(); - const contractCodeEth = (await helper.getWeb3().eth.getCode(flipper.options.address)); - - expect(contractCodeSub).to.has.length.greaterThan(4); - expect(contractCodeEth).to.has.length.greaterThan(4); - }); -}); --- a/js-packages/tests/src/eth/helpersSmoke.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import type {IKeyringPair} from '@polkadot/types/types'; - -describe('Helpers sanity check', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Contract owner is recorded', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const flipper = await helper.eth.deployFlipper(owner); - - expect(await (await helper.ethNativeContract.contractHelpers(owner)).methods.contractOwner(flipper.options.address).call()).to.be.equal(owner); - }); - - itEth('Flipper is working', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const flipper = await helper.eth.deployFlipper(owner); - - expect(await flipper.methods.getValue().call()).to.be.false; - await flipper.methods.flip().send({from: owner}); - expect(await flipper.methods.getValue().call()).to.be.true; - }); -}); --- a/js-packages/tests/src/eth/marketplace-v2/Market.sol +++ /dev/null @@ -1,414 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.18; - -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; -import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; -import { UniqueNFT, CrossAddress } from "@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol"; -import { UniqueFungible, CrossAddress as CrossAddressF } from "@unique-nft/solidity-interfaces/contracts/UniqueFungible.sol"; -import { CollectionHelpers } from "@unique-nft/solidity-interfaces/contracts/CollectionHelpers.sol"; -import "./royalty/UniqueRoyaltyHelper.sol"; - -contract Market is Ownable, ReentrancyGuard { - using ERC165Checker for address; - - struct Order { - uint32 id; - uint32 collectionId; - uint32 tokenId; - uint32 amount; - uint256 price; - CrossAddress seller; - } - - uint32 public constant version = 0; - uint32 public constant buildVersion = 3; - bytes4 private constant InterfaceId_ERC721 = 0x80ac58cd; - bytes4 private constant InterfaceId_ERC165 = 0x5755c3f2; - CollectionHelpers private constant collectionHelpers = - CollectionHelpers(0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F); - - mapping(uint32 => mapping(uint32 => Order)) orders; - uint32 private idCount = 1; - uint32 public marketFee; - uint64 public ctime; - address public ownerAddress; - mapping(address => bool) public admins; - - event TokenIsUpForSale(uint32 version, Order item); - event TokenRevoke(uint32 version, Order item, uint32 amount); - event TokenIsApproved(uint32 version, Order item); - event TokenIsPurchased( - uint32 version, - Order item, - uint32 salesAmount, - CrossAddress buyer, - RoyaltyAmount[] royalties - ); - event Log(string message); - - error InvalidArgument(string info); - error InvalidMarketFee(); - error SellerIsNotOwner(); - error TokenIsAlreadyOnSale(); - error TokenIsNotApproved(); - error CollectionNotFound(); - error CollectionNotSupportedERC721(); - error OrderNotFound(); - error TooManyAmountRequested(); - error NotEnoughMoneyError(); - error InvalidRoyaltiesError(uint256 totalRoyalty); - error FailTransferToken(string reason); - - modifier onlyAdmin() { - require(msg.sender == this.owner() || admins[msg.sender], "Only admin can"); - _; - } - - modifier validCrossAddress(address eth, uint256 sub) { - if (eth == address(0) && sub == 0) { - revert InvalidArgument("Ethereum and Substrate addresses cannot be null at the same time"); - } - - if (eth != address(0) && sub != 0) { - revert InvalidArgument("Ethereum and Substrate addresses cannot be not null at the same time"); - } - - _; - } - - constructor(uint32 fee, uint64 timestamp) { - marketFee = fee; - ctime = timestamp; - - if (marketFee >= 100) { - revert InvalidMarketFee(); - } - } - - /** - * Fallback that allows this contract to receive native token. - * We need this for self-sponsoring - */ - fallback() external payable {} - - /** - * Receive also allows this contract to receive native token. - * We need this for self-sponsoring - */ - receive() external payable {} - - function getErc721(uint32 collectionId) private view returns (IERC721) { - address collectionAddress = collectionHelpers.collectionAddress( - collectionId - ); - - uint size; - assembly { - size := extcodesize(collectionAddress) - } - - if (size == 0) { - revert CollectionNotFound(); - } - - if (!collectionAddress.supportsInterface(InterfaceId_ERC721)) { - revert CollectionNotSupportedERC721(); - } - - return IERC721(collectionAddress); - } - - /** - * Add new admin. Only owner or an existing admin can add admins. - * - * @param admin: Address of a new admin to add - */ - function addAdmin(address admin) public onlyAdmin { - admins[admin] = true; - } - - /** - * Remove an admin. Only owner or an existing admin can remove admins. - * - * @param admin: Address of a new admin to add - */ - function removeAdmin(address admin) public onlyAdmin { - delete admins[admin]; - } - - /** - * Place an NFT or RFT token for sale. It must be pre-approved for transfers by this contract address. - * - * @param collectionId: ID of the token collection - * @param tokenId: ID of the token - * @param price: Price (with proper network currency decimals) - * @param amount: Number of token fractions to list (must always be 1 for NFT) - * @param seller: The seller cross-address (the beneficiary account to receive payment, may be different from transaction sender) - */ - function put( - uint32 collectionId, - uint32 tokenId, - uint256 price, - uint32 amount, - CrossAddress memory seller - ) public validCrossAddress(seller.eth, seller.sub) { - if (price == 0) { - revert InvalidArgument("price must not be zero"); - } - if (amount == 0) { - revert InvalidArgument("amount must not be zero"); - } - - if (orders[collectionId][tokenId].price > 0) { - revert TokenIsAlreadyOnSale(); - } - - IERC721 erc721 = getErc721(collectionId); - - if (erc721.ownerOf(tokenId) != msg.sender) { - revert SellerIsNotOwner(); - } - - if (erc721.getApproved(tokenId) != address(this)) { - revert TokenIsNotApproved(); - } - - Order memory order = Order( - 0, - collectionId, - tokenId, - amount, - price, - seller - ); - - order.id = idCount++; - orders[collectionId][tokenId] = order; - - emit TokenIsUpForSale(version, order); - } - - /** - * Get information about the listed token order - * - * @param collectionId: ID of the token collection - * @param tokenId: ID of the token - * @return The order information - */ - function getOrder( - uint32 collectionId, - uint32 tokenId - ) external view returns (Order memory) { - return orders[collectionId][tokenId]; - } - - /** - * Revoke the token from the sale. Only the original lister can use this method. - * - * @param collectionId: ID of the token collection - * @param tokenId: ID of the token - * @param amount: Number of token fractions to de-list (must always be 1 for NFT) - */ - function revoke( - uint32 collectionId, - uint32 tokenId, - uint32 amount - ) external { - if (amount == 0) { - revert InvalidArgument("amount must not be zero"); - } - - Order memory order = orders[collectionId][tokenId]; - - if (order.price == 0) { - revert OrderNotFound(); - } - - if (amount > order.amount) { - revert TooManyAmountRequested(); - } - - IERC721 erc721 = getErc721(collectionId); - - address ethAddress; - if (order.seller.eth != address(0)) { - ethAddress = order.seller.eth; - } else { - ethAddress = payable(address(uint160(order.seller.sub >> 96))); - } - if (erc721.ownerOf(tokenId) != ethAddress) { - revert SellerIsNotOwner(); - } - - order.amount -= amount; - if (order.amount == 0) { - delete orders[collectionId][tokenId]; - } else { - orders[collectionId][tokenId] = order; - } - - emit TokenRevoke(version, order, amount); - } - - /** - * Test if the token is still approved to be transferred by this contract and delete the order if not. - * - * @param collectionId: ID of the token collection - * @param tokenId: ID of the token - */ - function checkApproved(uint32 collectionId, uint32 tokenId) public onlyAdmin { - Order memory order = orders[collectionId][tokenId]; - if (order.price == 0) { - revert OrderNotFound(); - } - - IERC721 erc721 = getErc721(collectionId); - - if (erc721.getApproved(tokenId) != address(this) || erc721.ownerOf(tokenId) != getAddressFromCrossAccount(order.seller)) { - uint32 amount = order.amount; - order.amount = 0; - emit TokenRevoke(version, order, amount); - - delete orders[collectionId][tokenId]; - } else { - emit TokenIsApproved(version, order); - } - } - - function getAddressFromCrossAccount(CrossAddress memory account) private pure returns (address) { - if (account.eth != address(0)) { - return account.eth; - } else { - return address(uint160(account.sub >> 96)); - } - } - - /** - * Revoke the token from the sale. Only the contract admin can use this method. - * - * @param collectionId: ID of the token collection - * @param tokenId: ID of the token - */ - function revokeAdmin(uint32 collectionId, uint32 tokenId) public onlyAdmin { - Order memory order = orders[collectionId][tokenId]; - if (order.price == 0) { - revert OrderNotFound(); - } - - uint32 amount = order.amount; - order.amount = 0; - emit TokenRevoke(version, order, amount); - - delete orders[collectionId][tokenId]; - } - - /** - * Buy a token (partially for an RFT). - * - * @param collectionId: ID of the token collection - * @param tokenId: ID of the token - * @param amount: Number of token fractions to buy (must always be 1 for NFT) - * @param buyer: Cross-address of the buyer, eth part must be equal to the transaction signer address - */ - function buy( - uint32 collectionId, - uint32 tokenId, - uint32 amount, - CrossAddress memory buyer - ) public payable validCrossAddress(buyer.eth, buyer.sub) nonReentrant { - if (msg.value == 0) { - revert InvalidArgument("msg.value must not be zero"); - } - if (amount == 0) { - revert InvalidArgument("amount must not be zero"); - } - - Order memory order = orders[collectionId][tokenId]; - if (order.price == 0) { - revert OrderNotFound(); - } - - if (amount > order.amount) { - revert TooManyAmountRequested(); - } - - uint256 totalValue = order.price * amount; - uint256 feeValue = (totalValue * marketFee) / 100; - - if (msg.value < totalValue) { - revert NotEnoughMoneyError(); - } - - IERC721 erc721 = getErc721(order.collectionId); - if (erc721.getApproved(tokenId) != address(this)) { - revert TokenIsNotApproved(); - } - - order.amount -= amount; - if (order.amount == 0) { - delete orders[collectionId][tokenId]; - } else { - orders[collectionId][tokenId] = order; - } - - address collectionAddress = collectionHelpers.collectionAddress(collectionId); - UniqueNFT nft = UniqueNFT(collectionAddress); - - nft.transferFromCross( - order.seller, - buyer, - order.tokenId - ); - - (uint256 totalRoyalty, RoyaltyAmount[] memory royalties) = sendRoyalties(collectionAddress, tokenId, totalValue - feeValue); - - if (totalRoyalty >= totalValue - feeValue) { - revert InvalidRoyaltiesError(totalRoyalty); - } - - sendMoney(order.seller, totalValue - feeValue - totalRoyalty); - - if (msg.value > totalValue) { - sendMoney(buyer, msg.value - totalValue); - } - - emit TokenIsPurchased(version, order, amount, buyer, royalties); - } - - function sendMoney(CrossAddress memory to, uint256 money) private { - address collectionAddress = collectionHelpers.collectionAddress(0); - - UniqueFungible fungible = UniqueFungible(collectionAddress); - - CrossAddressF memory fromF = CrossAddressF(address(this), 0); - CrossAddressF memory toF = CrossAddressF(to.eth, to.sub); - - fungible.transferFromCross(fromF, toF, money); - } - - function sendRoyalties(address collection, uint tokenId, uint sellPrice) private returns (uint256, RoyaltyAmount[] memory) { - RoyaltyAmount[] memory royalties = UniqueRoyaltyHelper.calculate(collection, tokenId, sellPrice); - - uint256 totalRoyalty = 0; - - for (uint256 i=0; i 0) { - payable(transferTo).transfer(balance); - } - } -} --- a/js-packages/tests/src/eth/marketplace-v2/marketplace.test.ts +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import Web3 from 'web3'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {readFile} from 'fs/promises'; -import {SponsoringMode, itEth, usingEthPlaygrounds} from '../util/index.js'; -import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js'; -import {makeNames} from '../../util/index.js'; -import {expect} from 'chai'; - -const {dirname} = makeNames(import.meta.url); - -const MARKET_FEE = 1; - -describe('Market V2 Contract', () => { - let donor: IKeyringPair; - - before(async () => { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - - const marketOwner = await helper.eth.createAccountWithBalance(donor, 600n); - - await deployMarket(helper, marketOwner); - }); - }); - - async function deployMarket(helper: EthUniqueHelper, marketOwner: string) { - return await helper.ethContract.deployByCode( - marketOwner, - 'Market', - (await readFile(`${dirname}/Market.sol`)).toString(), - [ - { - solPath: '@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol', - fsPath: `${dirname}/../api/UniqueNFT.sol`, - }, - { - solPath: '@unique-nft/solidity-interfaces/contracts/UniqueFungible.sol', - fsPath: `${dirname}/../api/UniqueFungible.sol`, - }, - { - solPath: '@openzeppelin/contracts/utils/introspection/IERC165.sol', - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol`, - }, - { - solPath: '@openzeppelin/contracts/access/Ownable.sol', - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/access/Ownable.sol`, - }, - { - solPath: '@openzeppelin/contracts/utils/Context.sol', - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Context.sol`, - }, - { - solPath: '@openzeppelin/contracts/security/ReentrancyGuard.sol', - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol`, - }, - { - solPath: '@openzeppelin/contracts/utils/introspection/ERC165Checker.sol', - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/ERC165Checker.sol`, - }, - { - solPath: '@openzeppelin/contracts/token/ERC721/IERC721.sol', - fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol`, - }, - { - solPath: '@unique-nft/solidity-interfaces/contracts/CollectionHelpers.sol', - fsPath: `${dirname}/../api/CollectionHelpers.sol`, - }, - { - solPath: 'royalty/UniqueRoyaltyHelper.sol', - fsPath: `${dirname}/royalty/UniqueRoyaltyHelper.sol`, - }, - { - solPath: 'royalty/UniqueRoyalty.sol', - fsPath: `${dirname}/royalty/UniqueRoyalty.sol`, - }, - { - solPath: 'royalty/LibPart.sol', - fsPath: `${dirname}/royalty/LibPart.sol`, - }, - ], - 15000000, - [MARKET_FEE, 0], - ); - } - - function substrateAddressToHex(sub: Uint8Array| string, web3: Web3.default) { - if(typeof sub === 'string') - return web3.utils.padLeft(web3.utils.toHex(web3.utils.toBN(sub)), 64); - else if(sub instanceof Uint8Array) - return web3.utils.padLeft(web3.utils.bytesToHex(Array.from(sub)), 64); - throw Error('Infallible'); - } - - itEth('Put + Buy [eth]', async ({helper}) => { - const ONE_TOKEN = helper.balance.getOneTokenNominal(); - const PRICE = 2n * ONE_TOKEN; // 2 UNQ - const marketOwner = await helper.eth.createAccountWithBalance(donor, 60000n); - const market = await deployMarket(helper, marketOwner); - const contractHelpers = helper.ethNativeContract.contractHelpers(marketOwner); - - // Set external sponsoring - await contractHelpers.methods.setSponsor(market.options.address, marketOwner).send({from: marketOwner}); - await contractHelpers.methods.confirmSponsorship(market.options.address).send({from: marketOwner}); - - // Configure sponsoring - await contractHelpers.methods.setSponsoringMode(market.options.address, SponsoringMode.Generous).send({from: marketOwner}); - await contractHelpers.methods.setSponsoringRateLimit(market.options.address, 0).send({from: marketOwner}); - - const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(marketOwner, 'Sponsor', 'absolutely anything', 'ROC'); - const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', marketOwner, true); - - // Set collection sponsoring - await collection.methods.setCollectionSponsor(marketOwner).send({from: marketOwner}); - await collection.methods.confirmCollectionSponsorship().send({from: marketOwner}); - - const sellerCross = helper.ethCrossAccount.createAccount(); - const result = await collection.methods.mintCross(sellerCross, []).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - await collection.methods.approve(market.options.address, tokenId).send({from: sellerCross.eth}); - - // Seller has no funds at all, his transactions are sponsored - const sellerBalance = await helper.balance.getEthereum(sellerCross.eth); - expect(sellerBalance).to.be.eq(0n); - - const putResult = await market.methods.put(collectionId, tokenId, PRICE.toString(), 1, sellerCross).send({ - from: sellerCross.eth, gasLimit: 1_000_000, - }); - expect(putResult.events.TokenIsUpForSale).is.not.undefined; - - // Seller balance are still 0 - const sellerBalanceAfter = await helper.balance.getEthereum(sellerCross.eth); - expect(sellerBalanceAfter).to.be.eq(0n); - - let ownerCross = await collection.methods.ownerOfCross(tokenId).call(); - expect(ownerCross.eth).to.be.eq(sellerCross.eth); - expect(ownerCross.sub).to.be.eq(sellerCross.sub); - - const buyerCross = await helper.ethCrossAccount.createAccountWithBalance(donor, 10n); - - // Buyer has only 10 UNQ - const buyerBalance = await helper.balance.getEthereum(buyerCross.eth); - expect(buyerBalance).to.be.eq(10n * ONE_TOKEN); - - const buyResult = await market.methods.buy(collectionId, tokenId, 1, buyerCross).send({from: buyerCross.eth, value: PRICE.toString(), gasLimit: 1_000_000}); - expect(buyResult.events.TokenIsPurchased).is.not.undefined; - - // Buyer pays only value, transaction use sponsoring - const buyerBalanceAfter = await helper.balance.getEthereum(buyerCross.eth); - expect(buyerBalanceAfter).to.be.eq(10n * ONE_TOKEN - PRICE); - - ownerCross = await collection.methods.ownerOfCross(tokenId).call(); - expect(ownerCross.eth).to.be.eq(buyerCross.eth); - expect(ownerCross.sub).to.be.eq(buyerCross.sub); - }); - - itEth('Put + Buy [sub]', async ({helper}) => { - const ONE_TOKEN = helper.balance.getOneTokenNominal(); - const PRICE = 2n * ONE_TOKEN; // 2 UNQ - const web3 = helper.getWeb3(); - const marketOwner = await helper.eth.createAccountWithBalance(donor, 600n); - const market = await deployMarket(helper, marketOwner); - const contractHelpers = helper.ethNativeContract.contractHelpers(marketOwner); - - // Set self sponsoring from contract balance - await contractHelpers.methods.selfSponsoredEnable(market.options.address).send({from: marketOwner}); - await helper.eth.transferBalanceFromSubstrate(donor, market.options.address, 10n); - - // Configure sponsoring - await contractHelpers.methods.setSponsoringMode(market.options.address, SponsoringMode.Generous).send({from: marketOwner}); - await contractHelpers.methods.setSponsoringRateLimit(market.options.address, 0).send({from: marketOwner}); - - const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(marketOwner, 'Sponsor', 'absolutely anything', 'ROC'); - const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', marketOwner, true); - - // Set collection sponsoring - await collection.methods.setCollectionSponsor(marketOwner).send({from: marketOwner}); - await collection.methods.confirmCollectionSponsorship().send({from: marketOwner}); - - const seller = helper.util.fromSeed(`//Market-seller-${(new Date()).getTime()}`); - const sellerCross = helper.ethCrossAccount.fromKeyringPair(seller); - - // Seller has no funds at all, his transactions are sponsored - { - const sellerBalance = await helper.balance.getSubstrate(seller.address); - expect(sellerBalance).to.be.eq(0n); - } - - const result = await collection.methods.mintCross(sellerCross, []).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - await helper.nft.approveToken(seller, collectionId, tokenId, {Ethereum: market.options.address}); - - await helper.eth.sendEVM(seller, market.options.address, market.methods.put(collectionId, tokenId, PRICE, 1, sellerCross).encodeABI(), '0'); - // Seller balance is still zero - { - const sellerBalance = await helper.balance.getSubstrate(seller.address); - expect(sellerBalance).to.be.eq(0n); - } - let ownerCross = await collection.methods.ownerOfCross(tokenId).call(); - expect(ownerCross.eth).to.be.eq(sellerCross.eth); - expect(substrateAddressToHex(ownerCross.sub, web3)).to.be.eq(substrateAddressToHex(sellerCross.sub, web3)); - - const [buyer] = await helper.arrange.createAccounts([600n], donor); - // Buyer has only expected balance - { - const buyerBalance = await helper.balance.getSubstrate(buyer.address); - expect(buyerBalance).to.be.eq(600n * ONE_TOKEN); - } - const buyerCross = helper.ethCrossAccount.fromKeyringPair(buyer); - - const buyerBalanceBefore = await helper.balance.getSubstrate(buyer.address); - await helper.eth.sendEVM(buyer, market.options.address, market.methods.buy(collectionId, tokenId, 1, buyerCross).encodeABI(), PRICE.toString()); - const buyerBalanceAfter = await helper.balance.getSubstrate(buyer.address); - // Buyer balance not changed: transaction is sponsored - expect(buyerBalanceBefore).to.be.eq(buyerBalanceAfter + PRICE); - - const sellerBalanceAfterBuy = BigInt(await helper.balance.getSubstrate(seller.address)); - ownerCross = await collection.methods.ownerOfCross(tokenId).call(); - expect(ownerCross.eth).to.be.eq(buyerCross.eth); - expect(substrateAddressToHex(ownerCross.sub, web3)).to.be.eq(substrateAddressToHex(buyerCross.sub, web3)); - - // Seller got only PRICE - MARKET_FEE - expect(sellerBalanceAfterBuy).to.be.eq(PRICE * BigInt(100 - MARKET_FEE) / 100n); - }); -}); --- a/js-packages/tests/src/eth/marketplace-v2/royalty/LibPart.sol +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./UniqueRoyalty.sol"; - -library LibPart { - bytes32 public constant TYPE_HASH = keccak256("Part(address account,uint96 value)"); - - struct Part { - address payable account; - uint96 value; - } - - function hash(Part memory part) internal pure returns (bytes32) { - return keccak256(abi.encode(TYPE_HASH, part.account, part.value)); - } -} - -library LibPartAdapter { - function encode(LibPart.Part[] memory parts) internal pure returns (bytes memory) { - if (parts.length == 0) return ""; - - uint256[] memory encoded = new uint256[](parts.length * 2); - - for (uint i = 0; i < parts.length; i++) { - encoded[i * 2] = 0x0100000000000000000000000000000000000000000000040000000000000000 | uint256(parts[i].value); - encoded[i * 2 + 1] = uint256(uint160(address(parts[i].account))); - } - - return abi.encodePacked(encoded); - } - - function decode(bytes memory b) internal pure returns (LibPart.Part[] memory) { - if (b.length == 0) return new LibPart.Part[](0); - - require((b.length % (32 * 2)) == 0, "Invalid bytes length, expected (32 * 2) * UniqueRoyaltyParts count"); - uint partsCount = b.length / (32 * 2); - uint numbersCount = partsCount * 2; - - LibPart.Part[] memory parts = new LibPart.Part[](partsCount); - - // need this because numbers encoded via abi.encodePacked - bytes memory prefix = new bytes(64); - - assembly { - mstore(add(prefix, 32), 32) - mstore(add(prefix, 64), numbersCount) - } - - uint256[] memory encoded = abi.decode(bytes.concat(prefix, b), (uint256[])); - - for (uint i = 0; i < partsCount; i++) { - uint96 value = uint96(encoded[i * 2] & 0xFFFFFFFFFFFFFFFF); - address account = address(uint160(encoded[i * 2 + 1])); - - parts[i] = LibPart.Part({ - account: payable(account), - value: value - }); - } - - return parts; - } -} - -library LibPartAdapterComplex { - function decodeSafe(bytes memory data) internal pure returns (LibPart.Part[] memory) { - return fromUniqueRoyalties(UniqueRoyalty.decode(data)); - } - - function encodeSafe(LibPart.Part[] memory parts) internal pure returns (bytes memory) { - return UniqueRoyalty.encode(toUniqueRoyalties(parts)); - } - - function fromUniqueRoyalties(UniqueRoyaltyPart[] memory royalties) internal pure returns (LibPart.Part[] memory) { - LibPart.Part[] memory parts = new LibPart.Part[](royalties.length); - - for (uint i = 0; i < royalties.length; i++) { - uint96 value = royalties[i].decimals >= 4 - ? uint96(royalties[i].value * (10 ** (royalties[i].decimals - 4))) - : uint96(royalties[i].value / (10 ** (4 - royalties[i].decimals))); - - parts[i] = LibPart.Part({ - account: payable(CrossAddressLib.toAddress(royalties[i].crossAddress)), - value: value - }); - } - - return parts; - } - - function toUniqueRoyalties(LibPart.Part[] memory parts) internal pure returns (UniqueRoyaltyPart[] memory) { - UniqueRoyaltyPart[] memory royalties = new UniqueRoyaltyPart[](parts.length); - - for (uint i = 0; i < parts.length; i++) { - royalties[i] = UniqueRoyaltyPart({ - version: 1, - value: uint64(parts[i].value), - decimals: 4, - crossAddress: CrossAddress({ - sub: 0, - eth: parts[i].account - }), - isPrimarySaleOnly: false - }); - } - - return royalties; - } -} \ No newline at end of file --- a/js-packages/tests/src/eth/marketplace-v2/royalty/UniqueRoyalty.sol +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.17; - -import { CrossAddress } from "@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol"; - -struct UniqueRoyaltyPart { - uint8 version; - uint8 decimals; - uint64 value; - bool isPrimarySaleOnly; - CrossAddress crossAddress; -} - -library CrossAddressLib { - function toAddress(CrossAddress memory crossAddress) internal pure returns (address) { - return crossAddress.eth != address(0) ? crossAddress.eth : address(uint160(crossAddress.sub >> 96)); - } -} - -library UniqueRoyalty { - uint private constant DECIMALS_OFFSET = 4 * 16; - uint private constant ADDRESS_TYPE_OFFSET = 4 * (16 + 2); // 0 - eth, 1 - sub - uint private constant ROYALTY_TYPE_OFFSET = 4 * (16 + 2 + 1); // 0 - default, 1 - primary sale only - uint private constant VERSION_OFFSET = 4 * (16 + 2 + 1 + 1 + 42); - - uint private constant PART_LENGTH = 32 * 2; - - function decode(bytes memory b) internal pure returns (UniqueRoyaltyPart[] memory) { - if (b.length == 0) return new UniqueRoyaltyPart[](0); - - require((b.length % PART_LENGTH) == 0, "Invalid bytes length, expected (32 * 2) * UniqueRoyaltyParts count"); - uint partsCount = b.length / PART_LENGTH; - uint numbersCount = partsCount * 2; - - UniqueRoyaltyPart[] memory parts = new UniqueRoyaltyPart[](partsCount); - - // need this because numbers encoded via abi.encodePacked - bytes memory prefix = new bytes(64); - - assembly { - mstore(add(prefix, 32), 32) - mstore(add(prefix, 64), numbersCount) - } - - uint256[] memory encoded = abi.decode(bytes.concat(prefix, b), (uint256[])); - - for (uint i = 0; i < partsCount; i++) { - parts[i] = decodePart(encoded[i * 2], encoded[i * 2 + 1]); - } - - return parts; - } - - function encode(UniqueRoyaltyPart[] memory parts) internal pure returns (bytes memory) { - if (parts.length == 0) return ""; - - uint256[] memory encoded = new uint256[](parts.length * 2); - - for (uint i = 0; i < parts.length; i++) { - (uint256 encodedMeta, uint256 encodedAddress) = encodePart(parts[i]); - - encoded[i * 2] = encodedMeta; - encoded[i * 2 + 1] = encodedAddress; - } - - return abi.encodePacked(encoded); - } - - function decodePart(bytes memory b) internal pure returns (UniqueRoyaltyPart memory) { - require(b.length == PART_LENGTH, "Invalid bytes length, expected 32 * 2"); - - uint256[2] memory encoded = abi.decode(b, (uint256[2])); - - return decodePart(encoded[0], encoded[1]); - } - - function decodePart( - uint256 _meta, - uint256 _address - ) internal pure returns (UniqueRoyaltyPart memory) { - uint256 version = _meta >> VERSION_OFFSET; - bool isPrimarySaleOnly = (_meta & (1 << ROYALTY_TYPE_OFFSET)) > 0; - bool isEthereumAddress = (_meta & (1 << ADDRESS_TYPE_OFFSET)) == 0; - uint256 decimals = (_meta >> 4 * 16) & 0xFF; - uint256 value = _meta & 0xFFFFFFFFFFFFFFFF; - - CrossAddress memory crossAddress = isEthereumAddress - ? CrossAddress({ sub: 0, eth: address(uint160(_address)) }) - : CrossAddress({ sub: _address, eth: address(0) }); - - UniqueRoyaltyPart memory royaltyPart = UniqueRoyaltyPart({ - version: uint8(version), - decimals: uint8(decimals), - value: uint64(value), - isPrimarySaleOnly: isPrimarySaleOnly, - crossAddress: crossAddress - }); - - return royaltyPart; - } - - function encodePart(UniqueRoyaltyPart memory royaltyPart) internal pure returns (uint256, uint256) { - uint256 encodedMeta = 0; - uint256 encodedAddress = 0; - - encodedMeta |= uint256(royaltyPart.version) << VERSION_OFFSET; - if (royaltyPart.isPrimarySaleOnly) encodedMeta |= 1 << ROYALTY_TYPE_OFFSET; - - if (royaltyPart.crossAddress.eth == address(0x0)) { - encodedMeta |= 1 << ADDRESS_TYPE_OFFSET; - encodedAddress = royaltyPart.crossAddress.sub; - } else { - encodedAddress = uint256(uint160(royaltyPart.crossAddress.eth)); - } - - encodedMeta |= uint256(royaltyPart.decimals) << DECIMALS_OFFSET; - encodedMeta |= uint256(royaltyPart.value); - - return (encodedMeta, encodedAddress); - } -} \ No newline at end of file --- a/js-packages/tests/src/eth/marketplace-v2/royalty/UniqueRoyaltyHelper.sol +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.17; - -import "./UniqueRoyalty.sol"; -import "./LibPart.sol"; - -string constant ROYALTIES_PROPERTY = "royalties"; - -interface ICollection { - function collectionProperty(string memory key) external view returns (bytes memory); - function property(uint256 tokenId, string memory key) external view returns (bytes memory); -} - -struct RoyaltyAmount { - CrossAddress crossAddress; - uint amount; -} - -library UniqueRoyaltyHelper { - function encodePart(UniqueRoyaltyPart memory part) internal pure returns (bytes memory) { - (uint256 encodedMeta, uint256 encodedAddress) = UniqueRoyalty.encodePart(part); - - return abi.encodePacked(encodedMeta, encodedAddress); - } - - function decodePart(bytes memory data) internal pure returns (UniqueRoyaltyPart memory) { - return UniqueRoyalty.decodePart(data); - } - - // todo - implement smth better - check royalties sum is lte 100% - function validatePart(bytes memory b) internal pure returns (bool isValid) { - isValid = b.length == 64; - } - - function encode(UniqueRoyaltyPart[] memory royalties) internal pure returns (bytes memory) { - return UniqueRoyalty.encode(royalties); - } - - function decode(bytes memory data) internal pure returns (UniqueRoyaltyPart[] memory) { - return UniqueRoyalty.decode(data); - } - - // todo - implement smth better - check royalties sum is lte 100% - function validate(bytes memory b) internal pure returns (bool) { - return b.length % 64 == 0; - } - - function getTokenRoyalty(address collection, uint tokenId) internal view returns (UniqueRoyaltyPart[] memory) { - try ICollection(collection).property(tokenId, ROYALTIES_PROPERTY) returns (bytes memory encoded) { - return UniqueRoyalty.decode(encoded); - } catch { - return new UniqueRoyaltyPart[](0); - } - } - - function getCollectionRoyalty(address collection) internal view returns (UniqueRoyaltyPart[] memory) { - try ICollection(collection).collectionProperty(ROYALTIES_PROPERTY) returns (bytes memory encoded) { - return UniqueRoyalty.decode(encoded); - } catch { - return new UniqueRoyaltyPart[](0); - } - } - - function getRoyalty(address collection, uint tokenId) internal view returns (UniqueRoyaltyPart[] memory royalty) { - royalty = getTokenRoyalty(collection, tokenId); - - if (royalty.length == 0) { - royalty = getCollectionRoyalty(collection); - } - } - - function calculateRoyalties(UniqueRoyaltyPart[] memory royalties, bool isPrimarySale, uint sellPrice) internal pure returns (RoyaltyAmount[] memory) { - RoyaltyAmount[] memory royaltyAmounts = new RoyaltyAmount[](royalties.length); - uint amountsCount = 0; - - for (uint i = 0; i < royalties.length; i++) { - if (isPrimarySale == royalties[i].isPrimarySaleOnly) { - uint amount = (sellPrice * royalties[i].value) / (10 ** (royalties[i].decimals)); - - royaltyAmounts[amountsCount] = RoyaltyAmount({ - crossAddress: royalties[i].crossAddress, - amount: amount - }); - - amountsCount += 1; - } - } - - // shrink royaltyAmounts to amountsCount length - assembly { mstore(royaltyAmounts, amountsCount) } - - return royaltyAmounts; - } - - function calculateForPrimarySale(address collection, uint tokenId, uint sellPrice) internal view returns (RoyaltyAmount[] memory) { - UniqueRoyaltyPart[] memory royalties = getRoyalty(collection, tokenId); - - return calculateRoyalties(royalties, true, sellPrice); - } - - function calculate(address collection, uint tokenId, uint sellPrice) internal view returns (RoyaltyAmount[] memory) { - UniqueRoyaltyPart[] memory royalties = getRoyalty(collection, tokenId); - - return calculateRoyalties(royalties, false, sellPrice); - } -} \ No newline at end of file --- a/js-packages/tests/src/eth/marketplace-v2/utils.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.17; - -contract Utils { - function toString(address account) public pure returns (string memory) { - return toString(abi.encodePacked(account)); - } - - function toString(bool value) public pure returns (string memory) { - return value ? "true" : "false"; - } - - function toString(uint256 value) public pure returns (string memory) { - return toString(abi.encodePacked(value)); - } - - function toString(bytes32 value) public pure returns (string memory) { - return toString(abi.encodePacked(value)); - } - - function toString(bytes memory data) public pure returns (string memory) { - bytes memory alphabet = "0123456789abcdef"; - - bytes memory str = new bytes(2 + data.length * 2); - str[0] = "0"; - str[1] = "x"; - for (uint i = 0; i < data.length; i++) { - str[2 + i * 2] = alphabet[uint(uint8(data[i] >> 4))]; - str[3 + i * 2] = alphabet[uint(uint8(data[i] & 0x0f))]; - } - return string(str); - } -} \ No newline at end of file --- a/js-packages/tests/src/eth/marketplace/MarketPlace.sol +++ /dev/null @@ -1,414 +0,0 @@ -// SPDX-License-Identifier: Apache License -pragma solidity >=0.8.0; -import {UniqueNFT, Dummy, ERC165} from "../api/UniqueNFT.sol"; - -// Inline -interface ERC20Events { - event Transfer(address indexed from, address indexed to, uint256 value); - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); -} - -interface ERC20 is Dummy, ERC165, ERC20Events { - // Selector: name() 06fdde03 - function name() external view returns (string memory); - - // Selector: symbol() 95d89b41 - function symbol() external view returns (string memory); - - // Selector: totalSupply() 18160ddd - function totalSupply() external view returns (uint256); - - // Selector: decimals() 313ce567 - function decimals() external view returns (uint8); - - // Selector: balanceOf(address) 70a08231 - function balanceOf(address owner) external view returns (uint256); - - // Selector: transfer(address,uint256) a9059cbb - function transfer(address to, uint256 amount) external returns (bool); - - // Selector: transferFrom(address,address,uint256) 23b872dd - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); - - // Selector: approve(address,uint256) 095ea7b3 - function approve(address spender, uint256 amount) external returns (bool); - - // Selector: allowance(address,address) dd62ed3e - function allowance(address owner, address spender) - external - view - returns (uint256); -} - -interface UniqueFungible is Dummy, ERC165, ERC20 {} - -contract MarketPlace { - struct Order { - - uint256 idNFT; - address currencyCode; // UNIQ tokens as address address (1); wKSM - uint256 price; - uint256 time; - address idCollection; - address ownerAddr; - uint8 flagActive; - string name; - string symbol; - string tokenURI; - } - Order[] public orders; - uint test; - mapping (address => uint256) public balanceKSM; // [ownerAddr][currency] => [KSMs] - mapping (address => mapping (uint256 => uint256)) public asks ; // [buyer][idCollection][idNFT] => idorder - mapping (address => mapping (uint => uint[])) public ordersbyNFT; // [addressCollection] =>idNFT =>idorder - - mapping (address => uint[]) public asksbySeller; // [addressSeller] =>idorder - - mapping (address =>bool) internal isEscrow; - - //address escrow; - address owner; - address nativecoin; - - // from abstract contract ReentrancyGuard - // Booleans are more expensive than uint256 or any type that takes up a full - // word because each write operation emits an extra SLOAD to first read the - // slot's contents, replace the bits taken up by the boolean, and then write - // back. This is the compiler's defense against contract upgrades and - // pointer aliasing, and it cannot be disabled. - - // The values being non-zero value makes deployment a bit more expensive, - // but in exchange the refund on every call to nonReentrant will be lower in - // amount. Since refunds are capped to a percentage of the total - // transaction's gas, it is best to keep them low in cases like this one, to - // increase the likelihood of the full refund coming into effect. - uint8 private constant _NOT_ENTERED = 1; - uint8 private constant _ENTERED = 2; - - uint8 private _status; - - struct NFT { - address collection; - uint256 id; - } - - //function initialize() public initializer { - constructor () { // call setEscrow directly - owner = msg.sender; - - orders.push(Order( - 0, - address(0), - 0, - 0, - address(0), - address(0), - 0, "","","")); - _status = _NOT_ENTERED; - - } - - modifier nonReentrant() { // from abstract contract ReentrancyGuard - // On the first call to nonReentrant, _notEntered will be true - require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); - - // Any calls to nonReentrant after this point will fail - _status = _ENTERED; - - _; - - // By storing the original value once again, a refund is triggered (see - // https://eips.ethereum.org/EIPS/eip-2200) - _status = _NOT_ENTERED; - } - - modifier onlyEscrow () { - require(isEscrow [msg.sender] , "Only escrow can"); - _; - } - - modifier onlyOwner () { - require(msg.sender == owner, "Only owner can"); - _; - } - - /** - * Make bids (orders) to sell NFTs - */ - - - receive () external payable { - // revert ("Can't accept payment without collection and IDs, use dApp to send"); - } - fallback () external payable { - revert ("No such function"); - } - - event AddedAsk (uint256 _price, - address _currencyCode, - address _idCollection, - uint256 _idNFT, - uint256 orderId - ); - event EditedAsk (uint256 _price, - address _currencyCode, - address _idCollection, - uint256 _idNFT, - uint8 _active, - uint orderId); - - event CanceledAsk (address _idCollection, - uint256 _idNFT, - uint orderId - ); - - event DepositedKSM (uint256 _amount, address _sender); - - event BoughtNFT4KSM (address _idCollection, uint256 _idNFT, uint orderID, uint orderPrice ); - - event BoughtNFT (address _idCollection, uint256 _idNFT, uint orderID, uint orderPrice ); - - event WithdrawnAllKSM (address _sender, uint256 balance); - - event WithdrawnKSM (address _sender, uint256 balance); - - event Withdrawn (uint256 _amount, address _currencyCode, address _sender); - - - function setOwner (address _newOwner) public onlyOwner { - owner = _newOwner; - } - - function setEscrow (address _escrow, bool _state) public onlyOwner returns (bool) { - if (isEscrow[_escrow] != _state) { - isEscrow[_escrow] = _state; - return true; - } - return false; - } - - function setNativeCoin (address _coin) public onlyOwner { - nativecoin = _coin; - } - - - function addAsk (uint256 _price, - address _currencyCode, - address _idCollection, - uint256 _idNFT - ) public { // - address ownerNFT = UniqueNFT(_idCollection).ownerOf(_idNFT); - require (ownerNFT == msg.sender, "Only token owner can make ask"); - string memory nameNFT; - string memory symbolNFT; - string memory uriNFT; - try UniqueNFT(_idCollection).name() returns (string memory name_) { - nameNFT = name_; - } - catch { - nameNFT=""; - } - try UniqueNFT(_idCollection).symbol() returns (string memory symbol_) { - symbolNFT = symbol_; - } - catch { - symbolNFT=""; - } - try UniqueNFT(_idCollection).tokenURI(_idNFT) returns (string memory uri_) { - uriNFT = uri_; - } - catch { - uriNFT=""; - } - orders.push(Order( - _idNFT, - _currencyCode, - _price, - block.timestamp, - _idCollection, - msg.sender, - 1, // 1 = is active - nameNFT, - symbolNFT, - uriNFT - )); - - uint orderId = orders.length-1; - asks[_idCollection][_idNFT] = orderId; - asksbySeller[msg.sender].push(orderId); - UniqueNFT(_idCollection).transferFrom(msg.sender, address(this), _idNFT); - emit AddedAsk(_price, _currencyCode, _idCollection, _idNFT, orderId); - } - - function editAsk (uint256 _price, - address _currencyCode, - address _idCollection, - uint256 _idNFT, - uint8 _active) public { - - - uint orderID = asks[_idCollection][_idNFT]; - - require (orders[orderID].ownerAddr == msg.sender, "Only token owner can edit ask"); - require (orders[orderID].flagActive != 0, "This ask is closed"); - if (_price> 0 ) { - orders[orderID].price = _price ; - } - - if (_currencyCode != address(0) ) { - orders[orderID].currencyCode = _currencyCode ; - } - - orders[orderID].time = block.timestamp; - orders[orderID].flagActive = _active; - - emit EditedAsk(_price, _currencyCode, _idCollection, _idNFT, _active, orderID); - } - - - function cancelAsk (address _idCollection, - uint256 _idNFT - ) public { - - uint orderID = asks[_idCollection][_idNFT]; - - require (orders[orderID].ownerAddr == msg.sender, "Only token owner can edit ask"); - require (orders[orderID].flagActive != 0, "This ask is closed"); - - orders[orderID].time = block.timestamp; - orders[orderID].flagActive = 0; - UniqueNFT(_idCollection).transferFrom(address(this),orders[orderID].ownerAddr, _idNFT); - emit CanceledAsk(_idCollection, _idNFT, orderID); - } - - - function depositKSM (uint256 _amount, address _sender) public onlyEscrow { - balanceKSM[_sender] = balanceKSM[_sender] + _amount; - emit DepositedKSM(_amount, _sender); - } - - function buyKSM (address _idCollection, uint256 _idNFT, address _buyer, address _receiver ) public { - - Order memory order = orders[ asks[_idCollection][_idNFT]]; - require(isEscrow[msg.sender] || msg.sender == _buyer, "Only escrow or buyer can call buyKSM" ); - //1. reduce balance - - balanceKSM[_buyer] = balanceKSM[_buyer] - order.price; - balanceKSM[order.ownerAddr] = balanceKSM[order.ownerAddr] + order.price; - // 2. close order - orders[ asks[_idCollection][_idNFT]].flagActive = 0; - // 3. transfer NFT to buyer - UniqueNFT(_idCollection).transferFrom(address(this), _receiver, _idNFT); - emit BoughtNFT4KSM(_idCollection, _idNFT, asks[_idCollection][_idNFT], order.price); - - } - function buy (address _idCollection, uint256 _idNFT ) public payable returns (bool result) { //buing for UNQ like as ethers - - Order memory order = orders[asks[_idCollection][_idNFT]]; - //1. check sent amount and send to seller - require (msg.value == order.price, "Not right amount sent, have to be equal price" ); - // 2. close order - orders[ asks[_idCollection][_idNFT]].flagActive = 0; - - // 3. transfer NFT to buyer - UniqueNFT(_idCollection).transferFrom(address(this), msg.sender, _idNFT); - //uint balance = address(this).balance; - result = payable(order.ownerAddr).send (order.price); - emit BoughtNFT(_idCollection, _idNFT, asks[_idCollection][_idNFT], order.price); - - } - -/* - function buyOther (address _idCollection, uint256 _idNFT, address _currencyCode, uint _amount ) public { //buy for sny token if seller wants - - Order memory order = orders[ asks[_idCollection][_idNFT]]; - //1. check sent amount and transfer from buyer to seller - require (order.price == _amount && order.currencyCode == _currencyCode, "Not right amount or currency sent, have to be equal currency and price" ); - // !!! transfer have to be approved to marketplace! - UniqueFungible(order.currencyCode).transferFrom(msg.sender, address(this), order.price); //to not disclojure buyer's address - UniqueFungible(order.currencyCode).transfer(order.ownerAddr, order.price); - // 2. close order - orders[ asks[_idCollection][_idNFT]].flagActive = 0; - // 3. transfer NFT to buyer - UniqueNFT(_idCollection).transferFrom(address(this), msg.sender, _idNFT); - - - } - */ - - function withdrawAllKSM (address _sender) public nonReentrant returns (uint lastBalance ){ - require(isEscrow[msg.sender] || msg.sender == _sender, "Only escrow or balance owner can withdraw all KSM" ); - - lastBalance = balanceKSM[_sender]; - balanceKSM[_sender] =0; - emit WithdrawnAllKSM(_sender, lastBalance); - } - - function withdrawKSM (uint _amount, address _sender) onlyEscrow public { - balanceKSM[_sender] = balanceKSM[_sender] - _amount; - emit WithdrawnKSM(_sender, balanceKSM[_sender]); - - } - - function withdraw (uint256 _amount, address _currencyCode) public nonReentrant returns (bool result ){ //onlyOwner - address payable _sender = payable( msg.sender); - if (_currencyCode != nativecoin ) { //erc20 compat. tokens on UNIQUE chain - // uint balance = UniqueFungible(_currencyCode).balanceOf(address(this)); - UniqueFungible(_currencyCode).transfer(_sender, _amount); - } else { - // uint balance = address(this).balance; - - result = (_sender).send(_amount); // for UNQ like as ethers - } - emit Withdrawn(_amount, _currencyCode, _sender); - return result; - - } - - - // event GettedOrder(uint , Order); - function getOrder (address _idCollection, uint256 _idNFT) public view returns (Order memory) { - uint orderId = asks[_idCollection][_idNFT]; - Order memory order = orders[orderId]; - // emit GettedOrder (orderId, order); - return order; - } - - function getOrdersLen () public view returns (uint) { - return orders.length; - } - - function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) public pure returns(bytes4) { - return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); - } - - //TODO make destructing function to return all - function terminate(address[] calldata tokens, NFT[] calldata nfts) public onlyOwner { - // Transfer tokens to owner (TODO: error handling) - for (uint i = 0; i < tokens.length; i++) { - address addr = tokens[i]; - UniqueFungible token = UniqueFungible(addr); - uint256 balance = token.balanceOf(address(this)); - token.transfer(owner, balance); - } - for (uint i = 0; i < nfts.length; i++) { - address addr = nfts[i].collection; - UniqueNFT token = UniqueNFT(addr); - token.transferFrom(address(this), owner, nfts[i].id); - } - // Transfer Eth to owner and terminate contract - address payable owner1 = payable (owner); - uint balance = address(this).balance; - owner1.transfer(balance); - - } - -} --- a/js-packages/tests/src/eth/marketplace/marketplace.test.ts +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {readFile} from 'fs/promises'; -import {itEth, usingEthPlaygrounds, expect, SponsoringMode} from '../util/index.js'; -import {makeNames} from '../../util/index.js'; - -const {dirname} = makeNames(import.meta.url); - -describe('Matcher contract usage', () => { - const PRICE = 2000n; - let donor: IKeyringPair; - let alice: IKeyringPair; - let aliceMirror: string; - let aliceDoubleMirror: string; - let seller: IKeyringPair; - let sellerMirror: string; - - before(async () => { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - beforeEach(async () => { - await usingEthPlaygrounds(async (helper, privateKey) => { - [alice] = await helper.arrange.createAccounts([1000n], donor); - aliceMirror = helper.address.substrateToEth(alice.address).toLowerCase(); - aliceDoubleMirror = helper.address.ethToSubstrate(aliceMirror); - seller = await privateKey(`//Seller/${Date.now()}`); - sellerMirror = helper.address.substrateToEth(seller.address).toLowerCase(); - - await helper.balance.transferToSubstrate(donor, aliceDoubleMirror, 10_000_000_000_000_000_000n); - }); - }); - - itEth('With UNQ', async ({helper}) => { - const matcherOwner = await helper.eth.createAccountWithBalance(donor); - const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2); - - const sponsor = await helper.eth.createAccountWithBalance(donor); - const helpers = await helper.ethNativeContract.contractHelpers(matcherOwner); - await helpers.methods.setSponsoringMode(matcher.options.address, SponsoringMode.Allowlisted).send({from: matcherOwner}); - await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner}); - - await helpers.methods.setSponsor(matcher.options.address, sponsor).send({from: matcherOwner}); - await helpers.methods.confirmSponsorship(matcher.options.address).send({from: sponsor}); - - const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}, pendingSponsor: {Substrate: alice.address}}); - await collection.confirmSponsorship(alice); - await collection.addToAllowList(alice, {Substrate: aliceDoubleMirror}); - const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); - await helper.eth.transferBalanceFromSubstrate(donor, aliceMirror); - - await helpers.methods.toggleAllowed(matcher.options.address, aliceMirror, true).send({from: matcherOwner}); - await helpers.methods.toggleAllowed(matcher.options.address, sellerMirror, true).send({from: matcherOwner}); - - const token = await collection.mintToken(alice, {Ethereum: sellerMirror}); - - // Token is owned by seller initially - expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror}); - - // Ask - { - await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0'); - await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0'); - } - - // Token is transferred to matcher - expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()}); - - // Buy - { - const sellerBalanceBeforePurchase = await helper.balance.getSubstrate(seller.address); - await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buy(evmCollection.options.address, token.tokenId).encodeABI(), PRICE.toString()); - expect(await helper.balance.getSubstrate(seller.address) - sellerBalanceBeforePurchase === PRICE); - } - - // Token is transferred to evm account of alice - expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror}); - - // Transfer token to substrate side of alice - await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address}); - - // Token is transferred to substrate account of alice, seller received funds - expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address}); - }); - - itEth('With escrow', async ({helper}) => { - const matcherOwner = await helper.eth.createAccountWithBalance(donor); - const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2); - - const sponsor = await helper.eth.createAccountWithBalance(donor); - const escrow = await helper.eth.createAccountWithBalance(donor); - await matcher.methods.setEscrow(escrow, true).send({from: matcherOwner}); - const helpers = await helper.ethNativeContract.contractHelpers(matcherOwner); - await helpers.methods.setSponsoringMode(matcher.options.address, SponsoringMode.Allowlisted).send({from: matcherOwner}); - await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner}); - - await helpers.methods.setSponsor(matcher.options.address, sponsor).send({from: matcherOwner}); - await helpers.methods.confirmSponsorship(matcher.options.address).send({from: sponsor}); - - const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}, pendingSponsor: {Substrate: alice.address}}); - await collection.confirmSponsorship(alice); - await collection.addToAllowList(alice, {Substrate: aliceDoubleMirror}); - const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); - await helper.eth.transferBalanceFromSubstrate(donor, aliceMirror); - - - await helpers.methods.toggleAllowed(matcher.options.address, aliceMirror, true).send({from: matcherOwner}); - - await helpers.methods.toggleAllowed(matcher.options.address, sellerMirror, true).send({from: matcherOwner}); - - const token = await collection.mintToken(alice, {Ethereum: sellerMirror}); - - // Token is owned by seller initially - expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror}); - - // Ask - { - await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0'); - await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0'); - } - - // Token is transferred to matcher - expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()}); - - // Give buyer KSM - await matcher.methods.depositKSM(PRICE, aliceMirror).send({from: escrow}); - - // Buy - { - expect(await matcher.methods.balanceKSM(sellerMirror).call()).to.be.equal('0'); - expect(await matcher.methods.balanceKSM(aliceMirror).call()).to.be.equal(PRICE.toString()); - - await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buyKSM(evmCollection.options.address, token.tokenId, aliceMirror, aliceMirror).encodeABI(), '0'); - - // Price is removed from buyer balance, and added to seller - expect(await matcher.methods.balanceKSM(aliceMirror).call()).to.be.equal('0'); - expect(await matcher.methods.balanceKSM(sellerMirror).call()).to.be.equal(PRICE.toString()); - } - - // Token is transferred to evm account of alice - expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror}); - - // Transfer token to substrate side of alice - await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address}); - - // Token is transferred to substrate account of alice, seller received funds - expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address}); - }); - - itEth('Sell tokens from substrate user via EVM contract', async ({helper}) => { - const matcherOwner = await helper.eth.createAccountWithBalance(donor); - const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2); - - await helper.eth.transferBalanceFromSubstrate(donor, matcher.options.address); - - const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}}); - const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); - - await helper.balance.transferToSubstrate(donor, seller.address, 100_000_000_000_000_000_000n); - - const token = await collection.mintToken(alice, {Ethereum: sellerMirror}); - - // Token is owned by seller initially - expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror}); - - // Ask - { - await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0'); - await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0'); - } - - // Token is transferred to matcher - expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()}); - - // Buy - { - const sellerBalanceBeforePurchase = await helper.balance.getSubstrate(seller.address); - await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buy(evmCollection.options.address, token.tokenId).encodeABI(), PRICE.toString()); - expect(await helper.balance.getSubstrate(seller.address) - sellerBalanceBeforePurchase === PRICE); - } - - // Token is transferred to evm account of alice - expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror}); - - // Transfer token to substrate side of alice - await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address}); - - // Token is transferred to substrate account of alice, seller received funds - expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address}); - }); -}); --- a/js-packages/tests/src/eth/migration.seqtest.ts +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {Struct} from '@polkadot/types'; - -import type {IEvent} from '@unique/playgrounds/src/types.js'; -import type {InterfaceTypes} from '@polkadot/types/types/registry'; -import {ApiPromise} from '@polkadot/api'; - -const encodeEvent = (api: ApiPromise, pallet: string, palletEvents: string, event: string, fields: any) => { - const palletIndex = api.runtimeMetadata.asV14.pallets.find(p => p.name.toString() == pallet)!.index.toNumber(); - const eventMeta = api.events[palletEvents][event].meta; - const eventIndex = eventMeta.index.toNumber(); - const data = [ - palletIndex, eventIndex, - ]; - const metaEvent = api.registry.findMetaEvent(new Uint8Array(data)); - data.push(...new Struct(api.registry, {data: metaEvent}, {data: fields}).toU8a()); - - const typeName = api.registry.lookup.names.find(n => n.endsWith('RuntimeEvent'))!; - const obj = api.registry.createType(typeName, new Uint8Array(data)) as InterfaceTypes['RuntimeEvent']; - return obj.toHex(); -}; - -describe('EVM Migrations', () => { - let superuser: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - superuser = await privateKey('//Alice'); - charlie = await privateKey('//Charlie'); - }); - }); - - // todo:playgrounds requires sudo, look into later - itEth('Deploy contract saved state', async ({helper}) => { - /* - contract StatefulContract { - uint counter; - mapping (uint => uint) kv; - - function inc() public { - counter = counter + 1; - } - function counterValue() public view returns (uint) { - return counter; - } - - function set(uint key, uint value) public { - kv[key] = value; - } - - function get(uint key) public view returns (uint) { - return kv[key]; - } - } - */ - const ADDRESS = '0x4956bf52ef9ed8789f21bc600e915e0d961079f6'; - const CODE = '0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80631ab06ee514610051578063371303c01461006d5780637bfdec3b146100775780639507d39a14610095575b600080fd5b61006b60048036038101906100669190610160565b6100c5565b005b6100756100e1565b005b61007f6100f8565b60405161008c91906101af565b60405180910390f35b6100af60048036038101906100aa9190610133565b610101565b6040516100bc91906101af565b60405180910390f35b8060016000848152602001908152602001600020819055505050565b60016000546100f091906101ca565b600081905550565b60008054905090565b600060016000838152602001908152602001600020549050919050565b60008135905061012d8161025e565b92915050565b60006020828403121561014957610148610259565b5b60006101578482850161011e565b91505092915050565b6000806040838503121561017757610176610259565b5b60006101858582860161011e565b92505060206101968582860161011e565b9150509250929050565b6101a981610220565b82525050565b60006020820190506101c460008301846101a0565b92915050565b60006101d582610220565b91506101e083610220565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156102155761021461022a565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b61026781610220565b811461027257600080fd5b5056fea26469706673582212206a02d2fb5c244105ab884961479c1aee3b4c1011e4b5530ab483eb22344a865664736f6c63430008060033'; - const DATA = [ - // counter = 10 - ['0x0000000000000000000000000000000000000000000000000000000000000000', '0x000000000000000000000000000000000000000000000000000000000000000a'], - // kv = {1: 1, 2: 2, 3: 3, 4: 4}, - ['0xcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f', '0x0000000000000000000000000000000000000000000000000000000000000001'], - ['0xd9d16d34ffb15ba3a3d852f0d403e2ce1d691fb54de27ac87cd2f993f3ec330f', '0x0000000000000000000000000000000000000000000000000000000000000002'], - ['0x7dfe757ecd65cbd7922a9c0161e935dd7fdbcc0e999689c7d31633896b1fc60b', '0x0000000000000000000000000000000000000000000000000000000000000003'], - ['0xedc95719e9a3b28dd8e80877cb5880a9be7de1a13fc8b05e7999683b6b567643', '0x0000000000000000000000000000000000000000000000000000000000000004'], - ]; - - const caller = await helper.eth.createAccountWithBalance(superuser); - - const txBegin = helper.constructApiCall('api.tx.evmMigration.begin', [ADDRESS]); - const txSetData = helper.constructApiCall('api.tx.evmMigration.setData', [ADDRESS, DATA]); - const txFinish = helper.constructApiCall('api.tx.evmMigration.finish', [ADDRESS, CODE]); - await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txBegin])).to.be.fulfilled; - await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txSetData])).to.be.fulfilled; - await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txFinish])).to.be.fulfilled; - - const web3 = helper.getWeb3(); - const contract = new web3.eth.Contract([ - { - inputs: [], - name: 'counterValue', - outputs: [{ - internalType: 'uint256', - name: '', - type: 'uint256', - }], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [{ - internalType: 'uint256', - name: 'key', - type: 'uint256', - }], - name: 'get', - outputs: [{ - internalType: 'uint256', - name: '', - type: 'uint256', - }], - stateMutability: 'view', - type: 'function', - }, - ], ADDRESS, {from: caller, gas: helper.eth.DEFAULT_GAS}); - - expect(await contract.methods.counterValue().call()).to.be.equal('10'); - for(let i = 1; i <= 4; i++) { - expect(await contract.methods.get(i).call()).to.be.equal(i.toString()); - } - }); - itEth('Fake collection creation on substrate side', async ({helper}) => { - const txInsertEvents = helper.constructApiCall('api.tx.evmMigration.insertEvents', [[ - encodeEvent(helper.getApi(), 'Common', 'common', 'CollectionCreated', [ - // Collection Id - 9999, - // Collection mode: NFT - 1, - // Owner - charlie.address, - ]), - ]]); - await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEvents]); - const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; - const eventStrings = event.map(e => `${e.section}.${e.method}`); - - expect(eventStrings).to.contain('common.CollectionCreated'); - }); - itEth('Fake token creation on substrate side', async ({helper}) => { - const txInsertEvents = helper.constructApiCall('api.tx.evmMigration.insertEvents', [[ - encodeEvent(helper.getApi(), 'Common', 'common', 'ItemCreated', [ - // Collection Id - 9999, - // TokenId - 9999, - // Owner - {Substrate: charlie.address}, - // Amount - 1, - ]), - ]]); - await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEvents]); - const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[]; - const eventStrings = event.map(e => `${e.section}.${e.method}`); - - expect(eventStrings).to.contain('common.ItemCreated'); - }); - itEth('Fake token creation on ethereum side', async ({helper}) => { - const collection = await helper.nft.mintCollection(superuser); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const caller = await helper.eth.createAccountWithBalance(superuser); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - { - const txInsertEthLogs = helper.constructApiCall('api.tx.evmMigration.insertEthLogs', [[ - { - // Contract, which has emitted this log - address: collectionAddress, - - topics: [ - // First topic - event signature - helper.getWeb3().eth.abi.encodeEventSignature('Transfer(address,address,uint256)'), - // Rest of topics - indexed event fields in definition order - helper.getWeb3().eth.abi.encodeParameter('address', '0x' + '00'.repeat(20)), - helper.getWeb3().eth.abi.encodeParameter('address', caller), - helper.getWeb3().eth.abi.encodeParameter('uint256', 9999), - ], - - // Every field coming from event, which is not marked as indexed, should be encoded here - // NFT transfer has no such fields, but here is an example for some other possible event: - // data: helper.getWeb3().eth.abi.encodeParameters(['uint256', 'address'], [22, collectionAddress]) - data: [], - }, - ]]); - await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEthLogs]); - } - - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal('0x' + '00'.repeat(20)); - expect(event.returnValues.to).to.be.equal(caller); - expect(event.returnValues.tokenId).to.be.equal('9999'); - }); -}); --- a/js-packages/tests/src/eth/nativeFungible.test.ts +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2019-2023 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import {UniqueHelper} from '@unique/playgrounds/src/unique.js'; - -describe('NativeFungible: ERC20 calls', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('approve()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = helper.eth.createAccount(); - const collectionAddress = helper.ethAddress.fromCollectionId(0); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - await expect(contract.methods.approve(spender, 100).call({from: owner})).to.be.rejectedWith('approve not supported'); - }); - - itEth('balanceOf()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor, 123n); - const collectionAddress = helper.ethAddress.fromCollectionId(0); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - const balance = await contract.methods.balanceOf(owner).call({from: owner}); - expect(balance).to.be.eq('123000000000000000000'); - }); - - itEth('decimals()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(0); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - const realDecimals = (await helper.chain.getChainProperties().tokenDecimals)[0].toString(); - const decimals = await contract.methods.decimals().call({from: owner}); - expect(decimals).to.be.eq(realDecimals); - }); - - itEth('name()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(0); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - const realName = await UniqueHelper.detectNetwork(helper.getApi()); - const name = await contract.methods.name().call({from: owner}); - expect(name).to.be.eq(realName); - }); - - itEth('symbol()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(0); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - const realName = (await helper.chain.getChainProperties().tokenSymbol)[0]; - const name = await contract.methods.symbol().call({from: owner}); - expect(name).to.be.eq(realName); - }); - - itEth('totalSupply()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(0); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - const totalSupplyEth = BigInt(await contract.methods.totalSupply().call({from: owner})); - const totalSupplySub = await helper.balance.getTotalIssuance(); - expect(totalSupplyEth).to.be.eq(totalSupplySub); - }); - - itEth('transfer()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(0); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - const balanceOwnerBefore = await helper.balance.getEthereum(owner); - const balanceReceiverBefore = await helper.balance.getEthereum(receiver); - - await contract.methods.transfer(receiver, 50).send({from: owner}); - - const balanceOwnerAfter = await helper.balance.getEthereum(owner); - const balanceReceiverAfter = await helper.balance.getEthereum(receiver); - - expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true; - expect(balanceReceiverBefore + 50n).to.be.equal(balanceReceiverAfter); - }); - - itEth('transferFrom()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(0); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - const balanceOwnerBefore = await helper.balance.getEthereum(owner); - const balanceReceiverBefore = await helper.balance.getEthereum(receiver); - - await contract.methods.transferFrom(owner, receiver, 50).send({from: owner}); - - const balanceOwnerAfter = await helper.balance.getEthereum(owner); - const balanceReceiverAfter = await helper.balance.getEthereum(receiver); - - expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true; - expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true; - - await expect(contract.methods.transferFrom(receiver, receiver, 50).call({from: owner})).to.be.rejectedWith('ApprovedValueTooLow'); - }); -}); - -describe('NativeFungible: ERC20UniqueExtensions calls', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('transferCross()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(0); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - - const balanceOwnerBefore = await helper.balance.getEthereum(owner); - const balanceReceiverBefore = await helper.balance.getEthereum(receiver.eth); - - await contract.methods.transferCross(receiver, 50).send({from: owner}); - - const balanceOwnerAfter = await helper.balance.getEthereum(owner); - const balanceReceiverAfter = await helper.balance.getEthereum(receiver.eth); - - expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true; - expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true; - }); - - itEth('transferFromCross()', async ({helper}) => { - const owner = await helper.ethCrossAccount.createAccountWithBalance(donor); - const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(0); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth); - - const balanceOwnerBefore = await helper.balance.getEthereum(owner.eth); - const balanceReceiverBefore = await helper.balance.getEthereum(receiver.eth); - - await contract.methods.transferFromCross(owner, receiver, 50).send({from: owner.eth}); - - const balanceOwnerAfter = await helper.balance.getEthereum(owner.eth); - const balanceReceiverAfter = await helper.balance.getEthereum(receiver.eth); - - expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true; - expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true; - - await expect(contract.methods.transferFromCross(receiver, receiver, 50).call({from: owner.eth})).to.be.rejectedWith('no permission'); - }); -}); --- a/js-packages/tests/src/eth/nativeRpc/estimateGas.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {expect, itEth, usingEthPlaygrounds} from '../util/index.js'; -import type {IKeyringPair} from '@polkadot/types/types'; - - -describe('Ethereum native RPC calls', () => { - let donor: IKeyringPair; - const NATIVE_TOKEN_ADDRESS = '0x17c4e6453cc49aaaaeaca894e6d9683e00000000'; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('estimate gas', async ({helper}) => { - const BALANCE = 100n; - const BALANCE_TO_TRANSFER = 90n; - - const owner = await helper.eth.createAccountWithBalance(donor, BALANCE); - const recepient = helper.eth.createAccount(); - - const web3 = helper.getWeb3(); - // data: transfer(recepient, 90); - const data = web3.eth.abi.encodeFunctionCall({ - name: 'transfer', - type: 'function', - inputs: [{ - type: 'address', - name: 'to', - },{ - type: 'uint256', - name: 'amount', - }], - }, [recepient, (BALANCE_TO_TRANSFER * (10n ** 18n)).toString()]); - - const estimateGas = await web3.eth.estimateGas({ - to: NATIVE_TOKEN_ADDRESS, - value: '0x0', - data, - from: owner, - maxFeePerGas: '0x14c9338c61d', - }); - - expect(estimateGas).to.be.greaterThan(35000).and.to.be.lessThan(50000); - }); -}); --- a/js-packages/tests/src/eth/nesting/nest.test.ts +++ /dev/null @@ -1,289 +0,0 @@ -import type {IKeyringPair} from '@polkadot/types/types'; -import {Contract} from 'web3-eth-contract'; - -import {itEth, usingEthPlaygrounds, expect} from '../util/index.js'; -import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js'; - -const createNestingCollection = async ( - helper: EthUniqueHelper, - owner: string, - mergeDeprecated = false, -): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => { - const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, mergeDeprecated); - await contract.methods.setCollectionNesting([true, false, []]).send({from: owner}); - - return {collectionId, collectionAddress, contract}; -}; - - -describe('EVM nesting tests group', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - describe('Integration Test: EVM Nesting', () => { - itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId, contract} = await createNestingCollection(helper, owner); - - // Create a token to be nested to - const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner}); - const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId; - const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId); - - // Create a nested token - const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner}); - const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId; - expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress); - - // Create a token to be nested and nest - const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner}); - const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId; - - await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner}); - expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress); - - // Unnest token back - await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner}); - expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner); - }); - - itEth('NFT: collectionNesting()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress: unnestedCollectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const unnestedContract = await helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner); - expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]); - - const {contract} = await createNestingCollection(helper, owner); - expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, []]); - await contract.methods.setCollectionNesting([true, false, [unnestedCollectionAddress]]).send({from: owner}); - expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress]]); - await contract.methods.setCollectionNesting([false, true, [unnestedCollectionAddress]]).send({from: owner}); - expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, true, [unnestedCollectionAddress]]); - await contract.methods.setCollectionNesting([false, false, []]).send({from: owner}); - expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]); - }); - - // Sof-deprecated - itEth('NFT: collectionNestingRestrictedCollectionIds() & collectionNestingPermissions', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId: unnestedCollsectionId, collectionAddress: unnsetedCollectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const unnestedContract = await helper.ethNativeContract.collection(unnsetedCollectionAddress, 'nft', owner, true); - expect(await unnestedContract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([false, []]); - - const {contract} = await createNestingCollection(helper, owner, true); - expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, []]); - await contract.methods['setCollectionNesting(bool,address[])'](true, [unnsetedCollectionAddress]).send({from: owner}); - expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, [unnestedCollsectionId.toString()]]); - expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['1', false], ['0', true]]); - await contract.methods['setCollectionNesting(bool)'](false).send({from: owner}); - expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['1', false], ['0', false]]); - }); - - itEth('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner); - const {contract: contractB} = await createNestingCollection(helper, owner); - await contractA.methods.setCollectionNesting([true, false, [contractA.options.address, contractB.options.address]]).send({from: owner}); - - // Create a token to nest into - const mintingtargetNftTokenIdResult = await contractA.methods.mint(owner).send({from: owner}); - const targetNftTokenId = mintingtargetNftTokenIdResult.events.Transfer.returnValues.tokenId; - const nftTokenAddressA1 = helper.ethAddress.fromTokenId(collectionIdA, targetNftTokenId); - - // Create a token for nesting in the same collection as the target - const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner}); - const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId; - - // Create a token for nesting in a different collection - const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner}); - const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId; - - // Nest - await contractA.methods.transfer(nftTokenAddressA1, nftTokenIdA).send({from: owner}); - expect(await contractA.methods.ownerOf(nftTokenIdA).call()).to.be.equal(nftTokenAddressA1); - - await contractB.methods.transfer(nftTokenAddressA1, nftTokenIdB).send({from: owner}); - expect(await contractB.methods.ownerOf(nftTokenIdB).call()).to.be.equal(nftTokenAddressA1); - }); - }); - - describe('Negative Test: EVM Nesting', () => { - itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionId, contract} = await createNestingCollection(helper, owner); - await contract.methods.setCollectionNesting([false, false, []]).send({from: owner}); - - // Create a token to nest into - const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner}); - const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; - const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId); - - // Create a token to nest - const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner}); - const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId; - - // Try to nest - await expect(contract.methods - .transfer(targetNftTokenAddress, nftTokenId) - .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest'); - }); - - itEth('NFT: disallows a non-Owner to nest someone else\'s token', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const malignant = await helper.eth.createAccountWithBalance(donor); - - const {collectionId, contract} = await createNestingCollection(helper, owner); - - // Mint a token - const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner}); - const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; - const targetTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId); - - // Mint a token belonging to a different account - const mintingTokenIdResult = await contract.methods.mint(malignant).send({from: owner}); - const tokenId = mintingTokenIdResult.events.Transfer.returnValues.tokenId; - - // Try to nest one token in another as a non-owner account - await expect(contract.methods - .transfer(targetTokenAddress, tokenId) - .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest'); - }); - - itEth('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const malignant = await helper.eth.createAccountWithBalance(donor); - - const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner); - const {contract: contractB} = await createNestingCollection(helper, owner); - - await contractA.methods.setCollectionNesting([true, false, [contractA.options.address, contractB.options.address]]).send({from: owner}); - - // Create a token in one collection - const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner}); - const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId; - const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA); - - // Create a token in another collection - const mintingTokenIdBResult = await contractB.methods.mint(malignant).send({from: owner}); - const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId; - - // Try to drag someone else's token into the other collection and nest - await expect(contractB.methods - .transfer(nftTokenAddressA, nftTokenIdB) - .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest'); - }); - - itEth('NFT: disallows to nest token in an unlisted collection', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner); - const {contract: contractB} = await createNestingCollection(helper, owner); - - await contractA.methods.setCollectionNesting([true, false, [contractA.options.address]]).send({from: owner}); - - // Create a token in one collection - const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner}); - const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId; - const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA); - - // Create a token in another collection - const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner}); - const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId; - - - // Try to nest into a token in the other collection, disallowed in the first - await expect(contractB.methods - .transfer(nftTokenAddressA, nftTokenIdB) - .call()).to.be.rejectedWith('SourceCollectionIsNotAllowedToNest'); - }); - }); - - describe('Fungible', () => { - async function createFungibleCollection(helper: EthUniqueHelper, owner: string, mode: 'ft' | 'native ft') { - if(mode === 'ft') { - const {collectionAddress} = await helper.eth.createFungibleCollection(owner, '', 18, '', ''); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - await contract.methods.mint(owner, 100n).send({from: owner}); - return {collectionAddress, contract}; - } - - // native ft - const collectionAddress = helper.ethAddress.fromCollectionId(0); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - return {collectionAddress, contract}; - } - - [ - {mode: 'ft' as const}, - {mode: 'native ft' as const}, - ].map(testCase => { - itEth(`Allow nest [${testCase.mode}]`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner); - const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode); - - const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner}); - const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; - const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId); - - await ftContract.methods.transfer(targetTokenAddress, 10n).send({from: owner}); - expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('10'); - }); - }); - - [ - {mode: 'ft' as const}, - {mode: 'native ft' as const}, - ].map(testCase => { - itEth(`Allow partial/full unnest [${testCase.mode}]`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner); - const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode); - - const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner}); - const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; - const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId); - - await ftContract.methods.transfer(targetTokenAddress, 10n).send({from: owner}); - - await ftContract.methods.transferFrom(targetTokenAddress, owner, 5n).send({from: owner}); - expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('5'); - - await ftContract.methods.transferFrom(targetTokenAddress, owner, 5n).send({from: owner}); - expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('0'); - }); - }); - - [ - {mode: 'ft' as const}, - {mode: 'native ft' as const}, - ].map(testCase => { - itEth(`Disallow nest into collection without nesting permission [${testCase.mode}] (except for native fungible collection)`, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner); - await targetContract.methods.setCollectionNesting([false, false, []]).send({from: owner}); - - const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode); - - const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner}); - const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId; - const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId); - - if(testCase.mode === 'ft') { - await expect(ftContract.methods.transfer(targetTokenAddress, 10n).call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest'); - } else { - await expect(ftContract.methods.transfer(targetTokenAddress, 10n).call({from: owner})).to.be.not.rejected; - } - }); - }); - }); -}); --- a/js-packages/tests/src/eth/nonFungible.test.ts +++ /dev/null @@ -1,1127 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; -import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {Contract} from 'web3-eth-contract'; -import type {ITokenPropertyPermission} from '@unique/playgrounds/src/types.js'; -import {CREATE_COLLECTION_DATA_DEFAULTS, TokenPermissionField} from './util/playgrounds/types.js'; - -describe('Check ERC721 token URI for NFT', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', baseUri); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - const result = await contract.methods.mint(receiver).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - expect(tokenId).to.be.equal('1'); - - if(propertyKey && propertyValue) { - // Set URL or suffix - await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send(); - } - - const event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.tokenId).to.be.equal(tokenId); - - return {contract, nextTokenId: tokenId}; - } - - itEth('Empty tokenURI', async ({helper}) => { - const {contract, nextTokenId} = await setup(helper, ''); - expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal(''); - }); - - itEth('TokenURI from url', async ({helper}) => { - const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI'); - expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI'); - }); - - itEth('TokenURI from baseURI', async ({helper}) => { - const {contract, nextTokenId} = await setup(helper, 'BaseURI_'); - expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_'); - }); - - itEth('TokenURI from baseURI + suffix', async ({helper}) => { - const suffix = '/some/suffix'; - const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix); - expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix); - }); -}); - -describe('NFT: Plain calls', () => { - let donor: IKeyringPair; - let minter: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - // TODO combine all minting tests in one place - [ - 'substrate' as const, - 'ethereum' as const, - ].map(testCase => { - itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => { - const collectionAdmin = await helper.eth.createAccountWithBalance(donor); - - const receiverEth = helper.eth.createAccount(); - const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); - const receiverSub = bob; - const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub); - - // const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob); - const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)})); - const permissions: ITokenPropertyPermission[] = properties - .map(p => ({ - key: p.key, permission: { - tokenOwner: false, - collectionAdmin: true, - mutable: false, - }, - })); - - const collection = await helper.nft.mintCollection(minter, { - tokenPrefix: 'ethp', - tokenPropertyPermissions: permissions, - }); - await collection.addAdmin(minter, {Ethereum: collectionAdmin}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', collectionAdmin, true); - let expectedTokenId = await contract.methods.nextTokenId().call(); - let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send(); - let tokenId = result.events.Transfer.returnValues.tokenId; - expect(tokenId).to.be.equal(expectedTokenId); - - let event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address)); - expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); - - expectedTokenId = await contract.methods.nextTokenId().call(); - result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send(); - event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address)); - expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); - - tokenId = result.events.Transfer.returnValues.tokenId; - - expect(tokenId).to.be.equal(expectedTokenId); - - expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties - .map(p => helper.ethProperty.property(p.key, p.value.toString()))); - - expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)) - .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address}); - }); - }); - - itEth('Non-owner and non admin cannot mintCross', async ({helper}) => { - const nonOwner = await helper.eth.createAccountWithBalance(donor); - const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner); - - const collection = await helper.nft.mintCollection(minter); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft'); - - await expect(collectionEvm.methods.mintCross(nonOwnerCross, []).call({from: nonOwner})) - .to.be.rejectedWith('PublicMintingNotAllowed'); - }); - - //TODO: CORE-302 add eth methods - itEth.skip('Can perform mintBulk()', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const collection = await helper.nft.mintCollection(minter); - await collection.addAdmin(minter, {Ethereum: caller}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); - { - const bulkSize = 3; - const nextTokenId = await contract.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - const result = await contract.methods.mintBulkWithTokenURI( - receiver, - Array.from({length: bulkSize}, (_, i) => ( - [+nextTokenId + i, `Test URI ${i}`] - )), - ).send({from: caller}); - - const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId); - for(let i = 0; i < bulkSize; i++) { - const event = events[i]; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.equal(receiver); - expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`); - - expect(await contract.methods.tokenURI(+nextTokenId + i).call()).to.be.equal(`Test URI ${i}`); - } - } - }); - - itEth('Can perform mintBulkCross()', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const callerCross = helper.ethCrossAccount.fromAddress(caller); - const receiver = helper.eth.createAccount(); - const receiverCross = helper.ethCrossAccount.fromAddress(receiver); - - const permissions = [ - {code: TokenPermissionField.Mutable, value: true}, - {code: TokenPermissionField.TokenOwner, value: true}, - {code: TokenPermissionField.CollectionAdmin, value: true}, - ]; - const {collectionAddress} = await helper.eth.createCollection( - caller, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: 'nft', - adminList: [callerCross], - tokenPropertyPermissions: [ - {key: 'key_0_0', permissions}, - {key: 'key_1_0', permissions}, - {key: 'key_1_1', permissions}, - {key: 'key_2_0', permissions}, - {key: 'key_2_1', permissions}, - {key: 'key_2_2', permissions}, - ], - }, - ).send(); - - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); - { - const nextTokenId = await contract.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - const result = await contract.methods.mintBulkCross([ - { - owner: receiverCross, - properties: [ - {key: 'key_0_0', value: Buffer.from('value_0_0')}, - ], - }, - { - owner: receiverCross, - properties: [ - {key: 'key_1_0', value: Buffer.from('value_1_0')}, - {key: 'key_1_1', value: Buffer.from('value_1_1')}, - ], - }, - { - owner: receiverCross, - properties: [ - {key: 'key_2_0', value: Buffer.from('value_2_0')}, - {key: 'key_2_1', value: Buffer.from('value_2_1')}, - {key: 'key_2_2', value: Buffer.from('value_2_2')}, - ], - }, - ]).send({from: caller}); - const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId); - const bulkSize = 3; - for(let i = 0; i < bulkSize; i++) { - const event = events[i]; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.equal(receiver); - expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`); - } - - const properties = [ - await contract.methods.properties(+nextTokenId, []).call(), - await contract.methods.properties(+nextTokenId + 1, []).call(), - await contract.methods.properties(+nextTokenId + 2, []).call(), - ]; - expect(properties).to.be.deep.equal([ - [ - ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')], - ], - [ - ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')], - ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')], - ], - [ - ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')], - ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')], - ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')], - ], - ]); - } - }); - - itEth('Can perform burn()', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - - const collection = await helper.nft.mintCollection(minter, {}); - const {tokenId} = await collection.mintToken(minter, {Ethereum: caller}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); - - { - const result = await contract.methods.burn(tokenId).send({from: caller}); - - const event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(caller); - expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); - } - }); - - itEth('Can perform approve()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = helper.eth.createAccount(); - - const collection = await helper.nft.mintCollection(minter, {}); - const {tokenId} = await collection.mintToken(minter, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - { - const badTokenId = await contract.methods.nextTokenId().call() + 1; - await expect(contract.methods.getApproved(badTokenId).call()).to.be.rejectedWith('revert TokenNotFound'); - } - { - const approved = await contract.methods.getApproved(tokenId).call(); - expect(approved).to.be.equal('0x0000000000000000000000000000000000000000'); - } - { - const result = await contract.methods.approve(spender, tokenId).send({from: owner}); - - const event = result.events.Approval; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.owner).to.be.equal(owner); - expect(event.returnValues.approved).to.be.equal(spender); - expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); - } - { - const approved = await contract.methods.getApproved(tokenId).call(); - expect(approved).to.be.equal(spender); - } - }); - - itEth('Can perform setApprovalForAll()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const operator = helper.eth.createAccount(); - - const collection = await helper.nft.mintCollection(minter, {}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call(); - expect(approvedBefore).to.be.equal(false); - - { - const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner}); - - expect(result.events.ApprovalForAll).to.be.like({ - address: collectionAddress, - event: 'ApprovalForAll', - returnValues: { - owner, - operator, - approved: true, - }, - }); - - const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call(); - expect(approvedAfter).to.be.equal(true); - } - - { - const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner}); - - expect(result.events.ApprovalForAll).to.be.like({ - address: collectionAddress, - event: 'ApprovalForAll', - returnValues: { - owner, - operator, - approved: false, - }, - }); - - const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call(); - expect(approvedAfter).to.be.equal(false); - } - }); - - itEth('Can perform burn with ApprovalForAll', async ({helper}) => { - const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const owner = await helper.eth.createAccountWithBalance(donor); - const operator = await helper.eth.createAccountWithBalance(donor); - - const token = await collection.mintToken(minter, {Ethereum: owner}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft'); - - { - await contract.methods.setApprovalForAll(operator, true).send({from: owner}); - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator}); - const events = result.events.Transfer; - - expect(events).to.be.like({ - address, - event: 'Transfer', - returnValues: { - from: owner, - to: '0x0000000000000000000000000000000000000000', - tokenId: token.tokenId.toString(), - }, - }); - } - - expect(await helper.nft.doesTokenExist(collection.collectionId, token.tokenId)).to.be.false; - }); - - itEth('Can perform transfer with ApprovalForAll', async ({helper}) => { - const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const owner = await helper.eth.createAccountWithBalance(donor); - const operator = await helper.eth.createAccountWithBalance(donor); - const receiver = charlie; - - const token = await collection.mintToken(minter, {Ethereum: owner}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft'); - - { - await contract.methods.setApprovalForAll(operator, true).send({from: owner}); - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); - const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator}); - const event = result.events.Transfer; - expect(event).to.be.like({ - address: helper.ethAddress.fromCollectionId(collection.collectionId), - event: 'Transfer', - returnValues: { - from: owner, - to: helper.address.substrateToEth(receiver.address), - tokenId: token.tokenId.toString(), - }, - }); - } - - expect(await token.getOwner()).to.be.like({Substrate: receiver.address}); - }); - - itEth('Can perform burnFromCross()', async ({helper}) => { - const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - const ownerSub = bob; - const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub); - const ownerEth = await helper.eth.createAccountWithBalance(donor); - const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth); - - const burnerEth = await helper.eth.createAccountWithBalance(donor); - const burnerCrossEth = helper.ethCrossAccount.fromAddress(burnerEth); - - const token1 = await collection.mintToken(minter, {Substrate: ownerSub.address}); - const token2 = await collection.mintToken(minter, {Ethereum: ownerEth}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft'); - - // Approve tokens from substrate and ethereum: - await token1.approve(ownerSub, {Ethereum: burnerEth}); - await collectionEvm.methods.approveCross(burnerCrossEth, token2.tokenId).send({from: ownerEth}); - - // can burnFromCross: - const result1 = await collectionEvm.methods.burnFromCross(ownerCrossSub, token1.tokenId).send({from: burnerEth}); - const result2 = await collectionEvm.methods.burnFromCross(ownerCrossEth, token2.tokenId).send({from: burnerEth}); - const events1 = result1.events.Transfer; - const events2 = result2.events.Transfer; - - // Check events for burnFromCross (substrate and ethereum): - [ - [events1, token1, helper.address.substrateToEth(ownerSub.address)], - [events2, token2, ownerEth], - ].map(burnData => { - expect(burnData[0]).to.be.like({ - address: collectionAddress, - event: 'Transfer', - returnValues: { - from: burnData[2], - to: '0x0000000000000000000000000000000000000000', - tokenId: burnData[1].tokenId.toString(), - }, - }); - }); - - expect(await token1.doesExist()).to.be.false; - expect(await token2.doesExist()).to.be.false; - }); - - // TODO combine all approve tests in one place - itEth('Can perform approveCross()', async ({helper}) => { - // arrange: create accounts - const owner = await helper.eth.createAccountWithBalance(donor); - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const receiverSub = charlie; - const recieverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub); - const receiverEth = await helper.eth.createAccountWithBalance(donor); - const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); - - // arrange: create collection and tokens: - const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - const token1 = await collection.mintToken(minter, {Ethereum: owner}); - const token2 = await collection.mintToken(minter, {Ethereum: owner}); - - const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); - - // Can approveCross substrate and ethereum address: - const resultSub = await collectionEvm.methods.approveCross(recieverCrossSub, token1.tokenId).send({from: owner}); - const resultEth = await collectionEvm.methods.approveCross(receiverCrossEth, token2.tokenId).send({from: owner}); - const eventSub = resultSub.events.Approval; - const eventEth = resultEth.events.Approval; - expect(eventSub).to.be.like({ - address: helper.ethAddress.fromCollectionId(collection.collectionId), - event: 'Approval', - returnValues: { - owner, - approved: helper.address.substrateToEth(receiverSub.address), - tokenId: token1.tokenId.toString(), - }, - }); - expect(eventEth).to.be.like({ - address: helper.ethAddress.fromCollectionId(collection.collectionId), - event: 'Approval', - returnValues: { - owner, - approved: receiverEth, - tokenId: token2.tokenId.toString(), - }, - }); - - // Substrate address can transferFrom approved tokens: - await helper.nft.transferTokenFrom(receiverSub, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiverSub.address}); - expect(await helper.nft.getTokenOwner(collection.collectionId, token1.tokenId)).to.deep.eq({Substrate: receiverSub.address}); - // Ethereum address can transferFromCross approved tokens: - await collectionEvm.methods.transferFromCross(ownerCross, receiverCrossEth, token2.tokenId).send({from: receiverEth}); - expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()}); - }); - - itEth('Non-owner and non admin cannot approveCross', async ({helper}) => { - const nonOwner = await helper.eth.createAccountWithBalance(donor); - const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner); - const owner = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); - const token = await collection.mintToken(minter, {Ethereum: owner}); - - await expect(collectionEvm.methods.approveCross(nonOwnerCross, token.tokenId).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned'); - }); - - itEth('Can reaffirm approved address', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner); - const [receiver1, receiver2] = await helper.arrange.createAccounts([100n, 100n], donor); - const receiver1Cross = helper.ethCrossAccount.fromKeyringPair(receiver1); - const receiver2Cross = helper.ethCrossAccount.fromKeyringPair(receiver2); - const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - const token1 = await collection.mintToken(minter, {Ethereum: owner}); - const token2 = await collection.mintToken(minter, {Ethereum: owner}); - const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); - - // Can approve and reaffirm approved address: - await collectionEvm.methods.approveCross(receiver1Cross, token1.tokenId).send({from: owner}); - await collectionEvm.methods.approveCross(receiver2Cross, token1.tokenId).send({from: owner}); - - // receiver1 cannot transferFrom: - await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected; - // receiver2 can transferFrom: - await helper.nft.transferTokenFrom(receiver2, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver2.address}); - - // can set approved address to self address to remove approval: - await collectionEvm.methods.approveCross(receiver1Cross, token2.tokenId).send({from: owner}); - await collectionEvm.methods.approveCross(ownerCrossEth, token2.tokenId).send({from: owner}); - - // receiver1 cannot transfer token anymore: - await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token2.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected; - }); - - itEth('Can perform transferFrom()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const collection = await helper.nft.mintCollection(minter, {}); - const {tokenId} = await collection.mintToken(minter, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - await contract.methods.approve(spender, tokenId).send({from: owner}); - - { - const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: spender}); - - const event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(owner); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(1); - } - - { - const balance = await contract.methods.balanceOf(owner).call(); - expect(+balance).to.equal(0); - } - }); - - itEth('Can perform transferFromCross()', async ({helper}) => { - const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const [owner, receiver] = await helper.arrange.createAccounts([100n, 100n], donor); - const spender = await helper.eth.createAccountWithBalance(donor); - - const token = await collection.mintToken(minter, {Substrate: owner.address}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft'); - - await token.approve(owner, {Ethereum: spender}); - - { - const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner); - const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); - const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender}); - const event = result.events.Transfer; - expect(event).to.be.like({ - address: helper.ethAddress.fromCollectionId(collection.collectionId), - event: 'Transfer', - returnValues: { - from: helper.address.substrateToEth(owner.address), - to: helper.address.substrateToEth(receiver.address), - tokenId: token.tokenId.toString(), - }, - }); - } - - expect(await token.getOwner()).to.be.like({Substrate: receiver.address}); - }); - - itEth('Can perform transfer()', async ({helper}) => { - const collection = await helper.nft.mintCollection(minter, {}); - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const {tokenId} = await collection.mintToken(minter, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - { - const result = await contract.methods.transfer(receiver, tokenId).send({from: owner}); - - const event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(owner); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); - } - - { - const balance = await contract.methods.balanceOf(owner).call(); - expect(+balance).to.equal(0); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(1); - } - }); - - itEth('Can perform transferCross()', async ({helper}) => { - const collection = await helper.nft.mintCollection(minter, {}); - const owner = await helper.eth.createAccountWithBalance(donor); - const receiverEth = await helper.eth.createAccountWithBalance(donor); - const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); - const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter); - - const {tokenId} = await collection.mintToken(minter, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - { - // Can transferCross to ethereum address: - const result = await collectionEvm.methods.transferCross(receiverCrossEth, tokenId).send({from: owner}); - // Check events: - const event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(owner); - expect(event.returnValues.to).to.be.equal(receiverEth); - expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); - - // owner has balance = 0: - const ownerBalance = await collectionEvm.methods.balanceOf(owner).call(); - expect(+ownerBalance).to.equal(0); - // receiver owns token: - const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); - expect(+receiverBalance).to.equal(1); - expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()}); - } - - { - // Can transferCross to substrate address: - const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, tokenId).send({from: receiverEth}); - // Check events: - const event = substrateResult.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(receiverEth); - expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address)); - expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); - - // owner has balance = 0: - const ownerBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); - expect(+ownerBalance).to.equal(0); - // receiver owns token: - const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address}); - expect(receiverBalance).to.contain(tokenId); - } - }); - - ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => { - const sender = await helper.eth.createAccountWithBalance(donor); - const tokenOwner = await helper.eth.createAccountWithBalance(donor); - const receiverSub = minter; - const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter); - - const collection = await helper.nft.mintCollection(minter, {}); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', sender); - - await collection.mintToken(minter, {Ethereum: sender}); - const nonSendersToken = await collection.mintToken(minter, {Ethereum: tokenOwner}); - - // Cannot transferCross someone else's token: - const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub; - await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected; - // Cannot transfer token if it does not exist: - await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected; - })); - - itEth('Check balanceOfCross()', async ({helper}) => { - const collection = await helper.nft.mintCollection(minter, {}); - const owner = await helper.ethCrossAccount.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth); - - expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0'); - - for(let i = 1; i < 10; i++) { - await collection.mintToken(minter, {Ethereum: owner.eth}); - expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString()); - } - }); - - itEth('Check ownerOfCross()', async ({helper}) => { - const collection = await helper.nft.mintCollection(minter, {}); - let owner = await helper.ethCrossAccount.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth); - const {tokenId} = await collection.mintToken(minter, {Ethereum: owner.eth}); - - for(let i = 1n; i < 10n; i++) { - const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth}); - expect(ownerCross.eth).to.be.eq(owner.eth); - expect(ownerCross.sub).to.be.eq(owner.sub); - - const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor); - await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth}); - owner = newOwner; - } - }); -}); - -describe('NFT: Fees', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - }); - }); - - itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = helper.eth.createAccount(); - - const collection = await helper.nft.mintCollection(alice, {}); - const {tokenId} = await collection.mintToken(alice, {Ethereum: owner}); - - const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner); - - const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, tokenId).send({from: owner})); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - }); - - itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = await helper.eth.createAccountWithBalance(donor); - - const collection = await helper.nft.mintCollection(alice, {}); - const {tokenId} = await collection.mintToken(alice, {Ethereum: owner}); - - const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner); - - await contract.methods.approve(spender, tokenId).send({from: owner}); - - const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender})); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - }); - - itEth('Can perform transferFromCross()', async ({helper}) => { - const collectionMinter = alice; - const owner = bob; - const receiver = charlie; - const collection = await helper.nft.mintCollection(collectionMinter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const spender = await helper.eth.createAccountWithBalance(donor); - - const token = await collection.mintToken(collectionMinter, {Substrate: owner.address}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft'); - - await token.approve(owner, {Ethereum: spender}); - - { - const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner); - const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); - const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender}); - const event = result.events.Transfer; - expect(event).to.be.like({ - address: helper.ethAddress.fromCollectionId(collection.collectionId), - event: 'Transfer', - returnValues: { - from: helper.address.substrateToEth(owner.address), - to: helper.address.substrateToEth(receiver.address), - tokenId: token.tokenId.toString(), - }, - }); - } - - expect(await token.getOwner()).to.be.like({Substrate: receiver.address}); - }); - - itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const collection = await helper.nft.mintCollection(alice, {}); - const {tokenId} = await collection.mintToken(alice, {Ethereum: owner}); - - const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner); - - const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner})); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - }); -}); - -describe('NFT: Substrate calls', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([20n], donor); - }); - }); - - itEth('Events emitted for mint()', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {}); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft'); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - const {tokenId} = await collection.mintToken(alice); - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.event).to.be.equal('Transfer'); - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.tokenId).to.be.equal(tokenId.toString()); - }); - - itEth('Events emitted for burn()', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {}); - const token = await collection.mintToken(alice); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft'); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - await token.burn(alice); - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.event).to.be.equal('Transfer'); - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString()); - }); - - itEth('Events emitted for approve()', async ({helper}) => { - const receiver = helper.eth.createAccount(); - - const collection = await helper.nft.mintCollection(alice, {}); - const token = await collection.mintToken(alice); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft'); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - await token.approve(alice, {Ethereum: receiver}); - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.event).to.be.equal('Approval'); - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.approved).to.be.equal(receiver); - expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString()); - }); - - itEth('Events emitted for transferFrom()', async ({helper}) => { - const [bob] = await helper.arrange.createAccounts([10n], donor); - const receiver = helper.eth.createAccount(); - - const collection = await helper.nft.mintCollection(alice, {}); - const token = await collection.mintToken(alice); - await token.approve(alice, {Substrate: bob.address}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft'); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}); - - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`); - }); - - itEth('Events emitted for transfer()', async ({helper}) => { - const receiver = helper.eth.createAccount(); - - const collection = await helper.nft.mintCollection(alice, {}); - const token = await collection.mintToken(alice); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft'); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - await token.transfer(alice, {Ethereum: receiver}); - - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`); - }); -}); - -describe('Common metadata', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([20n], donor); - }); - }); - - itEth('Returns collection name', async ({helper}) => { - const caller = helper.eth.createAccount(); - const tokenPropertyPermissions = [{ - key: 'URI', - permission: { - mutable: true, - collectionAdmin: true, - tokenOwner: false, - }, - }]; - const collection = await helper.nft.mintCollection( - alice, - { - name: 'oh River', - tokenPrefix: 'CHANGE', - properties: [{key: 'ERC721Metadata', value: '1'}], - tokenPropertyPermissions, - }, - ); - - const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller); - const name = await contract.methods.name().call(); - expect(name).to.equal('oh River'); - }); - - itEth('Returns symbol name', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const tokenPropertyPermissions = [{ - key: 'URI', - permission: { - mutable: true, - collectionAdmin: true, - tokenOwner: false, - }, - }]; - const collection = await helper.nft.mintCollection( - alice, - { - name: 'oh River', - tokenPrefix: 'CHANGE', - properties: [{key: 'ERC721Metadata', value: '1'}], - tokenPropertyPermissions, - }, - ); - - const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller); - const symbol = await contract.methods.symbol().call(); - expect(symbol).to.equal('CHANGE'); - }); -}); - -describe('Negative tests', () => { - let donor: IKeyringPair; - let minter: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itEth('[negative] Cant perform burn without approval', async ({helper}) => { - const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = await helper.eth.createAccountWithBalance(donor); - - const token = await collection.mintToken(minter, {Ethereum: owner}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft'); - - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected; - - await contract.methods.setApprovalForAll(spender, true).send({from: owner}); - await contract.methods.setApprovalForAll(spender, false).send({from: owner}); - - await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected; - }); - - itEth('[negative] Cant perform transfer without approval', async ({helper}) => { - const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - const receiver = alice; - - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = await helper.eth.createAccountWithBalance(donor); - - const token = await collection.mintToken(minter, {Ethereum: owner}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft'); - - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); - - await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected; - - await contract.methods.setApprovalForAll(spender, true).send({from: owner}); - await contract.methods.setApprovalForAll(spender, false).send({from: owner}); - - await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected; - }); -}); --- a/js-packages/tests/src/eth/payable.test.ts +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; - -import {itEth, expect, usingEthPlaygrounds} from './util/index.js'; -import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; -import {makeNames} from '../util/index.js'; - -const {dirname} = makeNames(import.meta.url); - -describe('EVM payable contracts', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Evm contract can receive wei from eth account', async ({helper}) => { - const deployer = await helper.eth.createAccountWithBalance(donor); - const contract = await helper.eth.deployCollectorContract(deployer); - - const web3 = helper.getWeb3(); - - await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', gas: helper.eth.DEFAULT_GAS}); - - expect(await contract.methods.getCollected().call()).to.be.equal('10000'); - }); - - itEth('Evm contract can receive wei from substrate account', async ({helper}) => { - const deployer = await helper.eth.createAccountWithBalance(donor); - const contract = await helper.eth.deployCollectorContract(deployer); - const [alice] = await helper.arrange.createAccounts([40n], donor); - - const weiCount = '10000'; - - // Transaction fee/value will be payed from subToEth(sender) evm balance, - // which is backed by evmToAddress(subToEth(sender)) substrate balance - await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address), 5n); - - - await helper.eth.sendEVM(alice, contract.options.address, contract.methods.giveMoney().encodeABI(), weiCount); - - expect(await contract.methods.getCollected().call()).to.be.equal(weiCount); - }); - - // We can't handle sending balance to backing storage of evm balance, because evmToAddress operation is irreversible - itEth('Wei sent directly to backing storage of evm contract balance is unaccounted', async({helper}) => { - const deployer = await helper.eth.createAccountWithBalance(donor); - const contract = await helper.eth.deployCollectorContract(deployer); - const [alice] = await helper.arrange.createAccounts([10n], donor); - - const weiCount = 10_000n; - - await helper.eth.transferBalanceFromSubstrate(alice, contract.options.address, weiCount, false); - - expect(await contract.methods.getUnaccounted().call()).to.be.equal(weiCount.toString()); - }); - - itEth('Balance can be retrieved from evm contract', async({helper}) => { - const FEE_BALANCE = 10n * helper.balance.getOneTokenNominal(); - const CONTRACT_BALANCE = 1n * helper.balance.getOneTokenNominal(); - - const deployer = await helper.eth.createAccountWithBalance(donor); - const contract = await helper.eth.deployCollectorContract(deployer); - const [alice] = await helper.arrange.createAccounts([20n], donor); - - const web3 = helper.getWeb3(); - - await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS}); - - const [receiver] = await helper.arrange.createAccounts([0n], donor); - - // First receive balance on eth balance of bob - { - const ethReceiver = helper.address.substrateToEth(receiver.address); - expect(await web3.eth.getBalance(ethReceiver)).to.be.equal('0'); - await contract.methods.withdraw(ethReceiver).send({from: deployer}); - expect(await web3.eth.getBalance(ethReceiver)).to.be.equal(CONTRACT_BALANCE.toString()); - } - - // Some balance is required to pay fee for evm.withdraw call - await helper.balance.transferToSubstrate(alice, receiver.address, FEE_BALANCE); - // await transferBalanceExpectSuccess(api, alice, receiver.address, FEE_BALANCE.toString()); - - // Withdraw balance from eth to substrate - { - const initialReceiverBalance = await helper.balance.getSubstrate(receiver.address); - await helper.executeExtrinsic(receiver, 'api.tx.evm.withdraw', [helper.address.substrateToEth(receiver.address), CONTRACT_BALANCE.toString()], true); - const finalReceiverBalance = await helper.balance.getSubstrate(receiver.address); - - expect(finalReceiverBalance > initialReceiverBalance).to.be.true; - } - }); -}); - -describe('EVM transaction fees', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Fee is withdrawn from the user', async({helper}) => { - const deployer = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - const contract = await helper.eth.deployFlipper(deployer); - - const initialCallerBalance = await helper.balance.getEthereum(caller); - await contract.methods.flip().send({from: caller}); - const finalCallerBalance = await helper.balance.getEthereum(caller); - expect(finalCallerBalance < initialCallerBalance).to.be.true; - }); - - itEth('Fee for nested calls is withdrawn from the user', async({helper}) => { - const deployer = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - const contract = await deployProxyContract(helper, deployer); - - const initialCallerBalance = await helper.balance.getEthereum(caller); - const initialContractBalance = await helper.balance.getEthereum(contract.options.address); - await contract.methods.flip().send({from: caller}); - const finalCallerBalance = await helper.balance.getEthereum(caller); - const finalContractBalance = await helper.balance.getEthereum(contract.options.address); - expect(finalCallerBalance < initialCallerBalance).to.be.true; - expect(finalContractBalance == initialContractBalance).to.be.true; - }); - - itEth('Fee for nested calls to native methods is withdrawn from the user', async({helper}) => { - const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal(); - - const deployer = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - const contract = await deployProxyContract(helper, deployer); - - const collectionAddress = (await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection; - const initialCallerBalance = await helper.balance.getEthereum(caller); - const initialContractBalance = await helper.balance.getEthereum(contract.options.address); - await contract.methods.mintNftToken(collectionAddress).send({from: caller}); - const finalCallerBalance = await helper.balance.getEthereum(caller); - const finalContractBalance = await helper.balance.getEthereum(contract.options.address); - expect(finalCallerBalance < initialCallerBalance).to.be.true; - expect(finalContractBalance == initialContractBalance).to.be.true; - }); - - itEth('Fee for nested calls to create*Collection methods is withdrawn from the user and from the contract', async({helper}) => { - const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal(); - const deployer = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - const contract = await deployProxyContract(helper, deployer); - - const initialCallerBalance = await helper.balance.getEthereum(caller); - const initialContractBalance = await helper.balance.getEthereum(contract.options.address); - await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)}); - const finalCallerBalance = await helper.balance.getEthereum(caller); - const finalContractBalance = await helper.balance.getEthereum(contract.options.address); - expect(finalCallerBalance < initialCallerBalance).to.be.true; - expect(finalContractBalance == initialContractBalance).to.be.true; - }); - - itEth('Negative test: call createNFTCollection with wrong fee', async({helper}) => { - const SMALL_FEE = 1n * helper.balance.getOneTokenNominal(); - const BIG_FEE = 3n * helper.balance.getOneTokenNominal(); - const caller = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller); - - await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); - await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); - }); - - itEth('Negative test: call createRFTCollection with wrong fee', async({helper}) => { - const SMALL_FEE = 1n * helper.balance.getOneTokenNominal(); - const BIG_FEE = 3n * helper.balance.getOneTokenNominal(); - const caller = await helper.eth.createAccountWithBalance(donor); - const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller); - - await expect(collectionHelper.methods.createRFTCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); - await expect(collectionHelper.methods.createRFTCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); - }); - - itEth('Get collection creation fee', async({helper}) => { - const deployer = await helper.eth.createAccountWithBalance(donor); - expect(await helper.eth.getCollectionCreationFee(deployer)).to.be.equal(String(2n * helper.balance.getOneTokenNominal())); - }); - - async function deployProxyContract(helper: EthUniqueHelper, deployer: string) { - return await helper.ethContract.deployByCode( - deployer, - 'ProxyContract', - ` - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.6; - - import {CollectionHelpers} from "../api/CollectionHelpers.sol"; - import {UniqueNFT} from "../api/UniqueNFT.sol"; - - error Value(uint256 value); - - contract ProxyContract { - bool value = false; - address innerContract; - - event CollectionCreated(address collection); - event TokenMinted(uint256 tokenId); - - receive() external payable {} - - constructor() { - innerContract = address(new InnerContract()); - } - - function flip() public { - value = !value; - InnerContract(innerContract).flip(); - } - - function createNFTCollection() external payable { - address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F; - address nftCollection = CollectionHelpers(collectionHelpers).createNFTCollection{value: msg.value}("A", "B", "C"); - emit CollectionCreated(nftCollection); - } - - function mintNftToken(address collectionAddress) external { - UniqueNFT collection = UniqueNFT(collectionAddress); - uint256 tokenId = collection.mint(msg.sender); - emit TokenMinted(tokenId); - } - - function getValue() external view returns (bool) { - return InnerContract(innerContract).getValue(); - } - } - - contract InnerContract { - bool value = false; - function flip() external { - value = !value; - } - function getValue() external view returns (bool) { - return value; - } - } - `, - [ - { - solPath: 'api/CollectionHelpers.sol', - fsPath: `${dirname}/api/CollectionHelpers.sol`, - }, - { - solPath: 'api/UniqueNFT.sol', - fsPath: `${dirname}/api/UniqueNFT.sol`, - }, - ], - ); - } -}); --- a/js-packages/tests/src/eth/precompile.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; - -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; - -describe('Precompiles', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('ecrecover is supported', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const ecrecoverCompiledСontract = await helper.ethContract.compile( - 'ECRECOVER', - ` - // SPDX-License-Identifier: MIT - pragma solidity ^0.8.17; - - contract ECRECOVER{ - address addressTest = 0x12Cb274aAD8251C875c0bf6872b67d9983E53fDd; - bytes32 msgHash1 = 0xc51dac836bc7841a01c4b631fa620904fc8724d7f9f1d3c420f0e02adf229d50; - bytes32 msgHash2 = 0xc51dac836bc7841a01c4b631fa620904fc8724d7f9f1d3c420f0e02adf229d51; - uint8 v = 0x1b; - bytes32 r = 0x44287513919034a471a7dc2b2ed121f95984ae23b20f9637ba8dff471b6719ef; - bytes32 s = 0x7d7dc30309a3baffbfd9342b97d0e804092c0aeb5821319aa732bc09146eafb4; - - - function verifyValid() public view returns(bool) { - // Use ECRECOVER to verify address - return ecrecover(msgHash1, v, r, s) == (addressTest); - } - - function verifyInvalid() public view returns(bool) { - // Use ECRECOVER to verify address - return ecrecover(msgHash2, v, r, s) == (addressTest); - } - } - `, - ); - - const ecrecoverСontract = await helper.ethContract.deployByAbi(owner, ecrecoverCompiledСontract.abi, ecrecoverCompiledСontract.object); - expect(await ecrecoverСontract.methods.verifyValid().call({from: owner})).to.be.true; - expect(await ecrecoverСontract.methods.verifyInvalid().call({from: owner})).to.be.false; - }); - - itEth('sr25519 is supported', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sr25519CompiledСontract = await helper.ethContract.compile( - 'SR25519Contract', - ` - // SPDX-License-Identifier: MIT - pragma solidity ^0.8.17; - - /** - * @title SR25519 signature interface. - */ - interface SR25519 { - /** - * @dev Verify signed message using SR25519 crypto. - * @return A boolean confirming whether the public key is signer for the message. - */ - function verify( - bytes32 public_key, - bytes calldata signature, - bytes calldata message - ) external view returns (bool); - } - - contract SR25519Contract{ - SR25519 public constant sr25519 = SR25519(0x0000000000000000000000000000000000005002); - - bytes32 public_key = 0x96b2f9237ed0890fbeed891ebb81b91ac0d5d5b6e3afcdbc95df1b68d9f14036; - bytes signature = hex"4a5d733d7c568f2e88abf0467fd497f87c1be3e940ed897efdf9da72eaad394ef9918cb574ee99c54485775b17a0deaf46ff7a1f10346cea39fff0e4ede97689"; - bytes message1 = hex"7372323535313920697320737570706f72746564"; - bytes message2 = hex"7372323535313920697320737570706f7274656401"; - - - function verifyValid() public view returns(bool) { - return sr25519.verify(public_key, signature, message1); - } - - function verifyInvalid() public view returns(bool) { - return sr25519.verify(public_key, signature, message2); - } - } - `, - ); - - const sr25519Сontract = await helper.ethContract.deployByAbi(owner, sr25519CompiledСontract.abi, sr25519CompiledСontract.object); - expect(await sr25519Сontract.methods.verifyValid().call({from: owner})).to.be.true; - expect(await sr25519Сontract.methods.verifyInvalid().call({from: owner})).to.be.false; - }); -}); --- a/js-packages/tests/src/eth/proxy/UniqueFungibleProxy.abi +++ /dev/null @@ -1 +0,0 @@ -[{"inputs":[{"internalType":"address","name":"_proxied","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"interfaceId","type":"uint32"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file --- a/js-packages/tests/src/eth/proxy/UniqueFungibleProxy.bin +++ /dev/null @@ -1 +0,0 @@ -608060405234801561001057600080fd5b5060405161098038038061098083398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b6108ed806100936000396000f3fe608060405234801561001057600080fd5b506004361061009e5760003560e01c806370a082311161006657806370a082311461012757806395d89b411461013a578063a9059cbb14610142578063dd62ed3e14610155578063f4f4b5001461016857600080fd5b806306fdde03146100a3578063095ea7b3146100c157806318160ddd146100e457806323b872dd146100fa578063313ce5671461010d575b600080fd5b6100ab61017b565b6040516100b8919061083e565b60405180910390f35b6100d46100cf3660046106e3565b610200565b60405190151581526020016100b8565b6100ec61028f565b6040519081526020016100b8565b6100d46101083660046106a7565b610316565b6101156103ad565b60405160ff90911681526020016100b8565b6100ec610135366004610659565b610434565b6100ab6104b8565b6100d46101503660046106e3565b6104fc565b6100ec610163366004610674565b610536565b6100d46101763660046107f5565b6105bc565b60008054604080516306fdde0360e01b815290516060936001600160a01b03909316926306fdde039260048082019391829003018186803b1580156101bf57600080fd5b505afa1580156101d3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526101fb919081019061072f565b905090565b6000805460405163095ea7b360e01b81526001600160a01b038581166004830152602482018590529091169063095ea7b3906044015b602060405180830381600087803b15801561025057600080fd5b505af1158015610264573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610288919061070d565b9392505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156102de57600080fd5b505afa1580156102f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fb91906107dc565b600080546040516323b872dd60e01b81526001600160a01b038681166004830152858116602483015260448201859052909116906323b872dd90606401602060405180830381600087803b15801561036d57600080fd5b505af1158015610381573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a5919061070d565b949350505050565b60008060009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156103fc57600080fd5b505afa158015610410573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fb919061081b565b600080546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a082319060240160206040518083038186803b15801561047a57600080fd5b505afa15801561048e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b291906107dc565b92915050565b60008054604080516395d89b4160e01b815290516060936001600160a01b03909316926395d89b419260048082019391829003018186803b1580156101bf57600080fd5b6000805460405163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529091169063a9059cbb90604401610236565b60008054604051636eb1769f60e11b81526001600160a01b03858116600483015284811660248301529091169063dd62ed3e9060440160206040518083038186803b15801561058457600080fd5b505afa158015610598573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061028891906107dc565b6000805460405162f4f4b560e81b815263ffffffff841660048201526001600160a01b039091169063f4f4b5009060240160206040518083038186803b15801561060557600080fd5b505afa158015610619573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b2919061070d565b80356001600160a01b038116811461065457600080fd5b919050565b60006020828403121561066b57600080fd5b6102888261063d565b6000806040838503121561068757600080fd5b6106908361063d565b915061069e6020840161063d565b90509250929050565b6000806000606084860312156106bc57600080fd5b6106c58461063d565b92506106d36020850161063d565b9150604084013590509250925092565b600080604083850312156106f657600080fd5b6106ff8361063d565b946020939093013593505050565b60006020828403121561071f57600080fd5b8151801515811461028857600080fd5b60006020828403121561074157600080fd5b815167ffffffffffffffff8082111561075957600080fd5b818401915084601f83011261076d57600080fd5b81518181111561077f5761077f6108a1565b604051601f8201601f19908116603f011681019083821181831017156107a7576107a76108a1565b816040528281528760208487010111156107c057600080fd5b6107d1836020830160208801610871565b979650505050505050565b6000602082840312156107ee57600080fd5b5051919050565b60006020828403121561080757600080fd5b813563ffffffff8116811461028857600080fd5b60006020828403121561082d57600080fd5b815160ff8116811461028857600080fd5b602081526000825180602084015261085d816040850160208701610871565b601f01601f19169190910160400192915050565b60005b8381101561088c578181015183820152602001610874565b8381111561089b576000848401525b50505050565b634e487b7160e01b600052604160045260246000fdfea2646970667358221220c05e4cb7ab439b86c0b6ac8a84eec6a230b592fa63d1ab2e3a7f1474c27338f364736f6c63430008070033 \ No newline at end of file --- a/js-packages/tests/src/eth/proxy/UniqueFungibleProxy.sol +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: OTHER - -pragma solidity >=0.8.0 <0.9.0; - -import "../api/UniqueFungible.sol"; - -contract UniqueFungibleProxy is UniqueFungible { - UniqueFungible proxied; - - constructor(address _proxied) UniqueFungible() { - proxied = UniqueFungible(_proxied); - } - - function name() external view override returns (string memory) { - return proxied.name(); - } - - function symbol() external view override returns (string memory) { - return proxied.symbol(); - } - - function totalSupply() external view override returns (uint256) { - return proxied.totalSupply(); - } - - function supportsInterface(uint32 interfaceId) - external - view - override - returns (bool) - { - return proxied.supportsInterface(interfaceId); - } - - function decimals() external view override returns (uint8) { - return proxied.decimals(); - } - - function balanceOf(address owner) external view override returns (uint256) { - return proxied.balanceOf(owner); - } - - function transfer(address to, uint256 amount) - external - override - returns (bool) - { - return proxied.transfer(to, amount); - } - - function transferFrom( - address from, - address to, - uint256 amount - ) external override returns (bool) { - return proxied.transferFrom(from, to, amount); - } - - function approve(address spender, uint256 amount) - external - override - returns (bool) - { - return proxied.approve(spender, amount); - } - - function allowance(address owner, address spender) - external - view - override - returns (uint256) - { - return proxied.allowance(owner, spender); - } -} --- a/js-packages/tests/src/eth/proxy/UniqueNFTProxy.abi +++ /dev/null @@ -1 +0,0 @@ -[{"inputs":[{"internalType":"address","name":"_proxied","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[],"name":"MintingFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"approved","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"}],"name":"deleteProperty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finishMinting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"mintBulk","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"uint256","name":"field_0","type":"uint256"},{"internalType":"string","name":"field_1","type":"string"}],"internalType":"struct Tuple0[]","name":"tokens","type":"tuple[]"}],"name":"mintBulkWithTokenURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"tokenUri","type":"string"}],"name":"mintWithTokenURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintingFinished","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFromWithData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string","name":"value","type":"string"}],"name":"setProperty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file --- a/js-packages/tests/src/eth/proxy/UniqueNFTProxy.bin +++ /dev/null @@ -1 +0,0 @@ -608060405234801561001057600080fd5b506040516116bb3803806116bb83398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b611628806100936000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80634f6ccce7116100f957806379cc679011610097578063a22cb46511610071578063a22cb46514610399578063a9059cbb146103ac578063c87b56dd146103bf578063e985e9c5146103d257600080fd5b806379cc6790146103765780637d64bcb41461038957806395d89b411461039157600080fd5b806362d9491f116100d357806362d9491f146103355780636352211e1461034857806370a082311461035b57806375794a3c1461036e57600080fd5b80634f6ccce7146102fc57806350bb4e7f1461030f57806360a116721461032257600080fd5b80632f745c591161016657806340c10f191161014057806340c10f19146102b057806342842e0e146102c357806342966c68146102d657806344a9945e146102e957600080fd5b80632f745c5914610277578063342419141461028a578063365430061461029d57600080fd5b8063081812fc116101a2578063081812fc1461020e578063095ea7b31461023957806318160ddd1461024e57806323b872dd1461026457600080fd5b806301ffc9a7146101c957806305d2035b146101f157806306fdde03146101f9575b600080fd5b6101dc6101d7366004610dca565b6103e5565b60405190151581526020015b60405180910390f35b6101dc610462565b6102016104df565b6040516101e89190610e4c565b61022161021c366004610e5f565b610550565b6040516001600160a01b0390911681526020016101e8565b61024c610247366004610e90565b6105bf565b005b61025661062a565b6040519081526020016101e8565b61024c610272366004610ebc565b6106a2565b610256610285366004610e90565b610716565b61024c610298366004610ff3565b610793565b6101dc6102ab36600461104c565b6107f8565b6101dc6102be366004610e90565b61086e565b61024c6102d1366004610ebc565b6108a8565b61024c6102e4366004610e5f565b6108e9565b6101dc6102f736600461114f565b61091a565b61025661030a366004610e5f565b61094d565b6101dc61031d3660046111f5565b6109bc565b61024c61033036600461124e565b610a3c565b61024c6103433660046112ce565b610aab565b610221610356366004610e5f565b610add565b610256610369366004611332565b610b0f565b610256610b42565b61024c610384366004610e90565b610b96565b6101dc610bcf565b610201610c25565b61024c6103a736600461135d565b610c6e565b61024c6103ba366004610e90565b610ca8565b6102016103cd366004610e5f565b610ce1565b6102216103e0366004611396565b610d53565b600080546040516301ffc9a760e01b81526001600160e01b0319841660048201526001600160a01b03909116906301ffc9a790602401602060405180830381865afa158015610438573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c91906113c4565b92915050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166305d2035b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104da91906113c4565b905090565b60008054604080516306fdde0360e01b815290516060936001600160a01b03909316926306fdde0392600480820193918290030181865afa158015610528573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104da91908101906113e1565b6000805460405163020604bf60e21b8152600481018490526001600160a01b039091169063081812fc906024015b602060405180830381865afa15801561059b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c9190611458565b60005460405163095ea7b360e01b81526001600160a01b038481166004830152602482018490529091169063095ea7b3906044015b600060405180830381600087803b15801561060e57600080fd5b505af1158015610622573d6000803e3d6000fd5b505050505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561067e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104da9190611475565b6000546040516323b872dd60e01b81526001600160a01b038581166004830152848116602483015260448201849052909116906323b872dd906064015b600060405180830381600087803b1580156106f957600080fd5b505af115801561070d573d6000803e3d6000fd5b50505050505050565b60008054604051632f745c5960e01b81526001600160a01b0385811660048301526024820185905290911690632f745c5990604401602060405180830381865afa158015610768573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c9190611475565b9392505050565b600054604051630d09064560e21b81526001600160a01b03909116906334241914906107c3908490600401610e4c565b600060405180830381600087803b1580156107dd57600080fd5b505af11580156107f1573d6000803e3d6000fd5b5050505050565b60008054604051631b2a180360e11b81526001600160a01b039091169063365430069061082b908690869060040161148e565b6020604051808303816000875af115801561084a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c91906113c4565b600080546040516340c10f1960e01b81526001600160a01b03858116600483015260248201859052909116906340c10f199060440161082b565b600054604051632142170760e11b81526001600160a01b038581166004830152848116602483015260448201849052909116906342842e0e906064016106df565b600054604051630852cd8d60e31b8152600481018390526001600160a01b03909116906342966c68906024016107c3565b60008054604051632254ca2f60e11b81526001600160a01b03909116906344a9945e9061082b9086908690600401611513565b60008054604051634f6ccce760e01b8152600481018490526001600160a01b0390911690634f6ccce7906024015b602060405180830381865afa158015610998573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c9190611475565b600080546040516350bb4e7f60e01b81526001600160a01b03909116906350bb4e7f906109f190879087908790600401611569565b6020604051808303816000875af1158015610a10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3491906113c4565b949350505050565b6000546040516330508b3960e11b81526001600160a01b03909116906360a1167290610a72908790879087908790600401611590565b600060405180830381600087803b158015610a8c57600080fd5b505af1158015610aa0573d6000803e3d6000fd5b505050505b50505050565b6000546040516362d9491f60e01b81526001600160a01b03909116906362d9491f906105f490859085906004016115cd565b600080546040516331a9108f60e11b8152600481018490526001600160a01b0390911690636352211e9060240161057e565b600080546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a082319060240161097b565b60008060009054906101000a90046001600160a01b03166001600160a01b03166375794a3c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561067e573d6000803e3d6000fd5b60005460405163079cc67960e41b81526001600160a01b03848116600483015260248201849052909116906379cc6790906044016105f4565b60008060009054906101000a90046001600160a01b03166001600160a01b0316637d64bcb46040518163ffffffff1660e01b81526004016020604051808303816000875af11580156104b6573d6000803e3d6000fd5b60008054604080516395d89b4160e01b815290516060936001600160a01b03909316926395d89b4192600480820193918290030181865afa158015610528573d6000803e3d6000fd5b60005460405163a22cb46560e01b81526001600160a01b03848116600483015283151560248301529091169063a22cb465906044016105f4565b60005460405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490529091169063a9059cbb906044016105f4565b60005460405163c87b56dd60e01b8152600481018390526060916001600160a01b03169063c87b56dd90602401600060405180830381865afa158015610d2b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261045c91908101906113e1565b6000805460405163e985e9c560e01b81526001600160a01b03858116600483015284811660248301529091169063e985e9c590604401602060405180830381865afa158015610da6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c9190611458565b600060208284031215610ddc57600080fd5b81356001600160e01b03198116811461078c57600080fd5b60005b83811015610e0f578181015183820152602001610df7565b83811115610aa55750506000910152565b60008151808452610e38816020860160208601610df4565b601f01601f19169290920160200192915050565b60208152600061078c6020830184610e20565b600060208284031215610e7157600080fd5b5035919050565b6001600160a01b0381168114610e8d57600080fd5b50565b60008060408385031215610ea357600080fd5b8235610eae81610e78565b946020939093013593505050565b600080600060608486031215610ed157600080fd5b8335610edc81610e78565b92506020840135610eec81610e78565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610f3657610f36610efd565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610f6557610f65610efd565b604052919050565b600067ffffffffffffffff821115610f8757610f87610efd565b50601f01601f191660200190565b6000610fa8610fa384610f6d565b610f3c565b9050828152838383011115610fbc57600080fd5b828260208301376000602084830101529392505050565b600082601f830112610fe457600080fd5b61078c83833560208501610f95565b60006020828403121561100557600080fd5b813567ffffffffffffffff81111561101c57600080fd5b610a3484828501610fd3565b600067ffffffffffffffff82111561104257611042610efd565b5060051b60200190565b600080604080848603121561106057600080fd5b833561106b81610e78565b925060208481013567ffffffffffffffff8082111561108957600080fd5b818701915087601f83011261109d57600080fd5b81356110ab610fa382611028565b81815260059190911b8301840190848101908a8311156110ca57600080fd5b8585015b8381101561113d578035858111156110e65760008081fd5b8601808d03601f19018913156110fc5760008081fd5b611104610f13565b888201358152898201358781111561111c5760008081fd5b61112a8f8b83860101610fd3565b828b0152508452509186019186016110ce565b50809750505050505050509250929050565b6000806040838503121561116257600080fd5b823561116d81610e78565b915060208381013567ffffffffffffffff81111561118a57600080fd5b8401601f8101861361119b57600080fd5b80356111a9610fa382611028565b81815260059190911b820183019083810190888311156111c857600080fd5b928401925b828410156111e6578335825292840192908401906111cd565b80955050505050509250929050565b60008060006060848603121561120a57600080fd5b833561121581610e78565b925060208401359150604084013567ffffffffffffffff81111561123857600080fd5b61124486828701610fd3565b9150509250925092565b6000806000806080858703121561126457600080fd5b843561126f81610e78565b9350602085013561127f81610e78565b925060408501359150606085013567ffffffffffffffff8111156112a257600080fd5b8501601f810187136112b357600080fd5b6112c287823560208401610f95565b91505092959194509250565b600080604083850312156112e157600080fd5b823567ffffffffffffffff808211156112f957600080fd5b61130586838701610fd3565b9350602085013591508082111561131b57600080fd5b5061132885828601610fd3565b9150509250929050565b60006020828403121561134457600080fd5b813561078c81610e78565b8015158114610e8d57600080fd5b6000806040838503121561137057600080fd5b823561137b81610e78565b9150602083013561138b8161134f565b809150509250929050565b600080604083850312156113a957600080fd5b82356113b481610e78565b9150602083013561138b81610e78565b6000602082840312156113d657600080fd5b815161078c8161134f565b6000602082840312156113f357600080fd5b815167ffffffffffffffff81111561140a57600080fd5b8201601f8101841361141b57600080fd5b8051611429610fa382610f6d565b81815285602083850101111561143e57600080fd5b61144f826020830160208601610df4565b95945050505050565b60006020828403121561146a57600080fd5b815161078c81610e78565b60006020828403121561148757600080fd5b5051919050565b6001600160a01b0383168152604060208083018290528351828401819052600092916060600583901b860181019290860190878301865b8281101561150457888603605f190184528151805187528501518587018890526114f188880182610e20565b96505092840192908401906001016114c5565b50939998505050505050505050565b6001600160a01b038316815260406020808301829052835191830182905260009184820191906060850190845b8181101561155c57845183529383019391830191600101611540565b5090979650505050505050565b60018060a01b038416815282602082015260606040820152600061144f6060830184610e20565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906115c390830184610e20565b9695505050505050565b6040815260006115e06040830185610e20565b828103602084015261144f8185610e2056fea26469706673582212205bdf4275f4b714a1029ccfe77c0f9a0e20e121a932e1e5840cace97dfa886da964736f6c634300080d0033 \ No newline at end of file --- a/js-packages/tests/src/eth/proxy/UniqueNFTProxy.sol +++ /dev/null @@ -1,186 +0,0 @@ -// SPDX-License-Identifier: OTHER - -pragma solidity >=0.8.0 <0.9.0; - -import "../api/UniqueNFT.sol"; - -contract UniqueNFTProxy is UniqueNFT { - UniqueNFT proxied; - - constructor(address _proxied) UniqueNFT() { - proxied = UniqueNFT(_proxied); - } - - function name() external view override returns (string memory) { - return proxied.name(); - } - - function symbol() external view override returns (string memory) { - return proxied.symbol(); - } - - function totalSupply() external view override returns (uint256) { - return proxied.totalSupply(); - } - - function balanceOf(address owner) external view override returns (uint256) { - return proxied.balanceOf(owner); - } - - function ownerOf(uint256 tokenId) external view override returns (address) { - return proxied.ownerOf(tokenId); - } - - function safeTransferFromWithData( - address from, - address to, - uint256 tokenId, - bytes memory data - ) external override { - return proxied.safeTransferFromWithData(from, to, tokenId, data); - } - - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) external override { - return proxied.safeTransferFrom(from, to, tokenId); - } - - function transferFrom( - address from, - address to, - uint256 tokenId - ) external override { - return proxied.transferFrom(from, to, tokenId); - } - - function approve(address approved, uint256 tokenId) external override { - return proxied.approve(approved, tokenId); - } - - function setApprovalForAll(address operator, bool approved) - external - override - { - return proxied.setApprovalForAll(operator, approved); - } - - function getApproved(uint256 tokenId) - external - view - override - returns (address) - { - return proxied.getApproved(tokenId); - } - - function isApprovedForAll(address owner, address operator) - external - view - override - returns (address) - { - return proxied.isApprovedForAll(owner, operator); - } - - function burn(uint256 tokenId) external override { - return proxied.burn(tokenId); - } - - function tokenByIndex(uint256 index) - external - view - override - returns (uint256) - { - return proxied.tokenByIndex(index); - } - - function tokenOfOwnerByIndex(address owner, uint256 index) - external - view - override - returns (uint256) - { - return proxied.tokenOfOwnerByIndex(owner, index); - } - - function tokenURI(uint256 tokenId) - external - view - override - returns (string memory) - { - return proxied.tokenURI(tokenId); - } - - function mintingFinished() external view override returns (bool) { - return proxied.mintingFinished(); - } - - function mint(address to) - external - override - returns (uint256) - { - return proxied.mint(to); - } - - function mintWithTokenURI( - address to, - string memory tokenUri - ) external override returns (uint256) { - return proxied.mintWithTokenURI(to, tokenUri); - } - - function finishMinting() external override returns (bool) { - return proxied.finishMinting(); - } - - function transfer(address to, uint256 tokenId) external override { - return proxied.transfer(to, tokenId); - } - - function nextTokenId() external view override returns (uint256) { - return proxied.nextTokenId(); - } - - function burnFrom(address from, uint256 tokenId) external override { - return proxied.burnFrom(from, tokenId); - } - - function supportsInterface(bytes4 interfaceId) - external - view - override - returns (bool) - { - return proxied.supportsInterface(interfaceId); - } - - function mintBulk(address to, uint256[] memory tokenIds) - external - override - returns (bool) - { - return proxied.mintBulk(to, tokenIds); - } - - function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) - external - override - returns (bool) - { - return proxied.mintBulkWithTokenURI(to, tokens); - } - - function setProperty(string memory key, string memory value) external override { - return proxied.setProperty(key, value); - } - - function deleteProperty(string memory key) external override { - return proxied.deleteProperty(key); - } -} --- a/js-packages/tests/src/eth/proxy/fungibleProxy.test.ts +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {expect} from 'chai'; -import {readFile} from 'fs/promises'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {itEth, usingEthPlaygrounds} from '../util/index.js'; -import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js'; -import {makeNames} from '../../util/index.js'; - -const {dirname} = makeNames(import.meta.url); - -async function proxyWrap(helper: EthUniqueHelper, wrapped: any, donor: IKeyringPair) { - // Proxy owner has no special privilegies, we don't need to reuse them - const owner = await helper.eth.createAccountWithBalance(donor); - const web3 = helper.getWeb3(); - const proxyContract = new web3.eth.Contract(JSON.parse((await readFile(`${dirname}/UniqueFungibleProxy.abi`)).toString()), undefined, { - from: owner, - gas: helper.eth.DEFAULT_GAS, - }); - const proxy = await proxyContract.deploy({data: (await readFile(`${dirname}/UniqueFungibleProxy.bin`)).toString(), arguments: [wrapped.options.address]}).send({from: owner}); - return proxy; -} - -describe('Fungible (Via EVM proxy): Information getting', () => { - let alice: IKeyringPair; - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - - itEth('totalSupply', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); - const caller = await helper.eth.createAccountWithBalance(donor); - await collection.mint(alice, 200n, {Substrate: alice.address}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - const totalSupply = await contract.methods.totalSupply().call(); - - expect(totalSupply).to.equal('200'); - }); - - itEth('balanceOf', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); - const caller = await helper.eth.createAccountWithBalance(donor); - - await collection.mint(alice, 200n, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - const balance = await contract.methods.balanceOf(caller).call(); - - expect(balance).to.equal('200'); - }); -}); - -describe('Fungible (Via EVM proxy): Plain calls', () => { - let alice: IKeyringPair; - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - - itEth('Can perform approve()', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); - const caller = await helper.eth.createAccountWithBalance(donor); - const spender = helper.eth.createAccount(); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - await collection.mint(alice, 200n, {Ethereum: contract.options.address}); - - { - const result = await contract.methods.approve(spender, 100).send({from: caller}); - const events = helper.eth.normalizeEvents(result.events); - - expect(events).to.be.deep.equal([ - { - address, - event: 'Approval', - args: { - owner: contract.options.address, - spender, - value: '100', - }, - }, - ]); - } - - { - const allowance = await contract.methods.allowance(contract.options.address, spender).call(); - expect(+allowance).to.equal(100); - } - }); - - itEth('Can perform transferFrom()', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); - const caller = await helper.eth.createAccountWithBalance(donor); - const owner = await helper.eth.createAccountWithBalance(donor); - - await collection.mint(alice, 200n, {Ethereum: owner}); - const receiver = helper.eth.createAccount(); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - - await evmCollection.methods.approve(contract.options.address, 100).send({from: owner}); - - { - const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: caller}); - const events = helper.eth.normalizeEvents(result.events); - expect(events).to.be.deep.equal([ - { - address, - event: 'Transfer', - args: { - from: owner, - to: receiver, - value: '49', - }, - }, - { - address, - event: 'Approval', - args: { - owner, - spender: contract.options.address, - value: '51', - }, - }, - ]); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(49); - } - - { - const balance = await contract.methods.balanceOf(owner).call(); - expect(+balance).to.equal(151); - } - }); - - itEth('Can perform transfer()', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0); - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - await collection.mint(alice, 200n, {Ethereum: contract.options.address}); - - { - const result = await contract.methods.transfer(receiver, 50).send({from: caller}); - const events = helper.eth.normalizeEvents(result.events); - expect(events).to.be.deep.equal([ - { - address, - event: 'Transfer', - args: { - from: contract.options.address, - to: receiver, - value: '50', - }, - }, - ]); - } - - { - const balance = await contract.methods.balanceOf(contract.options.address).call(); - expect(+balance).to.equal(150); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(50); - } - }); -}); --- a/js-packages/tests/src/eth/proxy/nonFungibleProxy.test.ts +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {readFile} from 'fs/promises'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {itEth, usingEthPlaygrounds, expect} from '../util/index.js'; -import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js'; -import {makeNames} from '../../util/index.js'; - -const {dirname} = makeNames(import.meta.url); - - -async function proxyWrap(helper: EthUniqueHelper, wrapped: any, donor: IKeyringPair) { - // Proxy owner has no special privilegies, we don't need to reuse them - const owner = await helper.eth.createAccountWithBalance(donor); - const web3 = helper.getWeb3(); - const proxyContract = new web3.eth.Contract(JSON.parse((await readFile(`${dirname}/UniqueNFTProxy.abi`)).toString()), undefined, { - from: owner, - gas: helper.eth.DEFAULT_GAS, - }); - const proxy = await proxyContract.deploy({data: (await readFile(`${dirname}/UniqueNFTProxy.bin`)).toString(), arguments: [wrapped.options.address]}).send({from: owner}); - return proxy; -} - -describe('NFT (Via EVM proxy): Information getting', () => { - let alice: IKeyringPair; - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - - itEth('totalSupply', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const caller = await helper.eth.createAccountWithBalance(donor); - await collection.mintToken(alice, {Substrate: alice.address}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - const totalSupply = await contract.methods.totalSupply().call(); - - expect(totalSupply).to.equal('1'); - }); - - itEth('balanceOf', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - const caller = await helper.eth.createAccountWithBalance(donor); - await collection.mintMultipleTokens(alice, [ - {owner: {Ethereum: caller}}, - {owner: {Ethereum: caller}}, - {owner: {Ethereum: caller}}, - ]); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - const balance = await contract.methods.balanceOf(caller).call(); - - expect(balance).to.equal('3'); - }); - - itEth('ownerOf', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - const caller = await helper.eth.createAccountWithBalance(donor); - const {tokenId} = await collection.mintToken(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - const owner = await contract.methods.ownerOf(tokenId).call(); - - expect(owner).to.equal(caller); - }); -}); - -describe('NFT (Via EVM proxy): Plain calls', () => { - let alice: IKeyringPair; - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - - // Soft-deprecated - itEth('[eth] Can perform mint()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'A', 'A', ''); - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const collectionEvmOwned = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller, true); - const contract = await proxyWrap(helper, collectionEvm, donor); - await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send(); - - { - const nextTokenId = await contract.methods.nextTokenId().call(); - const result = await contract.methods.mintWithTokenURI(receiver, nextTokenId, 'Test URI').send({from: caller}); - const tokenId = result.events.Transfer.returnValues.tokenId; - expect(tokenId).to.be.equal('1'); - - const event = helper.eth.normalizeEvents(result.events) - .find(event => event.event === 'Transfer')!; - event.address = event.address.toLocaleLowerCase(); - - expect(event).to.be.deep.equal({ - address: collectionAddress.toLocaleLowerCase(), - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: receiver, - tokenId, - }, - }); - - expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); - } - }); - - itEth('[cross] Can perform mint()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'A', 'A', ''); - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const collectionEvmOwned = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller); - const contract = await proxyWrap(helper, collectionEvm, donor); - const contractAddressCross = helper.ethCrossAccount.fromAddress(contract.options.address); - await collectionEvmOwned.methods.addCollectionAdminCross(contractAddressCross).send(); - - { - const nextTokenId = await contract.methods.nextTokenId().call(); - const result = await contract.methods.mintWithTokenURI(receiver, nextTokenId, 'Test URI').send({from: caller}); - const tokenId = result.events.Transfer.returnValues.tokenId; - expect(tokenId).to.be.equal('1'); - - const event = helper.eth.normalizeEvents(result.events) - .find(event => event.event === 'Transfer')!; - event.address = event.address.toLocaleLowerCase(); - - expect(event).to.be.deep.equal({ - address: collectionAddress.toLocaleLowerCase(), - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: receiver, - tokenId, - }, - }); - - expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); - } - }); - - //TODO: CORE-302 add eth methods - itEth.skip('Can perform mintBulk()', async ({helper}) => { - const collection = await helper.nft.mintCollection(donor, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); - - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - await collection.addAdmin(donor, {Ethereum: contract.options.address}); - - { - const nextTokenId = await contract.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - const result = await contract.methods.mintBulkWithTokenURI( - receiver, - [ - [nextTokenId, 'Test URI 0'], - [+nextTokenId + 1, 'Test URI 1'], - [+nextTokenId + 2, 'Test URI 2'], - ], - ).send({from: caller}); - const events = helper.eth.normalizeEvents(result.events); - - expect(events).to.be.deep.equal([ - { - address, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: receiver, - tokenId: nextTokenId, - }, - }, - { - address, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: receiver, - tokenId: String(+nextTokenId + 1), - }, - }, - { - address, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: receiver, - tokenId: String(+nextTokenId + 2), - }, - }, - ]); - - expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0'); - expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1'); - expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2'); - } - }); - - itEth('Can perform burn()', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const caller = await helper.eth.createAccountWithBalance(donor); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address}); - await collection.addAdmin(alice, {Ethereum: contract.options.address}); - - { - const result = await contract.methods.burn(tokenId).send({from: caller}); - const events = helper.eth.normalizeEvents(result.events); - - expect(events).to.be.deep.equal([ - { - address, - event: 'Transfer', - args: { - from: contract.options.address, - to: '0x0000000000000000000000000000000000000000', - tokenId: tokenId.toString(), - }, - }, - ]); - } - }); - - itEth('Can perform approve()', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const caller = await helper.eth.createAccountWithBalance(donor); - const spender = helper.eth.createAccount(); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address}); - - { - const result = await contract.methods.approve(spender, tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}); - const events = helper.eth.normalizeEvents(result.events); - - expect(events).to.be.deep.equal([ - { - address, - event: 'Approval', - args: { - owner: contract.options.address, - approved: spender, - tokenId: tokenId.toString(), - }, - }, - ]); - } - }); - - itEth('Can perform transferFrom()', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const caller = await helper.eth.createAccountWithBalance(donor); - const owner = await helper.eth.createAccountWithBalance(donor); - - const receiver = helper.eth.createAccount(); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - const {tokenId} = await collection.mintToken(alice, {Ethereum: owner}); - - await evmCollection.methods.approve(contract.options.address, tokenId).send({from: owner}); - - { - const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: caller}); - const events = helper.eth.normalizeEvents(result.events); - expect(events).to.be.deep.equal([ - { - address, - event: 'Transfer', - args: { - from: owner, - to: receiver, - tokenId: tokenId.toString(), - }, - }, - ]); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(1); - } - - { - const balance = await contract.methods.balanceOf(contract.options.address).call(); - expect(+balance).to.equal(0); - } - }); - - itEth('Can perform transfer()', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller); - const contract = await proxyWrap(helper, evmCollection, donor); - const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address}); - - { - const result = await contract.methods.transfer(receiver, tokenId).send({from: caller}); - const events = helper.eth.normalizeEvents(result.events); - expect(events).to.be.deep.equal([ - { - address, - event: 'Transfer', - args: { - from: contract.options.address, - to: receiver, - tokenId: tokenId.toString(), - }, - }, - ]); - } - - { - const balance = await contract.methods.balanceOf(contract.options.address).call(); - expect(+balance).to.equal(0); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(1); - } - }); -}); --- a/js-packages/tests/src/eth/proxyContract.test.ts +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; - -import {itEth, expect, usingEthPlaygrounds} from './util/index.js'; -import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; - -describe('EVM payable contracts', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Update proxy contract', async({helper}) => { - const deployer = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - const proxyContract = await deployProxyContract(helper, deployer); - - const realContractV1 = await deployRealContractV1(helper, deployer); - const realContractV1proxy = new helper.web3!.eth.Contract(realContractV1.options.jsonInterface, proxyContract.options.address, {from: caller, gas: helper.eth.DEFAULT_GAS}); - await proxyContract.methods.updateVersion(realContractV1.options.address).send(); - - await realContractV1proxy.methods.flip().send(); - await realContractV1proxy.methods.flip().send(); - await realContractV1proxy.methods.flip().send(); - const value1 = await realContractV1proxy.methods.getValue().call(); - const flipCount1 = await realContractV1proxy.methods.getFlipCount().call(); - expect(flipCount1).to.be.equal('3'); - expect(value1).to.be.equal(true); - - const realContractV2 = await deployRealContractV2(helper, deployer); - const realContractV2proxy = new helper.web3!.eth.Contract(realContractV2.options.jsonInterface, proxyContract.options.address, {from: caller, gas: helper.eth.DEFAULT_GAS}); - await proxyContract.methods.updateVersion(realContractV2.options.address).send(); - - await realContractV2proxy.methods.flip().send(); - await realContractV2proxy.methods.flip().send(); - await realContractV2proxy.methods.setStep(5).send(); - await realContractV2proxy.methods.increaseFlipCount().send(); - const value2 = await realContractV2proxy.methods.getValue().call(); - const flipCount2 = await realContractV2proxy.methods.getFlipCount().call(); - expect(value2).to.be.equal(true); - expect(flipCount2).to.be.equal('6'); - }); - - async function deployProxyContract(helper: EthUniqueHelper, deployer: string) { - return await helper.ethContract.deployByCode(deployer, 'ProxyContract', ` - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.6; - - contract ProxyContract { - event NewEvent(uint data); - receive() external payable {} - bytes32 private constant implementationSlot = bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1); - constructor() {} - function updateVersion(address newContractAddress) external { - bytes32 slot = implementationSlot; - assembly { - sstore(slot, newContractAddress) - } - } - fallback() external { - bytes32 slot = implementationSlot; - assembly { - let ptr := mload(0x40) - let contractAddress := sload(slot) - - calldatacopy(ptr, 0, calldatasize()) - - let result := delegatecall(gas(), contractAddress, ptr, calldatasize(), 0, 0) - let size := returndatasize() - - returndatacopy(ptr, 0, size) - - switch result - case 0 { revert(ptr, size) } - default { return(ptr, size) } - } - } - } - - interface RealContract { - function flip() external; - function getValue() external view returns (bool); - function getFlipCount() external view returns (uint); - }`); - } - - async function deployRealContractV1(helper: EthUniqueHelper, deployer: string) { - return await helper.ethContract.deployByCode(deployer, 'RealContractV1', ` - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.6; - - contract RealContractV1 { - bool value = false; - uint flipCount = 0; - function flip() external { - value = !value; - flipCount++; - } - function getValue() external view returns (bool) { - return value; - } - function getFlipCount() external view returns (uint) { - return flipCount; - } - }`); - } - - async function deployRealContractV2(helper: EthUniqueHelper, deployer: string) { - return await helper.ethContract.deployByCode(deployer, 'RealContractV2', ` - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.6; - - contract RealContractV2 { - bool value = false; - uint flipCount = 10; - uint step = 1; - function flip() external { - value = !value; - flipCount--; - } - function setStep(uint value) external { - step = value; - } - function increaseFlipCount() external { - flipCount = flipCount + step; - } - function getValue() external view returns (bool) { - return value; - } - function getFlipCount() external view returns (uint) { - return flipCount; - } - }`); - } -}); --- a/js-packages/tests/src/eth/reFungible.test.ts +++ /dev/null @@ -1,1019 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {Pallets, requirePalletsOrSkip} from '../util/index.js'; -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import type {ITokenPropertyPermission} from '@unique/playgrounds/src/types.js'; -import {CREATE_COLLECTION_DATA_DEFAULTS, TokenPermissionField} from './util/playgrounds/types.js'; - -describe('Refungible: Plain calls', () => { - let donor: IKeyringPair; - let minter: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - [ - 'substrate' as const, - 'ethereum' as const, - ].map(testCase => { - itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => { - const collectionAdmin = await helper.eth.createAccountWithBalance(donor); - - const receiverEth = helper.eth.createAccount(); - const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); - const receiverSub = bob; - const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub); - - const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)})); - const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: { - tokenOwner: false, - collectionAdmin: true, - mutable: false}})); - - - const collection = await helper.rft.mintCollection(minter, { - tokenPrefix: 'ethp', - tokenPropertyPermissions: permissions, - }); - await collection.addAdmin(minter, {Ethereum: collectionAdmin}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', collectionAdmin, true); - let expectedTokenId = await contract.methods.nextTokenId().call(); - let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send(); - let tokenId = result.events.Transfer.returnValues.tokenId; - expect(tokenId).to.be.equal(expectedTokenId); - - let event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address)); - expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); - - expectedTokenId = await contract.methods.nextTokenId().call(); - result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send(); - event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address)); - expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]); - - tokenId = result.events.Transfer.returnValues.tokenId; - - expect(tokenId).to.be.equal(expectedTokenId); - - expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties - .map(p => helper.ethProperty.property(p.key, p.value.toString()))); - - expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)) - .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address}); - }); - }); - - itEth.skip('Can perform mintBulk()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'MintBulky', '6', '6', ''); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - - { - const nextTokenId = await contract.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - const result = await contract.methods.mintBulkWithTokenURI( - receiver, - [ - [nextTokenId, 'Test URI 0'], - [+nextTokenId + 1, 'Test URI 1'], - [+nextTokenId + 2, 'Test URI 2'], - ], - ).send(); - - const events = result.events.Transfer; - for(let i = 0; i < 2; i++) { - const event = events[i]; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.equal(receiver); - expect(event.returnValues.tokenId).to.equal(String(+nextTokenId + i)); - } - - expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0'); - expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1'); - expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2'); - } - }); - - itEth('Can perform mintBulkCross() with multiple tokens', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const callerCross = helper.ethCrossAccount.fromAddress(caller); - const receiver = helper.eth.createAccount(); - const receiverCross = helper.ethCrossAccount.fromAddress(receiver); - - const permissions = [ - {code: TokenPermissionField.Mutable, value: true}, - {code: TokenPermissionField.TokenOwner, value: true}, - {code: TokenPermissionField.CollectionAdmin, value: true}, - ]; - const {collectionAddress} = await helper.eth.createCollection( - caller, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: 'rft', - adminList: [callerCross], - tokenPropertyPermissions: [ - {key: 'key_0_0', permissions}, - {key: 'key_1_0', permissions}, - {key: 'key_1_1', permissions}, - {key: 'key_2_0', permissions}, - {key: 'key_2_1', permissions}, - {key: 'key_2_2', permissions}, - ], - }, - ).send(); - - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - const nextTokenId = await contract.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - const result = await contract.methods.mintBulkCross([ - { - owners: [{ - owner: receiverCross, - pieces: 1, - }], - properties: [ - {key: 'key_0_0', value: Buffer.from('value_0_0')}, - ], - }, - { - owners: [{ - owner: receiverCross, - pieces: 2, - }], - properties: [ - {key: 'key_1_0', value: Buffer.from('value_1_0')}, - {key: 'key_1_1', value: Buffer.from('value_1_1')}, - ], - }, - { - owners: [{ - owner: receiverCross, - pieces: 1, - }], - properties: [ - {key: 'key_2_0', value: Buffer.from('value_2_0')}, - {key: 'key_2_1', value: Buffer.from('value_2_1')}, - {key: 'key_2_2', value: Buffer.from('value_2_2')}, - ], - }, - ]).send({from: caller}); - const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId); - const bulkSize = 3; - for(let i = 0; i < bulkSize; i++) { - const event = events[i]; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.equal(receiver); - expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`); - } - - const properties = [ - await contract.methods.properties(+nextTokenId, []).call(), - await contract.methods.properties(+nextTokenId + 1, []).call(), - await contract.methods.properties(+nextTokenId + 2, []).call(), - ]; - expect(properties).to.be.deep.equal([ - [ - ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')], - ], - [ - ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')], - ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')], - ], - [ - ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')], - ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')], - ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')], - ], - ]); - }); - - itEth('Can perform mintBulkCross() with multiple owners', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const callerCross = helper.ethCrossAccount.fromAddress(caller); - const receiver = helper.eth.createAccount(); - const receiverCross = helper.ethCrossAccount.fromAddress(receiver); - const receiver2 = helper.eth.createAccount(); - const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2); - - const permissions = [ - {code: TokenPermissionField.Mutable, value: true}, - {code: TokenPermissionField.TokenOwner, value: true}, - {code: TokenPermissionField.CollectionAdmin, value: true}, - ]; - const {collectionAddress} = await helper.eth.createCollection( - caller, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: 'rft', - adminList: [callerCross], - tokenPropertyPermissions: [ - {key: 'key_2_0', permissions}, - {key: 'key_2_1', permissions}, - {key: 'key_2_2', permissions}, - ], - }, - ).send(); - - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - const nextTokenId = await contract.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - const result = await contract.methods.mintBulkCross([{ - owners: [ - { - owner: receiverCross, - pieces: 1, - }, - { - owner: receiver2Cross, - pieces: 2, - }, - ], - properties: [ - {key: 'key_2_0', value: Buffer.from('value_2_0')}, - {key: 'key_2_1', value: Buffer.from('value_2_1')}, - {key: 'key_2_2', value: Buffer.from('value_2_2')}, - ], - }]).send({from: caller}); - const event = result.events.Transfer; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'); - expect(event.returnValues.tokenId).to.equal(`${+nextTokenId}`); - - const properties = [ - await contract.methods.properties(+nextTokenId, []).call(), - ]; - expect(properties).to.be.deep.equal([[ - ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')], - ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')], - ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')], - ]]); - }); - - itEth('Can perform setApprovalForAll()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const operator = helper.eth.createAccount(); - - const collection = await helper.rft.mintCollection(minter, {}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - - const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call(); - expect(approvedBefore).to.be.equal(false); - - { - const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner}); - - expect(result.events.ApprovalForAll).to.be.like({ - address: collectionAddress, - event: 'ApprovalForAll', - returnValues: { - owner, - operator, - approved: true, - }, - }); - - const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call(); - expect(approvedAfter).to.be.equal(true); - } - - { - const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner}); - - expect(result.events.ApprovalForAll).to.be.like({ - address: collectionAddress, - event: 'ApprovalForAll', - returnValues: { - owner, - operator, - approved: false, - }, - }); - - const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call(); - expect(approvedAfter).to.be.equal(false); - } - }); - - itEth('Can perform burn with ApprovalForAll', async ({helper}) => { - const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const owner = await helper.eth.createAccountWithBalance(donor); - const operator = await helper.eth.createAccountWithBalance(donor); - - const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'rft'); - - { - await contract.methods.setApprovalForAll(operator, true).send({from: owner}); - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator}); - const events = result.events.Transfer; - - expect(events).to.be.like({ - address, - event: 'Transfer', - returnValues: { - from: owner, - to: '0x0000000000000000000000000000000000000000', - tokenId: token.tokenId.toString(), - }, - }); - } - }); - - itEth('Can perform burn with approve and approvalForAll', async ({helper}) => { - const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const owner = await helper.eth.createAccountWithBalance(donor); - const operator = await helper.eth.createAccountWithBalance(donor); - - const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'rft'); - - const rftToken = await helper.ethNativeContract.rftTokenById(token.collectionId, token.tokenId, owner, true); - - { - await rftToken.methods.approve(operator, 15n).send({from: owner}); - await contract.methods.setApprovalForAll(operator, true).send({from: owner}); - await rftToken.methods.burnFrom(owner, 10n).send({from: operator}); - } - { - const allowance = await rftToken.methods.allowance(owner, operator).call(); - expect(+allowance).to.be.equal(5); - } - { - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const operatorCross = helper.ethCrossAccount.fromAddress(operator); - const allowance = await rftToken.methods.allowanceCross(ownerCross, operatorCross).call(); - expect(+allowance).to.equal(5); - } - }); - - itEth('Can perform transfer with ApprovalForAll', async ({helper}) => { - const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const owner = await helper.eth.createAccountWithBalance(donor); - const operator = await helper.eth.createAccountWithBalance(donor); - const receiver = charlie; - - const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'rft'); - - { - await contract.methods.setApprovalForAll(operator, true).send({from: owner}); - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); - const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator}); - const event = result.events.Transfer; - expect(event).to.be.like({ - address: helper.ethAddress.fromCollectionId(collection.collectionId), - event: 'Transfer', - returnValues: { - from: owner, - to: helper.address.substrateToEth(receiver.address), - tokenId: token.tokenId.toString(), - }, - }); - } - - expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]); - }); - - itEth('Can perform burn()', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Burny', '6', '6'); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - - const result = await contract.methods.mint(caller).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - { - const result = await contract.methods.burn(tokenId).send(); - const event = result.events.Transfer; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal(caller); - expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.tokenId).to.equal(tokenId.toString()); - } - }); - - itEth('Can perform transferFrom()', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'TransferFromy', '6', '6'); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - - const result = await contract.methods.mint(caller).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - - const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId); - - const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, caller); - await tokenContract.methods.repartition(15).send(); - - { - const tokenEvents: any = []; - tokenContract.events.allEvents((_: any, event: any) => { - tokenEvents.push(event); - }); - const result = await contract.methods.transferFrom(caller, receiver, tokenId).send(); - if(tokenEvents.length == 0) await helper.wait.newBlocks(1); - - let event = result.events.Transfer; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal(caller); - expect(event.returnValues.to).to.equal(receiver); - expect(event.returnValues.tokenId).to.equal(tokenId.toString()); - - event = tokenEvents[0]; - expect(event.address).to.equal(tokenAddress); - expect(event.returnValues.from).to.equal(caller); - expect(event.returnValues.to).to.equal(receiver); - expect(event.returnValues.value).to.equal('15'); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(1); - } - - { - const balance = await contract.methods.balanceOf(caller).call(); - expect(+balance).to.equal(0); - } - }); - - // Soft-deprecated - itEth('Can perform burnFrom()', async ({helper}) => { - const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = await helper.eth.createAccountWithBalance(donor); - - const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'rft', spender, true); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId); - const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - await tokenContract.methods.repartition(15).send(); - await tokenContract.methods.approve(spender, 15).send(); - - { - const result = await contract.methods.burnFrom(owner, token.tokenId).send(); - const event = result.events.Transfer; - expect(event).to.be.like({ - address: helper.ethAddress.fromCollectionId(collection.collectionId), - event: 'Transfer', - returnValues: { - from: owner, - to: '0x0000000000000000000000000000000000000000', - tokenId: token.tokenId.toString(), - }, - }); - } - - expect(await collection.getTokenBalance(token.tokenId, {Ethereum: owner})).to.be.eq(0n); - }); - - itEth('Can perform burnFromCross()', async ({helper}) => { - const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const owner = bob; - const spender = await helper.eth.createAccountWithBalance(donor); - - const token = await collection.mintToken(minter, 100n, {Substrate: owner.address}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'rft'); - - await token.repartition(owner, 15n); - await token.approve(owner, {Ethereum: spender}, 15n); - - { - const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner); - const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender}); - const event = result.events.Transfer; - expect(event).to.be.like({ - address: helper.ethAddress.fromCollectionId(collection.collectionId), - event: 'Transfer', - returnValues: { - from: helper.address.substrateToEth(owner.address), - to: '0x0000000000000000000000000000000000000000', - tokenId: token.tokenId.toString(), - }, - }); - } - - expect(await collection.getTokenBalance(token.tokenId, {Substrate: owner.address})).to.be.eq(0n); - }); - - itEth('Can perform transferFromCross()', async ({helper}) => { - const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const owner = bob; - const spender = await helper.eth.createAccountWithBalance(donor); - const receiver = charlie; - - const token = await collection.mintToken(minter, 100n, {Substrate: owner.address}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'rft'); - - await token.repartition(owner, 15n); - await token.approve(owner, {Ethereum: spender}, 15n); - - { - const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner); - const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); - const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender}); - const event = result.events.Transfer; - expect(event).to.be.like({ - address: helper.ethAddress.fromCollectionId(collection.collectionId), - event: 'Transfer', - returnValues: { - from: helper.address.substrateToEth(owner.address), - to: helper.address.substrateToEth(receiver.address), - tokenId: token.tokenId.toString(), - }, - }); - } - - expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]); - }); - - itEth('Can perform transfer()', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry', '6', '6'); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - - const result = await contract.methods.mint(caller).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - - { - const result = await contract.methods.transfer(receiver, tokenId).send(); - - const event = result.events.Transfer; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal(caller); - expect(event.returnValues.to).to.equal(receiver); - expect(event.returnValues.tokenId).to.equal(tokenId.toString()); - } - - { - const balance = await contract.methods.balanceOf(caller).call(); - expect(+balance).to.equal(0); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(1); - } - }); - - itEth('Can perform transferCross()', async ({helper}) => { - const sender = await helper.eth.createAccountWithBalance(donor); - const receiverEth = await helper.eth.createAccountWithBalance(donor); - const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); - const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter); - - const collection = await helper.rft.mintCollection(minter, {}); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', sender); - - const token = await collection.mintToken(minter, 50n, {Ethereum: sender}); - - { - // Can transferCross to ethereum address: - const result = await collectionEvm.methods.transferCross(receiverCrossEth, token.tokenId).send({from: sender}); - // Check events: - const event = result.events.Transfer; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal(sender); - expect(event.returnValues.to).to.equal(receiverEth); - expect(event.returnValues.tokenId).to.equal(token.tokenId.toString()); - // Sender's balance decreased: - const senderBalance = await collectionEvm.methods.balanceOf(sender).call(); - expect(+senderBalance).to.equal(0); - expect(await token.getBalance({Ethereum: sender})).to.eq(0n); - // Receiver's balance increased: - const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); - expect(+receiverBalance).to.equal(1); - expect(await token.getBalance({Ethereum: receiverEth})).to.eq(50n); - } - - { - // Can transferCross to substrate address: - const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, token.tokenId).send({from: receiverEth}); - // Check events: - const event = substrateResult.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(receiverEth); - expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address)); - expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`); - // Sender's balance decreased: - const senderBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); - expect(+senderBalance).to.equal(0); - expect(await token.getBalance({Ethereum: receiverEth})).to.eq(0n); - // Receiver's balance increased: - const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address}); - expect(receiverBalance).to.contain(token.tokenId); - expect(await token.getBalance({Substrate: minter.address})).to.eq(50n); - } - }); - - ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => { - const sender = await helper.eth.createAccountWithBalance(donor); - const tokenOwner = await helper.eth.createAccountWithBalance(donor); - const receiverSub = minter; - const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter); - - const collection = await helper.rft.mintCollection(minter, {}); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', sender); - - await collection.mintToken(minter, 50n, {Ethereum: sender}); - const nonSendersToken = await collection.mintToken(minter, 50n, {Ethereum: tokenOwner}); - - // Cannot transferCross someone else's token: - const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub; - await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected; - // Cannot transfer token if it does not exist: - await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected; - })); - - itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Partial-to-Full', '6', '6'); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - - const result = await contract.methods.mint(caller).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - - const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller); - - await tokenContract.methods.repartition(2).send(); - await tokenContract.methods.transfer(receiver, 1).send(); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - await tokenContract.methods.transfer(receiver, 1).send(); - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'); - expect(event.returnValues.to).to.equal(receiver); - expect(event.returnValues.tokenId).to.equal(tokenId.toString()); - }); - - itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Full-to-Partial', '6', '6'); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - - const result = await contract.methods.mint(caller).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - - const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller); - - await tokenContract.methods.repartition(2).send(); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - await tokenContract.methods.transfer(receiver, 1).send(); - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal(caller); - expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'); - expect(event.returnValues.tokenId).to.equal(tokenId.toString()); - }); - - itEth('Check balanceOfCross()', async ({helper}) => { - const collection = await helper.rft.mintCollection(minter, {}); - const owner = await helper.ethCrossAccount.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth); - - expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0'); - - for(let i = 1n; i < 10n; i++) { - await collection.mintToken(minter, 100n, {Ethereum: owner.eth}); - expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString()); - } - }); - - itEth('Check ownerOfCross()', async ({helper}) => { - const collection = await helper.rft.mintCollection(minter, {}); - let owner = await helper.ethCrossAccount.createAccountWithBalance(donor); - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth); - const {tokenId} = await collection.mintToken(minter, 100n,{Ethereum: owner.eth}); - - for(let i = 1n; i < 10n; i++) { - const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth}); - expect(ownerCross.eth).to.be.eq(owner.eth); - expect(ownerCross.sub).to.be.eq(owner.sub); - - const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor); - await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth}); - owner = newOwner; - } - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth, true); - const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor); - await tokenContract.methods.transferCross(newOwner, 50).send({from: owner.eth}); - const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth}); - expect(ownerCross.eth.toUpperCase()).to.be.eq('0XFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'); - expect(ownerCross.sub).to.be.eq('0'); - }); -}); - -describe('RFT: Fees', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer-From', '6', '6'); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - - const result = await contract.methods.mint(caller).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - - const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send()); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - expect(cost > 0n); - }); - - itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer', '6', '6'); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - - const result = await contract.methods.mint(caller).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - - const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send()); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - expect(cost > 0n); - }); -}); - -describe('Common metadata', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([1000n], donor); - }); - }); - - itEth('Returns collection name', async ({helper}) => { - const caller = helper.eth.createAccount(); - const tokenPropertyPermissions = [{ - key: 'URI', - permission: { - mutable: true, - collectionAdmin: true, - tokenOwner: false, - }, - }]; - const collection = await helper.rft.mintCollection( - alice, - { - name: 'Leviathan', - tokenPrefix: '11', - properties: [{key: 'ERC721Metadata', value: '1'}], - tokenPropertyPermissions, - }, - ); - - const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller); - const name = await contract.methods.name().call(); - expect(name).to.equal('Leviathan'); - }); - - itEth('Returns symbol name', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const tokenPropertyPermissions = [{ - key: 'URI', - permission: { - mutable: true, - collectionAdmin: true, - tokenOwner: false, - }, - }]; - const {collectionId} = await helper.rft.mintCollection( - alice, - { - name: 'Leviathan', - tokenPrefix: '12', - properties: [{key: 'ERC721Metadata', value: '1'}], - tokenPropertyPermissions, - }, - ); - - const contract = await helper.ethNativeContract.collectionById(collectionId, 'rft', caller); - const symbol = await contract.methods.symbol().call(); - expect(symbol).to.equal('12'); - }); -}); - -describe('Negative tests', () => { - let donor: IKeyringPair; - let minter: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itEth('[negative] Cant perform burn without approval', async ({helper}) => { - const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = await helper.eth.createAccountWithBalance(donor); - - const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'rft'); - - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - - await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected; - - await contract.methods.setApprovalForAll(spender, true).send({from: owner}); - await contract.methods.setApprovalForAll(spender, false).send({from: owner}); - - await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected; - }); - - itEth('[negative] Cant perform transfer without approval', async ({helper}) => { - const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = alice; - - const spender = await helper.eth.createAccountWithBalance(donor); - - const token = await collection.mintToken(minter, 100n, {Ethereum: owner}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'rft'); - - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver); - - await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected; - - await contract.methods.setApprovalForAll(spender, true).send({from: owner}); - await contract.methods.setApprovalForAll(spender, false).send({from: owner}); - - await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected; - }); - - itEth('[negative] Can perform mintBulkCross() with multiple owners and multiple tokens', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const callerCross = helper.ethCrossAccount.fromAddress(caller); - const receiver = helper.eth.createAccount(); - const receiverCross = helper.ethCrossAccount.fromAddress(receiver); - const receiver2 = helper.eth.createAccount(); - const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2); - - const permissions = [ - {code: TokenPermissionField.Mutable, value: true}, - {code: TokenPermissionField.TokenOwner, value: true}, - {code: TokenPermissionField.CollectionAdmin, value: true}, - ]; - const {collectionAddress} = await helper.eth.createCollection( - caller, - { - ...CREATE_COLLECTION_DATA_DEFAULTS, - name: 'A', - description: 'B', - tokenPrefix: 'C', - collectionMode: 'rft', - adminList: [callerCross], - tokenPropertyPermissions: [ - {key: 'key_0_0', permissions}, - {key: 'key_2_0', permissions}, - {key: 'key_2_1', permissions}, - {key: 'key_2_2', permissions}, - ], - }, - ).send(); - - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - const nextTokenId = await contract.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - const createData = [ - { - owners: [{ - owner: receiverCross, - pieces: 1, - }], - properties: [ - {key: 'key_0_0', value: Buffer.from('value_0_0')}, - ], - }, - { - owners: [ - { - owner: receiverCross, - pieces: 1, - }, - { - owner: receiver2Cross, - pieces: 2, - }, - ], - properties: [ - {key: 'key_2_0', value: Buffer.from('value_2_0')}, - {key: 'key_2_1', value: Buffer.from('value_2_1')}, - {key: 'key_2_2', value: Buffer.from('value_2_2')}, - ], - }, - ]; - - await expect(contract.methods.mintBulkCross(createData).call({from: caller})).to.be.rejectedWith('creation of multiple tokens supported only if they have single owner each'); - }); -}); --- a/js-packages/tests/src/eth/reFungibleToken.test.ts +++ /dev/null @@ -1,677 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {Pallets, requirePalletsOrSkip} from '../util/index.js'; -import {expect, itEth, usingEthPlaygrounds} from './util/index.js'; -import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {Contract} from 'web3-eth-contract'; - -// FIXME: Need erc721 for ReFubgible. -describe('Check ERC721 token URI for ReFungible', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - }); - }); - - async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Mint collection', 'a', 'b', baseUri); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - - const result = await contract.methods.mint(receiver).send(); - - const event = result.events.Transfer; - const tokenId = event.returnValues.tokenId; - expect(tokenId).to.be.equal('1'); - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.be.equal(receiver); - - if(propertyKey && propertyValue) { - // Set URL or suffix - - await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send(); - } - - return {contract, nextTokenId: tokenId}; - } - - itEth('Empty tokenURI', async ({helper}) => { - const {contract, nextTokenId} = await setup(helper, ''); - expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal(''); - }); - - itEth('TokenURI from url', async ({helper}) => { - const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI'); - expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI'); - }); - - itEth('TokenURI from baseURI', async ({helper}) => { - const {contract, nextTokenId} = await setup(helper, 'BaseURI_'); - expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_'); - }); - - itEth('TokenURI from baseURI + suffix', async ({helper}) => { - const suffix = '/some/suffix'; - const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix); - expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix); - }); -}); - -describe('Refungible: Plain calls', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([50n], donor); - }); - }); - - itEth('Can perform approve()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = helper.eth.createAccount(); - const collection = await helper.rft.mintCollection(alice); - const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - { - const result = await contract.methods.approve(spender, 100).send({from: owner}); - const event = result.events.Approval; - expect(event.address).to.be.equal(tokenAddress); - expect(event.returnValues.owner).to.be.equal(owner); - expect(event.returnValues.spender).to.be.equal(spender); - expect(event.returnValues.value).to.be.equal('100'); - } - - { - const allowance = await contract.methods.allowance(owner, spender).call(); - expect(+allowance).to.equal(100); - } - }); - - itEth('Can perform approveCross()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = helper.eth.createAccount(); - const spenderSub = (await helper.arrange.createAccounts([1n], donor))[0]; - const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender); - const spenderCrossSub = helper.ethCrossAccount.fromKeyringPair(spenderSub); - - - const collection = await helper.rft.mintCollection(alice); - const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - { - const result = await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner}); - const event = result.events.Approval; - expect(event.address).to.be.equal(tokenAddress); - expect(event.returnValues.owner).to.be.equal(owner); - expect(event.returnValues.spender).to.be.equal(spender); - expect(event.returnValues.value).to.be.equal('100'); - } - - { - const allowance = await contract.methods.allowance(owner, spender).call(); - expect(+allowance).to.equal(100); - } - - - { - const result = await contract.methods.approveCross(spenderCrossSub, 100).send({from: owner}); - const event = result.events.Approval; - expect(event.address).to.be.equal(tokenAddress); - expect(event.returnValues.owner).to.be.equal(owner); - expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(spenderSub.address)); - expect(event.returnValues.value).to.be.equal('100'); - } - - { - const allowance = await collection.getTokenApprovedPieces(tokenId, {Ethereum: owner}, {Substrate: spenderSub.address}); - expect(allowance).to.equal(100n); - } - - { - //TO-DO expect with future allowanceCross(owner, spenderCrossEth).call() - } - }); - - itEth('Non-owner and non admin cannot approveCross', async ({helper}) => { - const nonOwner = await helper.eth.createAccountWithBalance(donor); - const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner); - const owner = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}); - const token = await collection.mintToken(alice, 100n, {Ethereum: owner}); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId); - const tokenEvm = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - await expect(tokenEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned'); - }); - - [ - 'transferFrom', - 'transferFromCross', - ].map(testCase => - itEth(`Can perform ${testCase}`, async ({helper}) => { - const isCross = testCase === 'transferFromCross'; - const owner = await helper.eth.createAccountWithBalance(donor); - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const spender = await helper.eth.createAccountWithBalance(donor); - const receiverEth = helper.eth.createAccount(); - const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); - const [receiverSub] = await helper.arrange.createAccounts([1n], donor); - const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub); - - const collection = await helper.rft.mintCollection(alice); - const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - await contract.methods.approve(spender, 100).send({from: owner}); - - // 1. Can transfer from - // 1.1 Plain ethereum or cross address: - { - const result = await contract.methods[testCase]( - isCross ? ownerCross : owner, - isCross ? receiverCrossEth : receiverEth, - 49, - ).send({from: spender}); - - // Check events: - const transferEvent = result.events.Transfer; - expect(transferEvent.address).to.be.equal(tokenAddress); - expect(transferEvent.returnValues.from).to.be.equal(owner); - expect(transferEvent.returnValues.to).to.be.equal(receiverEth); - expect(transferEvent.returnValues.value).to.be.equal('49'); - - const approvalEvent = result.events.Approval; - expect(approvalEvent.address).to.be.equal(tokenAddress); - expect(approvalEvent.returnValues.owner).to.be.equal(owner); - expect(approvalEvent.returnValues.spender).to.be.equal(spender); - expect(approvalEvent.returnValues.value).to.be.equal('51'); - - // Check balances: - const receiverBalance = await contract.methods.balanceOf(receiverEth).call(); - const ownerBalance = await contract.methods.balanceOf(owner).call(); - - expect(+receiverBalance).to.equal(49); - expect(+ownerBalance).to.equal(151); - } - - // 1.2 Cross substrate address: - if(testCase === 'transferFromCross') { - const result = await contract.methods.transferFromCross(ownerCross, receiverCrossSub, 51).send({from: spender}); - // Check events: - const transferEvent = result.events.Transfer; - expect(transferEvent.address).to.be.equal(tokenAddress); - expect(transferEvent.returnValues.from).to.be.equal(owner); - expect(transferEvent.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address)); - expect(transferEvent.returnValues.value).to.be.equal('51'); - - const approvalEvent = result.events.Approval; - expect(approvalEvent.address).to.be.equal(tokenAddress); - expect(approvalEvent.returnValues.owner).to.be.equal(owner); - expect(approvalEvent.returnValues.spender).to.be.equal(spender); - expect(approvalEvent.returnValues.value).to.be.equal('0'); - - // Check balances: - const receiverBalance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address}); - const ownerBalance = await contract.methods.balanceOf(owner).call(); - expect(receiverBalance).to.equal(51n); - expect(+ownerBalance).to.equal(100); - } - })); - - [ - 'transfer', - 'transferCross', - ].map(testCase => - itEth(`Can perform ${testCase}()`, async ({helper}) => { - const isCross = testCase === 'transferCross'; - const owner = await helper.eth.createAccountWithBalance(donor); - const receiverEth = helper.eth.createAccount(); - const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); - const [receiverSub] = await helper.arrange.createAccounts([1n], donor); - const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub); - const collection = await helper.rft.mintCollection(alice); - const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - // 1. Can transfer to plain ethereum or cross-ethereum account: - { - const result = await contract.methods[testCase](isCross ? receiverCrossEth : receiverEth, 50).send({from: owner}); - // Check events - const transferEvent = result.events.Transfer; - expect(transferEvent.address).to.be.equal(tokenAddress); - expect(transferEvent.returnValues.from).to.be.equal(owner); - expect(transferEvent.returnValues.to).to.be.equal(receiverEth); - expect(transferEvent.returnValues.value).to.be.equal('50'); - // Check balances: - const ownerBalance = await contract.methods.balanceOf(owner).call(); - const receiverBalance = await contract.methods.balanceOf(receiverEth).call(); - expect(+ownerBalance).to.equal(150); - expect(+receiverBalance).to.equal(50); - } - - // 2. Can transfer to cross-substrate account: - if(isCross) { - const result = await contract.methods.transferCross(receiverCrossSub, 50).send({from: owner}); - // Check events: - const event = result.events.Transfer; - expect(event.address).to.be.equal(tokenAddress); - expect(event.returnValues.from).to.be.equal(owner); - expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address)); - expect(event.returnValues.value).to.be.equal('50'); - // Check balances: - const ownerBalance = await contract.methods.balanceOf(owner).call(); - const receiverBalance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address}); - expect(+ownerBalance).to.equal(100); - expect(receiverBalance).to.equal(50n); - } - })); - - [ - 'transfer', - 'transferCross', - ].map(testCase => - itEth(`Cannot ${testCase}() non-owned token`, async ({helper}) => { - const isCross = testCase === 'transferCross'; - const owner = await helper.eth.createAccountWithBalance(donor); - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const receiverEth = await helper.eth.createAccountWithBalance(donor); - const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); - const collection = await helper.rft.mintCollection(alice); - const rftOwner = await collection.mintToken(alice, 10n, {Ethereum: owner}); - const rftReceiver = await collection.mintToken(alice, 10n, {Ethereum: receiverEth}); - const tokenIdNonExist = 9999999; - - const tokenAddress1 = helper.ethAddress.fromTokenId(collection.collectionId, rftOwner.tokenId); - const tokenAddress2 = helper.ethAddress.fromTokenId(collection.collectionId, rftReceiver.tokenId); - const tokenAddressNonExist = helper.ethAddress.fromTokenId(collection.collectionId, tokenIdNonExist); - const tokenEvmOwner = await helper.ethNativeContract.rftToken(tokenAddress1, owner); - const tokenEvmReceiver = await helper.ethNativeContract.rftToken(tokenAddress2, owner); - const tokenEvmNonExist = await helper.ethNativeContract.rftToken(tokenAddressNonExist, owner); - - // 1. Can transfer zero amount (EIP-20): - await tokenEvmOwner.methods[testCase](isCross ? receiverCrossEth : receiverEth, 0).send({from: owner}); - // 2. Cannot transfer non-owned token: - await expect(tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 0).send({from: owner})).to.be.rejected; - await expect(tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 5).send({from: owner})).to.be.rejected; - // 3. Cannot transfer non-existing token: - await expect(tokenEvmNonExist.methods[testCase](isCross ? ownerCross : owner, 0).send({from: owner})).to.be.rejected; - await expect(tokenEvmNonExist.methods[testCase](isCross ? ownerCross : owner, 5).send({from: owner})).to.be.rejected; - - // 4. Storage is not corrupted: - expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]); - expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: receiverEth.toLowerCase()}]); - expect(await helper.rft.getTokenTop10Owners(collection.collectionId, tokenIdNonExist)).to.deep.eq([]); - - // 4.1 Tokens can be transferred: - await tokenEvmOwner.methods[testCase](isCross ? receiverCrossEth : receiverEth, 10).send({from: owner}); - await tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 10).send({from: receiverEth}); - expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: receiverEth.toLowerCase()}]); - expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]); - })); - - itEth('Can perform repartition()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.rft.mintCollection(alice); - const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner}); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - await contract.methods.repartition(200).send({from: owner}); - expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(200); - await contract.methods.transfer(receiver, 110).send({from: owner}); - expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(90); - expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(110); - - await expect(contract.methods.repartition(80).send({from: owner})).to.eventually.be.rejected; // Transaction is reverted - - await contract.methods.transfer(receiver, 90).send({from: owner}); - expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(0); - expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(200); - - await contract.methods.repartition(150).send({from: receiver}); - await expect(contract.methods.transfer(owner, 160).send({from: receiver})).to.eventually.be.rejected; // Transaction is reverted - expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(150); - }); - - itEth('Can repartition with increased amount', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.rft.mintCollection(alice); - const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner}); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - const result = await contract.methods.repartition(200).send(); - - const event = result.events.Transfer; - expect(event.address).to.be.equal(tokenAddress); - expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.be.equal(owner); - expect(event.returnValues.value).to.be.equal('100'); - }); - - itEth('Can repartition with decreased amount', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.rft.mintCollection(alice); - const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner}); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - const result = await contract.methods.repartition(50).send(); - const event = result.events.Transfer; - expect(event.address).to.be.equal(tokenAddress); - expect(event.returnValues.from).to.be.equal(owner); - expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.value).to.be.equal('50'); - }); - - itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Devastation', '6', '6'); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - - const result = await contract.methods.mint(caller).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId); - const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, caller, true); - - await tokenContract.methods.repartition(2).send(); - await tokenContract.methods.transfer(receiver, 1).send(); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - await tokenContract.methods.burnFrom(caller, 1).send(); - - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.tokenId).to.be.equal(tokenId); - }); - - itEth('Can perform burnFromCross()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const ownerSub = (await helper.arrange.createAccounts([10n], donor))[0]; - const ownerCross = helper.ethCrossAccount.fromAddress(owner); - const spender = await helper.eth.createAccountWithBalance(donor); - - const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender); - const ownerSubCross = helper.ethCrossAccount.fromKeyringPair(ownerSub); - - const collection = await helper.rft.mintCollection(alice); - const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); - - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - { - await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner}); - - await expect(contract.methods.burnFromCross(ownerCross, 50).send({from: spender})).to.be.fulfilled; - await expect(contract.methods.burnFromCross(ownerCross, 100).send({from: spender})).to.be.rejected; - expect(await contract.methods.balanceOf(owner).call({from: owner})).to.be.equal('150'); - } - { - const {tokenId} = await collection.mintToken(alice, 200n, {Substrate: ownerSub.address}); - await collection.approveToken(ownerSub, tokenId, {Ethereum: spender}, 100n); - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - await expect(contract.methods.burnFromCross(ownerSubCross, 50).send({from: spender})).to.be.fulfilled; - await expect(contract.methods.burnFromCross(ownerSubCross, 100).send({from: spender})).to.be.rejected; - expect(await collection.getTokenBalance(tokenId, {Substrate: ownerSub.address})).to.be.equal(150n); - } - }); - - itEth('Check balanceOfCross()', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {}); - const owner = await helper.ethCrossAccount.createAccountWithBalance(donor); - const other = await helper.ethCrossAccount.createAccountWithBalance(donor); - const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner.eth}); - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth); - - expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('200'); - expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0'); - - await tokenContract.methods.repartition(100n).send({from: owner.eth}); - expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100'); - expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0'); - - await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth}); - expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50'); - expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50'); - - await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth}); - expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0'); - expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100'); - - await tokenContract.methods.repartition(1000n).send({from: other.eth}); - await tokenContract.methods.transferCross(owner, 500n).send({from: other.eth}); - expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('500'); - expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('500'); - }); -}); - -describe('Refungible: Fees', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([50n], donor); - }); - }); - - itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = helper.eth.createAccount(); - const collection = await helper.rft.mintCollection(alice); - const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner}); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner})); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - }); - - itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const spender = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.rft.mintCollection(alice); - const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - await contract.methods.approve(spender, 100).send({from: owner}); - - const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender})); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - }); - - itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const collection = await helper.rft.mintCollection(alice); - const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner}); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner})); - expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))); - }); -}); - -describe('Refungible: Substrate calls', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([50n], donor); - }); - }); - - itEth('Events emitted for approve()', async ({helper}) => { - const receiver = helper.eth.createAccount(); - const collection = await helper.rft.mintCollection(alice); - const token = await collection.mintToken(alice, 200n); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - expect(await token.approve(alice, {Ethereum: receiver}, 100n)).to.be.true; - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.event).to.be.equal('Approval'); - expect(event.address).to.be.equal(tokenAddress); - expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.spender).to.be.equal(receiver); - expect(event.returnValues.value).to.be.equal('100'); - }); - - itEth('Events emitted for transferFrom()', async ({helper}) => { - const [bob] = await helper.arrange.createAccounts([10n], donor); - const receiver = helper.eth.createAccount(); - const collection = await helper.rft.mintCollection(alice); - const token = await collection.mintToken(alice, 200n); - await token.approve(alice, {Substrate: bob.address}, 100n); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - expect(await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n)).to.be.true; - if(events.length == 0) await helper.wait.newBlocks(1); - - let event = events[0]; - expect(event.event).to.be.equal('Transfer'); - expect(event.address).to.be.equal(tokenAddress); - expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.value).to.be.equal('51'); - - event = events[1]; - expect(event.event).to.be.equal('Approval'); - expect(event.address).to.be.equal(tokenAddress); - expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address)); - expect(event.returnValues.value).to.be.equal('49'); - }); - - itEth('Events emitted for transfer()', async ({helper}) => { - const receiver = helper.eth.createAccount(); - const collection = await helper.rft.mintCollection(alice); - const token = await collection.mintToken(alice, 200n); - - const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId); - const contract = await helper.ethNativeContract.rftToken(tokenAddress); - - const events: any = []; - contract.events.allEvents((_: any, event: any) => { - events.push(event); - }); - - expect(await token.transfer(alice, {Ethereum: receiver}, 51n)).to.be.true; - if(events.length == 0) await helper.wait.newBlocks(1); - const event = events[0]; - - expect(event.event).to.be.equal('Transfer'); - expect(event.address).to.be.equal(tokenAddress); - expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address)); - expect(event.returnValues.to).to.be.equal(receiver); - expect(event.returnValues.value).to.be.equal('51'); - }); -}); - -describe('ERC 1633 implementation', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Default parent token address and id', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sands', '', 'GRAIN'); - const collectionContract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - - const result = await collectionContract.methods.mint(owner).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - - const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId); - const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner); - - expect(await tokenContract.methods.parentToken().call()).to.be.equal(collectionAddress); - expect(await tokenContract.methods.parentTokenId().call()).to.be.equal(tokenId); - }); -}); --- a/js-packages/tests/src/eth/scheduling.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {expect} from 'chai'; -import {itSchedEth} from './util/index.js'; -import {EthUniqueHelper} from './util/playgrounds/unique.dev.js'; -import {Pallets, usingPlaygrounds} from '../util/index.js'; - -describe('Scheduing EVM smart contracts', () => { - - before(async () => { - await usingPlaygrounds(async (helper) => { - await helper.testUtils.enable(Pallets.TestUtils); - }); - }); - - itSchedEth.ifWithPallets('Successfully schedules and periodically executes an EVM contract', [Pallets.UniqueScheduler], async (scheduleKind, {helper, privateKey}) => { - const donor = await privateKey({url: import.meta.url}); - const [alice] = await helper.arrange.createAccounts([1000n], donor); - - const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; - - const deployer = await helper.eth.createAccountWithBalance(alice); - const flipper = await helper.eth.deployFlipper(deployer); - - const initialValue = await flipper.methods.getValue().call(); - await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address)); - - const waitForBlocks = 4; - const periodic = { - period: 2, - repetitions: 2, - }; - - await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) - .eth.sendEVM( - alice, - flipper.options.address, - flipper.methods.flip().encodeABI(), - '0', - ); - - expect(await flipper.methods.getValue().call()).to.be.equal(initialValue); - - await helper.wait.newBlocks(waitForBlocks + 1); - expect(await flipper.methods.getValue().call()).to.be.not.equal(initialValue); - - await helper.wait.newBlocks(periodic.period); - expect(await flipper.methods.getValue().call()).to.be.equal(initialValue); - }); -}); --- a/js-packages/tests/src/eth/sponsoring.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itEth, expect, SponsoringMode} from './util/index.js'; -import {usingPlaygrounds} from '../util/index.js'; - -describe('EVM sponsoring', () => { - let donor: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Fee is deducted from contract if sponsoring is enabled', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const caller = helper.eth.createAccount(); - const originalCallerBalance = await helper.balance.getEthereum(caller); - - expect(originalCallerBalance).to.be.equal(0n); - - const flipper = await helper.eth.deployFlipper(owner); - - const helpers = await helper.ethNativeContract.contractHelpers(owner); - - await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner}); - await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner}); - - await helpers.methods.setSponsor(flipper.options.address, sponsor).send({from: owner}); - await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); - - expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false; - await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner}); - await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner}); - expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true; - - const originalSponsorBalance = await helper.balance.getEthereum(sponsor); - expect(originalSponsorBalance).to.be.not.equal(0n); - - await flipper.methods.flip().send({from: caller}); - expect(await flipper.methods.getValue().call()).to.be.true; - - // Balance should be taken from flipper instead of caller - expect(await helper.balance.getEthereum(caller)).to.be.equal(originalCallerBalance); - expect(await helper.balance.getEthereum(sponsor)).to.be.not.equal(originalSponsorBalance); - }); - - itEth('...but this doesn\'t applies to payable value', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - const originalCallerBalance = await helper.balance.getEthereum(caller); - - expect(originalCallerBalance).to.be.not.equal(0n); - - const collector = await helper.eth.deployCollectorContract(owner); - - const helpers = await helper.ethNativeContract.contractHelpers(owner); - - await helpers.methods.toggleAllowlist(collector.options.address, true).send({from: owner}); - await helpers.methods.toggleAllowed(collector.options.address, caller, true).send({from: owner}); - - expect(await helpers.methods.sponsoringEnabled(collector.options.address).call()).to.be.false; - await helpers.methods.setSponsoringMode(collector.options.address, SponsoringMode.Allowlisted).send({from: owner}); - await helpers.methods.setSponsoringRateLimit(collector.options.address, 0).send({from: owner}); - expect(await helpers.methods.sponsoringEnabled(collector.options.address).call()).to.be.true; - - await helpers.methods.setSponsor(collector.options.address, sponsor).send({from: owner}); - await helpers.methods.confirmSponsorship(collector.options.address).send({from: sponsor}); - - const originalSponsorBalance = await helper.balance.getEthereum(sponsor); - expect(originalSponsorBalance).to.be.not.equal(0n); - - await collector.methods.giveMoney().send({from: caller, value: '10000'}); - - // Balance will be taken from both caller (value) and from collector (fee) - expect(await helper.balance.getEthereum(caller)).to.be.equals((originalCallerBalance - 10000n)); - expect(await helper.balance.getEthereum(sponsor)).to.be.not.equals(originalSponsorBalance); - expect(await collector.methods.getCollected().call()).to.be.equal('10000'); - }); -}); --- a/js-packages/tests/src/eth/tokenProperties.test.ts +++ /dev/null @@ -1,625 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {Contract} from 'web3-eth-contract'; -import {itEth, usingEthPlaygrounds, expect} from './util/index.js'; -import type {ITokenPropertyPermission} from '@unique/playgrounds/src/types.js'; -import {Pallets} from '../util/index.js'; -import {UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection} from '@unique/playgrounds/src/unique.js'; -import {CreateCollectionData, TokenPermissionField} from './util/playgrounds/types.js'; - -describe('EVM token properties', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([1000n], donor); - }); - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.ethCrossAccount.createAccountWithBalance(donor); - for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) { - const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - await collection.methods.addCollectionAdminCross(caller).send({from: owner}); - - await collection.methods.setTokenPropertyPermissions([ - ['testKey', [ - [TokenPermissionField.Mutable, mutable], - [TokenPermissionField.TokenOwner, tokenOwner], - [TokenPermissionField.CollectionAdmin, collectionAdmin]], - ], - ]).send({from: caller.eth}); - - expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{ - key: 'testKey', - permission: {mutable, collectionAdmin, tokenOwner}, - }]); - - expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([ - ['testKey', [ - [TokenPermissionField.Mutable.toString(), mutable], - [TokenPermissionField.TokenOwner.toString(), tokenOwner], - [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]], - ], - ]); - } - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as owner`, testCase.requiredPallets, async({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - - await collection.methods.setTokenPropertyPermissions([ - ['testKey_0', [ - [TokenPermissionField.Mutable, true], - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, true]], - ], - ['testKey_1', [ - [TokenPermissionField.Mutable, true], - [TokenPermissionField.TokenOwner, false], - [TokenPermissionField.CollectionAdmin, true]], - ], - ['testKey_2', [ - [TokenPermissionField.Mutable, false], - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, false]], - ], - ]).send({from: owner}); - - expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([ - { - key: 'testKey_0', - permission: {mutable: true, tokenOwner: true, collectionAdmin: true}, - }, - { - key: 'testKey_1', - permission: {mutable: true, tokenOwner: false, collectionAdmin: true}, - }, - { - key: 'testKey_2', - permission: {mutable: false, tokenOwner: true, collectionAdmin: false}, - }, - ]); - - expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([ - ['testKey_0', [ - [TokenPermissionField.Mutable.toString(), true], - [TokenPermissionField.TokenOwner.toString(), true], - [TokenPermissionField.CollectionAdmin.toString(), true]], - ], - ['testKey_1', [ - [TokenPermissionField.Mutable.toString(), true], - [TokenPermissionField.TokenOwner.toString(), false], - [TokenPermissionField.CollectionAdmin.toString(), true]], - ], - ['testKey_2', [ - [TokenPermissionField.Mutable.toString(), false], - [TokenPermissionField.TokenOwner.toString(), true], - [TokenPermissionField.CollectionAdmin.toString(), false]], - ], - ]); - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as admin`, testCase.requiredPallets, async({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.ethCrossAccount.createAccountWithBalance(donor); - - const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - await collection.methods.addCollectionAdminCross(caller).send({from: owner}); - - await collection.methods.setTokenPropertyPermissions([ - ['testKey_0', [ - [TokenPermissionField.Mutable, true], - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, true]], - ], - ['testKey_1', [ - [TokenPermissionField.Mutable, true], - [TokenPermissionField.TokenOwner, false], - [TokenPermissionField.CollectionAdmin, true]], - ], - ['testKey_2', [ - [TokenPermissionField.Mutable, false], - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, false]], - ], - ]).send({from: caller.eth}); - - expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([ - { - key: 'testKey_0', - permission: {mutable: true, tokenOwner: true, collectionAdmin: true}, - }, - { - key: 'testKey_1', - permission: {mutable: true, tokenOwner: false, collectionAdmin: true}, - }, - { - key: 'testKey_2', - permission: {mutable: false, tokenOwner: true, collectionAdmin: false}, - }, - ]); - - expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([ - ['testKey_0', [ - [TokenPermissionField.Mutable.toString(), true], - [TokenPermissionField.TokenOwner.toString(), true], - [TokenPermissionField.CollectionAdmin.toString(), true]], - ], - ['testKey_1', [ - [TokenPermissionField.Mutable.toString(), true], - [TokenPermissionField.TokenOwner.toString(), false], - [TokenPermissionField.CollectionAdmin.toString(), true]], - ], - ['testKey_2', [ - [TokenPermissionField.Mutable.toString(), false], - [TokenPermissionField.TokenOwner.toString(), true], - [TokenPermissionField.CollectionAdmin.toString(), false]], - ], - ]); - - })); - - [ - { - method: 'setProperties', - methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], - expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}], - }, - { - method: 'setProperty' /*Soft-deprecated*/, - methodParams: ['testKey1', Buffer.from('testValue1')], - expectedProps: [{key: 'testKey1', value: 'testValue1'}], - }, - ].map(testCase => - itEth(`[${testCase.method}] Can be set`, async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.nft.mintCollection(alice, { - tokenPropertyPermissions: [{ - key: 'testKey1', - permission: { - collectionAdmin: true, - }, - }, { - key: 'testKey2', - permission: { - collectionAdmin: true, - }, - }], - }); - - await collection.addAdmin(alice, {Ethereum: caller}); - const token = await collection.mintToken(alice); - - const collectionEvm = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller, testCase.method === 'setProperty'); - - await collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller}); - - const properties = await token.getProperties(); - expect(properties).to.deep.equal(testCase.expectedProps); - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`Can be multiple set/read for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - - const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)})); - const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {tokenOwner: true, - collectionAdmin: true, - mutable: true}})); - - const collection = await helper[testCase.mode].mintCollection(alice, { - tokenPrefix: 'ethp', - tokenPropertyPermissions: permissions, - }) as UniqueNFTCollection | UniqueRFTCollection; - - const token = await collection.mintToken(alice); - - const valuesBefore = await token.getProperties(properties.map(p => p.key)); - expect(valuesBefore).to.be.deep.equal([]); - - - await collection.addAdmin(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller); - - expect(await contract.methods.properties(token.tokenId, []).call()).to.be.deep.equal([]); - - await contract.methods.setProperties(token.tokenId, properties).send({from: caller}); - - const values = await token.getProperties(properties.map(p => p.key)); - expect(values).to.be.deep.equal(properties.map(p => ({key: p.key, value: p.value.toString()}))); - - expect(await contract.methods.properties(token.tokenId, []).call()).to.be.like(properties - .map(p => helper.ethProperty.property(p.key, p.value.toString()))); - - expect(await contract.methods.properties(token.tokenId, [properties[0].key]).call()) - .to.be.like([helper.ethProperty.property(properties[0].key, properties[0].value.toString())]); - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`Can be deleted for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper[testCase.mode].mintCollection(alice, { - tokenPropertyPermissions: [{ - key: 'testKey', - permission: { - mutable: true, - collectionAdmin: true, - }, - }, - { - key: 'testKey_1', - permission: { - mutable: true, - collectionAdmin: true, - }, - }], - }); - - const token = await collection.mintToken(alice); - await token.setProperties(alice, [{key: 'testKey', value: 'testValue'}, {key: 'testKey_1', value: 'testValue_1'}]); - expect(await token.getProperties()).to.has.length(2); - - await collection.addAdmin(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller); - - await contract.methods.deleteProperties(token.tokenId, ['testKey', 'testKey_1']).send({from: caller}); - - const result = await token.getProperties(['testKey', 'testKey_1']); - expect(result.length).to.equal(0); - })); - - itEth('Can be read', async({helper}) => { - const caller = helper.eth.createAccount(); - const collection = await helper.nft.mintCollection(alice, { - tokenPropertyPermissions: [{ - key: 'testKey', - permission: { - collectionAdmin: true, - }, - }], - }); - - const token = await collection.mintToken(alice); - await token.setProperties(alice, [{key: 'testKey', value: 'testValue'}]); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, 'nft', caller); - - const value = await contract.methods.property(token.tokenId, 'testKey').call(); - expect(value).to.equal(helper.getWeb3().utils.toHex('testValue')); - }); -}); - -describe('EVM token properties negative', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let caller: string; - let aliceCollection: UniqueNFTCollection; - let token: UniqueNFToken; - const tokenProps = [{key: 'testKey_1', value: 'testValue_1'}, {key: 'testKey_2', value: 'testValue_2'}]; - let collectionEvm: Contract; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([100n], donor); - }); - }); - - beforeEach(async () => { - // 1. create collection with props: testKey_1, testKey_2 - // 2. create token and set props testKey_1, testKey_2 - await usingEthPlaygrounds(async (helper) => { - aliceCollection = await helper.nft.mintCollection(alice, { - tokenPropertyPermissions: [{ - key: 'testKey_1', - permission: { - mutable: true, - collectionAdmin: true, - }, - }, - { - key: 'testKey_2', - permission: { - mutable: true, - collectionAdmin: true, - }, - }], - }); - token = await aliceCollection.mintToken(alice); - await token.setProperties(alice, tokenProps); - collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true); - }); - }); - - [ - {method: 'setProperty', methodParams: [tokenProps[1].key, Buffer.from('newValue')]}, - {method: 'setProperties', methodParams: [[{key: tokenProps[1].key, value: Buffer.from('newValue')}]]}, - ].map(testCase => - itEth(`[${testCase.method}] Cannot set properties of non-owned collection`, async ({helper}) => { - caller = await helper.eth.createAccountWithBalance(donor); - collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true); - // Caller not an owner and not an admin, so he cannot set properties: - await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission'); - await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected; - - // Props have not changed: - const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString())); - const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call(); - expect(actualProps).to.deep.eq(expectedProps); - })); - - [ - {method: 'setProperty', methodParams: ['testKey_3', Buffer.from('testValue3')]}, - {method: 'setProperties', methodParams: [[{key: 'testKey_3', value: Buffer.from('testValue3')}]]}, - ].map(testCase => - itEth(`[${testCase.method}] Cannot set non-existing properties`, async ({helper}) => { - caller = await helper.eth.createAccountWithBalance(donor); - collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true); - await helper.collection.addAdmin(alice, aliceCollection.collectionId, {Ethereum: caller}); - - await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission'); - await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected; - - // Props have not changed: - const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString())); - const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call(); - expect(actualProps).to.deep.eq(expectedProps); - })); - - [ - {method: 'deleteProperty', methodParams: ['testKey_2']}, - {method: 'deleteProperties', methodParams: [['testKey_2']]}, - ].map(testCase => - itEth(`[${testCase.method}] Cannot delete properties of non-owned collection`, async ({helper}) => { - caller = await helper.eth.createAccountWithBalance(donor); - collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, testCase.method == 'deleteProperty'); - // Caller not an owner and not an admin, so he cannot set properties: - await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission'); - await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected; - - // Props have not changed: - const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString())); - const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call(); - expect(actualProps).to.deep.eq(expectedProps); - })); - - [ - {method: 'deleteProperty', methodParams: ['testKey_3']}, - {method: 'deleteProperties', methodParams: [['testKey_3']]}, - ].map(testCase => - itEth(`[${testCase.method}] Cannot delete non-existing properties`, async ({helper}) => { - caller = await helper.eth.createAccountWithBalance(donor); - collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, testCase.method == 'deleteProperty'); - await helper.collection.addAdmin(alice, aliceCollection.collectionId, {Ethereum: caller}); - // Caller cannot delete non-existing properties: - await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission'); - await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected; - // Props have not changed: - const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString())); - const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call(); - expect(actualProps).to.deep.eq(expectedProps); - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions as non owner or admin`, testCase.requiredPallets, async({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const caller = await helper.eth.createAccountWithBalance(donor); - - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - - await expect(collection.methods.setTokenPropertyPermissions([ - ['testKey_0', [ - [TokenPermissionField.Mutable, true], - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, true]], - ], - ]).call({from: caller})).to.be.rejectedWith('NoPermission'); - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions with invalid character`, testCase.requiredPallets, async({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - - await expect(collection.methods.setTokenPropertyPermissions([ - // "Space" is invalid character - ['testKey 0', [ - [TokenPermissionField.Mutable, true], - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, true]], - ], - ]).call({from: owner})).to.be.rejectedWith('InvalidCharacterInPropertyKey'); - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`[${testCase.mode}] Can reconfigure token property permissions to stricter ones`, testCase.requiredPallets, async({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - - // 1. Owner sets strict property-permissions: - await collection.methods.setTokenPropertyPermissions([ - ['testKey', [ - [TokenPermissionField.Mutable, true], - [TokenPermissionField.TokenOwner, true], - [TokenPermissionField.CollectionAdmin, true]], - ], - ]).send({from: owner}); - - // 2. Owner can set stricter property-permissions: - for(const values of [[true, true, false], [true, false, false], [false, false, false]]) { - await collection.methods.setTokenPropertyPermissions([ - ['testKey', [ - [TokenPermissionField.Mutable, values[0]], - [TokenPermissionField.TokenOwner, values[1]], - [TokenPermissionField.CollectionAdmin, values[2]]], - ], - ]).send({from: owner}); - } - - expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{ - key: 'testKey', - permission: {mutable: false, collectionAdmin: false, tokenOwner: false}, - }]); - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`[${testCase.mode}] Cannot reconfigure token property permissions to less strict ones`, testCase.requiredPallets, async({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send(); - const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - - // 1. Owner sets strict property-permissions: - await collection.methods.setTokenPropertyPermissions([ - ['testKey', [ - [TokenPermissionField.Mutable, false], - [TokenPermissionField.TokenOwner, false], - [TokenPermissionField.CollectionAdmin, false]], - ], - ]).send({from: owner}); - - // 2. Owner cannot set less strict property-permissions: - for(const values of [[true, false, false], [false, true, false], [false, false, true]]) { - await expect(collection.methods.setTokenPropertyPermissions([ - ['testKey', [ - [TokenPermissionField.Mutable, values[0]], - [TokenPermissionField.TokenOwner, values[1]], - [TokenPermissionField.CollectionAdmin, values[2]]], - ], - ]).call({from: owner})).to.be.rejectedWith('NoPermission'); - } - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`[${testCase.mode}] Can't be multiple set/read for non-existent token`, testCase.requiredPallets, async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - - const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)})); - const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {tokenOwner: true, - collectionAdmin: true, - mutable: true}})); - - const collection = await helper[testCase.mode].mintCollection(alice, { - tokenPrefix: 'ethp', - tokenPropertyPermissions: permissions, - }) as UniqueNFTCollection | UniqueRFTCollection; - - await collection.addAdmin(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller); - - await expect(contract.methods.setProperties(1, properties).call({from: caller})).to.be.rejectedWith('TokenNotFound'); - })); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itEth.ifWithPallets(`[${testCase.mode}] Can't be deleted for non-existent token`, testCase.requiredPallets, async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper[testCase.mode].mintCollection(alice, { - tokenPropertyPermissions: [{ - key: 'testKey', - permission: { - mutable: true, - collectionAdmin: true, - }, - }, - { - key: 'testKey_1', - permission: { - mutable: true, - collectionAdmin: true, - }, - }], - }); - - - await collection.addAdmin(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller); - - await expect(contract.methods.deleteProperties(1, ['testKey', 'testKey_1']).call({from: caller})).to.be.rejectedWith('TokenNotFound'); - })); -}); - - -type ElementOf = A extends readonly (infer T)[] ? T : never; -function* cartesian>, R extends Array>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf}]> { - if(args.length === 0) { - yield internalRest as any; - return; - } - for(const value of args[0]) { - yield* cartesian([...internalRest, value], ...args.slice(1)) as any; - } -} --- a/js-packages/tests/src/eth/tokens/callMethodsERC20.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {Pallets, requirePalletsOrSkip} from '../../util/index.js'; -import {expect, itEth, usingEthPlaygrounds} from '../util/index.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {CreateCollectionData} from '../util/playgrounds/types.js'; - -[ - {mode: 'ft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, -].map(testCase => { - describe(`${testCase.mode.toUpperCase()}: ERC-20 call methods`, () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, testCase.requiredPallets); - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('totalSupply', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller]; - - const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('TotalSupply', '6', '6', testCase.mode)).send(); - if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller}); - - // Use collection contract for FT or token contract for RFT: - const contract = testCase.mode === 'ft' - ? collection - : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller); - - // Mint tokens: - testCase.mode === 'ft' - ? await contract.methods.mint(...mintingParams).send({from: caller}) - : await contract.methods.repartition(200).send({from: caller}); - - const totalSupply = await contract.methods.totalSupply().call(); - expect(totalSupply).to.equal('200'); - }); - - itEth('balanceOf', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller]; - - const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send(); - if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller}); - - // Use collection contract for FT or token contract for RFT: - const contract = testCase.mode === 'ft' - ? collection - : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller); - - // Mint tokens: - testCase.mode === 'ft' - ? await contract.methods.mint(...mintingParams).send({from: caller}) - : await contract.methods.repartition(200).send({from: caller}); - - const balance = await contract.methods.balanceOf(caller).call(); - expect(balance).to.equal('200'); - }); - - itEth('decimals', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send(); - if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller}); - - // Use collection contract for FT or token contract for RFT: - const contract = testCase.mode === 'ft' - ? collection - : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller); - - const decimals = await contract.methods.decimals().call(); - expect(decimals).to.equal(testCase.mode === 'rft' ? '0' : '18'); - }); - }); -}); --- a/js-packages/tests/src/eth/tokens/callMethodsERC721.test.ts +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {Pallets} from '../../util/index.js'; -import {expect, itEth, usingEthPlaygrounds} from '../util/index.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {CreateCollectionData} from '../util/playgrounds/types.js'; - - -describe('ERC-721 call methods', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - [ - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {mode: 'nft' as const, requiredPallets: []}, - ].map(testCase => { - itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: name/symbol/description`, testCase.requiredPallets, async ({helper}) => { - const callerEth = await helper.eth.createAccountWithBalance(donor); - const [callerSub] = await helper.arrange.createAccounts([100n], donor); - const [name, description, tokenPrefix] = ['Name', 'Description', 'Symbol']; - - const {collection: collectionEth} = await helper.eth.createCollection(callerEth, new CreateCollectionData(name, description, tokenPrefix, testCase.mode)).send(); - await collectionEth.methods.mint(callerEth).send({from: callerEth}); - const {collectionId} = await helper[testCase.mode].mintCollection(callerSub, {name, description, tokenPrefix}); - const collectionSub = await helper.ethNativeContract.collectionById(collectionId, testCase.mode, callerEth); - - // Can get name/symbol/description for Eth collection - expect(await collectionEth.methods.name().call()).to.eq(name); - expect(await collectionEth.methods.symbol().call()).to.eq(tokenPrefix); - expect(await collectionEth.methods.description().call()).to.eq(description); - // Can get name/symbol/description for Sub collection - expect(await collectionSub.methods.name().call()).to.eq(name); - expect(await collectionSub.methods.symbol().call()).to.eq(tokenPrefix); - expect(await collectionSub.methods.description().call()).to.eq(description); - }); - }); - - [ - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {mode: 'nft' as const, requiredPallets: []}, - ].map(testCase => { - itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: totalSupply`, testCase.requiredPallets, async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - - const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('TotalSupply', '6', '6', testCase.mode)).send(); - await collection.methods.mint(caller).send({from: caller}); - - const totalSupply = await collection.methods.totalSupply().call(); - expect(totalSupply).to.equal('1'); - }); - }); - - [ - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {mode: 'nft' as const, requiredPallets: []}, - ].map(testCase => { - itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: balanceOf`, testCase.requiredPallets, async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - - const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send(); - await collection.methods.mint(caller).send({from: caller}); - await collection.methods.mint(caller).send({from: caller}); - await collection.methods.mint(caller).send({from: caller}); - - const balance = await collection.methods.balanceOf(caller).call(); - expect(balance).to.equal('3'); - }); - }); - - [ - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {mode: 'nft' as const, requiredPallets: []}, - ].map(testCase => { - itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: ownerOf`, testCase.requiredPallets, async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('OwnerOf', '6', '6', testCase.mode)).send(); - - const result = await collection.methods.mint(caller).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - - const owner = await collection.methods.ownerOf(tokenId).call(); - expect(owner).to.equal(caller); - }); - }); - - [ - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - // TODO {mode: 'nft' as const, requiredPallets: []}, - ].map(testCase => { - itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: ownerOf after burn`, testCase.requiredPallets, async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('OwnerOf-AfterBurn', '6', '6', testCase.mode)).send(); - - const result = await collection.methods.mint(caller).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller, true); - - await tokenContract.methods.repartition(2).send(); - await tokenContract.methods.transfer(receiver, 1).send(); - - await tokenContract.methods.burnFrom(caller, 1).send(); - - const owner = await collection.methods.ownerOf(tokenId).call(); - expect(owner).to.equal(receiver); - }); - }); - - itEth.ifWithPallets('RFT: ownerOf for partial ownership', [Pallets.ReFungible], async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Partial-OwnerOf', '6', '6'); - const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller); - - const result = await contract.methods.mint(caller).send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller); - - await tokenContract.methods.repartition(2).send(); - await tokenContract.methods.transfer(receiver, 1).send(); - - const owner = await contract.methods.ownerOf(tokenId).call(); - expect(owner).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF'); - }); -}); --- a/js-packages/tests/src/eth/tokens/minting.test.ts +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {Pallets} from '../../util/index.js'; -import {expect, itEth, usingEthPlaygrounds} from '../util/index.js'; -import {CreateCollectionData} from '../util/playgrounds/types.js'; - - -describe('Minting tokens', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([30n, 20n], donor); - }); - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {mode: 'ft' as const, requiredPallets: []}, - ].map(testCase => { - itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Substrate collection`, testCase.requiredPallets, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver]; - - const collection = await helper[testCase.mode].mintCollection(alice); - await collection.addAdmin(alice, {Ethereum: owner}); - - const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - - const result = await contract.methods.mint(...mintingParams).send({from: owner}); - - // Check events: - const event = result.events.Transfer; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.equal(receiver); - if(testCase.mode === 'ft') - expect(event.returnValues.value).to.equal('100'); - - // Check token exist: - if(testCase.mode === 'ft') { - expect(await helper.ft.getBalance(collection.collectionId, {Ethereum: receiver})).to.eq(100n); - } else { - const tokenId = event.returnValues.tokenId; - expect(tokenId).to.be.equal('1'); - expect(await helper.collection.getLastTokenId(collection.collectionId)).to.eq(1); - expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']); - } - }); - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {mode: 'ft' as const, requiredPallets: []}, - ].map(testCase => { - itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver]; - - const {collection, collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Name', 'Desc', 'Prefix', testCase.mode)).send(); - - const result = await collection.methods.mint(...mintingParams).send({from: owner}); - - // Check events: - const event = result.events.Transfer; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.equal(receiver); - if(testCase.mode === 'ft') - expect(event.returnValues.value).to.equal('100'); - - // Check token exist: - if(testCase.mode === 'ft') { - expect(await helper.ft.getBalance(collectionId, {Ethereum: receiver})).to.eq(100n); - } else { - const tokenId = event.returnValues.tokenId; - expect(tokenId).to.be.equal('1'); - expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1); - expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']); - } - }); - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - {mode: 'ft' as const, requiredPallets: []}, - ].map(testCase => { - itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver]; - - const {collection, collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Name', 'Desc', 'Prefix', testCase.mode)).send(); - - const result = await collection.methods.mint(...mintingParams).send({from: owner}); - - // Check events: - const event = result.events.Transfer; - expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.equal(receiver); - if(testCase.mode === 'ft') - expect(event.returnValues.value).to.equal('100'); - - // Check token exist: - if(testCase.mode === 'ft') { - expect(await helper.ft.getBalance(collectionId, {Ethereum: receiver})).to.eq(100n); - } else { - const tokenId = event.returnValues.tokenId; - expect(tokenId).to.be.equal('1'); - expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1); - expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']); - } - }); - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => { - itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mintWithTokenURI() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', ''); - const contract = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner); - - const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - expect(tokenId).to.be.equal('1'); - - const event = result.events.Transfer; - expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000'); - expect(event.returnValues.to).to.be.equal(receiver); - - expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); - expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']); - // TODO: this wont work right now, need release 919000 first - // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send(); - // const tokenUri = await contract.methods.tokenURI(nextTokenId).call(); - // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`); - }); - }); -}); --- a/js-packages/tests/src/eth/transferValue.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itEth, usingEthPlaygrounds} from './util/index.js'; -import {expect} from 'chai'; - -describe('Send value to contract', () => { - let donor: IKeyringPair; - - before(async () => { - await usingEthPlaygrounds(async (_helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Send to and from contract', async ({helper}) => { - const contractOwner = await helper.eth.createAccountWithBalance(donor, 600n); - const [buyer, receiver] = await helper.arrange.createAccounts([600n, 600n], donor); - const receiverMirror = helper.address.substrateToEth(receiver.address); - const contract = await helper.ethContract.deployByCode( - contractOwner, - 'Test', - ` - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.18; - - contract Test { - function send() public payable { - if (msg.value < 1000000000000000000) - revert("Not enough gold"); - } - - function withdraw(address to) public { - payable(to).transfer(1000000000000000000); - } - } - `, - ); - - const balanceBefore = await helper.balance.getSubstrate(buyer.address); - await helper.eth.sendEVM(buyer, contract.options.address, contract.methods.send().encodeABI(), '2000000000000000000'); - const balanceAfter = await helper.balance.getSubstrate(buyer.address); - expect(balanceBefore - balanceAfter > 2000000000000000000n).to.be.true; - expect(await helper.balance.getEthereum(contract.options.address)).to.be.equal(2000000000000000000n); - - await helper.eth.sendEVM(buyer, contract.options.address, contract.methods.withdraw(receiverMirror).encodeABI(), '0'); - expect(await helper.balance.getEthereum(receiverMirror)).to.be.equal(1000000000000000000n); - expect(await helper.balance.getEthereum(contract.options.address)).to.be.equal(1000000000000000000n); - }); -}); --- a/js-packages/tests/src/eth/util/index.ts +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -import * as path from 'path'; -import type {IKeyringPair} from '@polkadot/types/types'; - -import config from '../../config.js'; - -import {EthUniqueHelper} from './playgrounds/unique.dev.js'; -import {SilentLogger, SilentConsole} from '@unique/playgrounds/src/unique.dev.js'; -import {makeNames} from '../../util/index.js'; -import type {SchedKind} from '../../util/index.js'; - -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import chaiLike from 'chai-like'; -import {getTestSeed, MINIMUM_DONOR_FUND, requirePalletsOrSkip} from '../../util/index.js'; - -chai.use(chaiAsPromised); -chai.use(chaiLike); -export const expect = chai.expect; - -export enum SponsoringMode { - Disabled = 0, - Allowlisted = 1, - Generous = 2, -} - -type PrivateKeyFn = (seed: string | {filename?: string, url?: string}) => Promise; - -export const usingEthPlaygrounds = async (code: (helper: EthUniqueHelper, privateKey: PrivateKeyFn) => Promise) => { - const silentConsole = new SilentConsole(); - silentConsole.enable(); - - const helper = new EthUniqueHelper(new SilentLogger()); - - try { - await helper.connect(config.substrateUrl); - await helper.connectWeb3(config.substrateUrl); - const ss58Format = helper.chain.getChainProperties().ss58Format; - const privateKey: PrivateKeyFn = async (seed) => { - if(typeof seed === 'string') { - return helper.util.fromSeed(seed, ss58Format); - } - if(seed.url) { - const {filename} = makeNames(seed.url); - seed.filename = filename; - } else if(seed.filename) { - // Pass - } else { - throw new Error('no url nor filename set'); - } - const actualSeed = getTestSeed(seed.filename); - let account = helper.util.fromSeed(actualSeed, ss58Format); - if(await helper.balance.getSubstrate(account.address) < MINIMUM_DONOR_FUND) { - console.warn(`${path.basename(seed.filename)}: Not enough funds present on the filename account. Using the default one as the donor instead.`); - account = helper.util.fromSeed('//Alice', ss58Format); - } - return account; - }; - await code(helper, privateKey); - } - finally { - await helper.disconnect(); - silentConsole.disable(); - } -}; - -export function itEth(name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) { - (opts.only ? it.only : - opts.skip ? it.skip : it)(name, async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - if(opts.requiredPallets) { - requirePalletsOrSkip(this, helper, opts.requiredPallets); - } - - await cb({helper, privateKey}); - }); - }); -} - -export function itEthIfWithPallet(name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) { - return itEth(name, cb, {requiredPallets: required, ...opts}); -} - -itEth.only = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEth(name, cb, {only: true}); -itEth.skip = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any) => itEth(name, cb, {skip: true}); - -itEthIfWithPallet.only = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEthIfWithPallet(name, required, cb, {only: true}); -itEthIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEthIfWithPallet(name, required, cb, {skip: true}); -itEth.ifWithPallets = itEthIfWithPallet; - -export function itSchedEth( - name: string, - cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, - opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}, -) { - itEth(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts); - itEth(name + ' (named scheduling)', (apis) => cb('named', apis), opts); -} -itSchedEth.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any) => itSchedEth(name, cb, {only: true}); -itSchedEth.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any) => itSchedEth(name, cb, {skip: true}); -itSchedEth.ifWithPallets = itSchedIfWithPallets; - -function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) { - return itSchedEth(name, cb, {requiredPallets: required, ...opts}); -} --- a/js-packages/tests/src/eth/util/playgrounds/types.ts +++ /dev/null @@ -1,133 +0,0 @@ -import {CollectionFlag} from '@unique/playgrounds/src/types.js'; -import type {TCollectionMode} from '@unique/playgrounds/src/types.js'; - -export interface ContractImports { - solPath: string; - fsPath: string; -} - -export interface CompiledContract { - abi: any; - object: string; -} - -export type NormalizedEvent = { - address: string, - event: string, - args: { [key: string]: string } -}; - -export interface OptionUint { - status: boolean, - value: bigint, -} - -export type EthAddress = string; - -export interface CrossAddress { - readonly eth: EthAddress, - readonly sub: string | Uint8Array, -} - -export type EthProperty = string[]; - -export enum TokenPermissionField { - Mutable, - TokenOwner, - CollectionAdmin -} - -export enum CollectionLimitField { - AccountTokenOwnership, - SponsoredDataSize, - SponsoredDataRateLimit, - TokenLimit, - SponsorTransferTimeout, - SponsorApproveTimeout, - OwnerCanTransfer, - OwnerCanDestroy, - TransferEnabled -} - -export interface CollectionLimit { - field: CollectionLimitField, - value: OptionUint, -} - -export interface CollectionLimitValue { - field: CollectionLimitField, - value: bigint, -} - -export enum CollectionMode { - Nonfungible, - Fungible, - Refungible, -} - -export interface PropertyPermission { - code: TokenPermissionField, - value: boolean, -} -export interface TokenPropertyPermission { - key: string, - permissions: PropertyPermission[], -} -export interface CollectionNestingAndPermission { - token_owner: boolean, - collection_admin: boolean, - restricted: string[], -} - -export const emptyAddress: [string, string] = [ - '0x0000000000000000000000000000000000000000', - '0', -]; - -export const CREATE_COLLECTION_DATA_DEFAULTS = { - decimals: 0, - properties: [], - tokenPropertyPermissions: [], - adminList: [], - nestingSettings: {token_owner: false, collection_admin: false, restricted: []}, - limits: [], - pendingSponsor: emptyAddress, - flags: 0, -}; - -export interface Property { - key: string; - value: Buffer; -} - -export class CreateCollectionData { - name: string; - description: string; - tokenPrefix: string; - collectionMode: TCollectionMode; - decimals? = 0; - properties?: Property[] = []; - tokenPropertyPermissions?: TokenPropertyPermission[] = []; - adminList?: CrossAddress[] = []; - nestingSettings?: CollectionNestingAndPermission = {token_owner: false, collection_admin: false, restricted: []}; - limits?: CollectionLimitValue[] = []; - pendingSponsor?: [string, string] = emptyAddress; - flags?: number | CollectionFlag[] = [0]; - - constructor( - name: string, - description: string, - tokenPrefix: string, - collectionMode: TCollectionMode, - decimals = 18, - ) { - this.name = name; - this.description = description; - this.tokenPrefix = tokenPrefix; - this.collectionMode = collectionMode; - if(collectionMode == 'ft') - this.decimals = decimals; - else - this.decimals = 0; - } -} --- a/js-packages/tests/src/eth/util/playgrounds/unique.dev.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -declare module 'solc'; \ No newline at end of file --- a/js-packages/tests/src/eth/util/playgrounds/unique.dev.ts +++ /dev/null @@ -1,617 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -/* eslint-disable function-call-argument-newline */ -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// - -import {readFile} from 'fs/promises'; - -import Web3 from 'web3'; -import {WebsocketProvider} from 'web3-core'; -import {Contract} from 'web3-eth-contract'; - -import solc from 'solc'; - -import {evmToAddress} from '@polkadot/util-crypto'; -import type {IKeyringPair} from '@polkadot/types/types'; - -import {ArrangeGroup, DevUniqueHelper} from '@unique/playgrounds/src/unique.dev.js'; - -import type {ContractImports, CompiledContract, CrossAddress, NormalizedEvent, EthProperty} from './types.js'; -import {CollectionMode, CreateCollectionData} from './types.js'; - -// Native contracts ABI -import collectionHelpersAbi from '../../abi/collectionHelpers.json' assert {type: 'json'}; -import nativeFungibleAbi from '../../abi/nativeFungible.json' assert {type: 'json'}; -import fungibleAbi from '../../abi/fungible.json' assert {type: 'json'}; -import fungibleDeprecatedAbi from '../../abi/fungibleDeprecated.json' assert {type: 'json'}; -import nonFungibleAbi from '../../abi/nonFungible.json' assert {type: 'json'}; -import nonFungibleDeprecatedAbi from '../../abi/nonFungibleDeprecated.json' assert {type: 'json'}; -import refungibleAbi from '../../abi/reFungible.json' assert {type: 'json'}; -import refungibleDeprecatedAbi from '../../abi/reFungibleDeprecated.json' assert {type: 'json'}; -import refungibleTokenAbi from '../../abi/reFungibleToken.json' assert {type: 'json'}; -import refungibleTokenDeprecatedAbi from '../../abi/reFungibleTokenDeprecated.json' assert {type: 'json'}; -import contractHelpersAbi from '../../abi/contractHelpers.json' assert {type: 'json'}; -import type {ICrossAccountId, TEthereumAccount, TCollectionMode} from '@unique/playgrounds/src/types.js'; - -class EthGroupBase { - helper: EthUniqueHelper; - gasPrice?: string; - - constructor(helper: EthUniqueHelper) { - this.helper = helper; - } - async getGasPrice() { - if(this.gasPrice) - return this.gasPrice; - this.gasPrice = await this.helper.getWeb3().eth.getGasPrice(); - return this.gasPrice; - } -} - -class ContractGroup extends EthGroupBase { - async findImports(imports?: ContractImports[]) { - if(!imports) return function(path: string) { - return {error: `File not found: ${path}`}; - }; - - const knownImports = {} as { [key: string]: string }; - for(const imp of imports) { - knownImports[imp.solPath] = (await readFile(imp.fsPath)).toString(); - } - - return function(path: string) { - if(path in knownImports) return {contents: knownImports[path]}; - return {error: `File not found: ${path}`}; - }; - } - - async compile(name: string, src: string, imports?: ContractImports[]): Promise { - const compiled = JSON.parse(solc.compile(JSON.stringify({ - language: 'Solidity', - sources: { - [`${name}.sol`]: { - content: src, - }, - }, - settings: { - outputSelection: { - '*': { - '*': ['*'], - }, - }, - }, - }), {import: await this.findImports(imports)})); - - const hasErrors = compiled['errors'] - && compiled['errors'].length > 0 - && compiled.errors.some(function(err: any) { - return err.severity == 'error'; - }); - - if(hasErrors) { - throw compiled.errors; - } - const out = compiled.contracts[`${name}.sol`][name]; - - return { - abi: out.abi, - object: '0x' + out.evm.bytecode.object, - }; - } - - async deployByCode(signer: string, name: string, src: string, imports?: ContractImports[], gas?: number, args?: any[]): Promise { - const compiledContract = await this.compile(name, src, imports); - return this.deployByAbi(signer, compiledContract.abi, compiledContract.object, gas, args); - } - - async deployByAbi(signer: string, abi: any, object: string, gas?: number, args?: any[]): Promise { - const web3 = this.helper.getWeb3(); - const contract = new web3.eth.Contract(abi, undefined, { - data: object, - from: signer, - gas: gas ?? this.helper.eth.DEFAULT_GAS, - }); - return await contract.deploy({data: object, arguments: args}).send({from: signer}); - } - -} - -class NativeContractGroup extends EthGroupBase { - - contractHelpers(caller: string) { - const web3 = this.helper.getWeb3(); - return new web3.eth.Contract(contractHelpersAbi as any, this.helper.getApi().consts.evmContractHelpers.contractAddress.toString(), { - from: caller, - gas: this.helper.eth.DEFAULT_GAS, - }); - } - - collectionHelpers(caller: string) { - const web3 = this.helper.getWeb3(); - return new web3.eth.Contract(collectionHelpersAbi as any, this.helper.getApi().consts.common.contractAddress.toString(), { - from: caller, - gas: this.helper.eth.DEFAULT_GAS, - }); - } - - collection(address: string, mode: TCollectionMode, caller?: string, mergeDeprecated = false) { - let abi; - if(address === this.helper.ethAddress.fromCollectionId(0)) { - abi = nativeFungibleAbi; - } else { - abi ={ - 'nft': nonFungibleAbi, - 'rft': refungibleAbi, - 'ft': fungibleAbi, - }[mode]; - } - if(mergeDeprecated) { - const deprecated = { - 'nft': nonFungibleDeprecatedAbi, - 'rft': refungibleDeprecatedAbi, - 'ft': fungibleDeprecatedAbi, - }[mode]; - abi = [...abi, ...deprecated]; - } - const web3 = this.helper.getWeb3(); - return new web3.eth.Contract(abi as any, address, { - gas: this.helper.eth.DEFAULT_GAS, - ...(caller ? {from: caller} : {}), - }); - } - - collectionById(collectionId: number, mode: 'nft' | 'rft' | 'ft', caller?: string, mergeDeprecated = false) { - return this.collection(this.helper.ethAddress.fromCollectionId(collectionId), mode, caller, mergeDeprecated); - } - - rftToken(address: string, caller?: string, mergeDeprecated = false) { - const web3 = this.helper.getWeb3(); - const abi = mergeDeprecated ? [...refungibleTokenAbi, ...refungibleTokenDeprecatedAbi] : refungibleTokenAbi; - return new web3.eth.Contract(abi as any, address, { - gas: this.helper.eth.DEFAULT_GAS, - ...(caller ? {from: caller} : {}), - }); - } - - rftTokenById(collectionId: number, tokenId: number, caller?: string, mergeDeprecated = false) { - return this.rftToken(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller, mergeDeprecated); - } -} - -class CreateCollectionTransaction { - signer: string; - data: CreateCollectionData; - mergeDeprecated: boolean; - helper: EthUniqueHelper; - - constructor(helper: EthUniqueHelper, signer: string, data: CreateCollectionData, mergeDeprecated = false) { - this.helper = helper; - this.signer = signer; - - let flags = 0; - // convert CollectionFlags to number and join them in one number - if(!data.flags) { - flags = 0; - } else if(typeof data.flags == 'number') { - flags = data.flags; - } else { - for(let i = 0; i < data.flags.length; i++){ - const flag = data.flags[i]; - flags = flags | flag; - } - } - data.flags = flags; - - this.data = data; - this.mergeDeprecated = mergeDeprecated; - } - - // eslint-disable-next-line require-await - private async createTransaction() { - const collectionHelper = this.helper.ethNativeContract.collectionHelpers(this.signer); - let collectionMode; - switch (this.data.collectionMode) { - case 'nft': collectionMode = CollectionMode.Nonfungible; break; - case 'rft': collectionMode = CollectionMode.Refungible; break; - case 'ft': collectionMode = CollectionMode.Fungible; break; - } - - const tx = collectionHelper.methods.createCollection([ - this.data.name, - this.data.description, - this.data.tokenPrefix, - collectionMode, - this.data.decimals, - this.data.properties, - this.data.tokenPropertyPermissions, - this.data.adminList, - this.data.nestingSettings, - this.data.limits, - this.data.pendingSponsor, - this.data.flags, - ]); - return tx; - } - - async send(options?: any): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[], collection: Contract }> { - const collectionCreationPrice = { - value: Number(this.helper.balance.getCollectionCreationPrice()), - }; - const tx = await this.createTransaction(); - const result = await tx.send({...options, ...collectionCreationPrice}); - - const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); - const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress); - const events = this.helper.eth.normalizeEvents(result.events); - const collection = await this.helper.ethNativeContract.collectionById(collectionId, this.data.collectionMode, this.signer, this.mergeDeprecated); - - return {collectionId, collectionAddress, events, collection}; - } - - async call(options?: any) { - const collectionCreationPrice = { - value: Number(this.helper.balance.getCollectionCreationPrice()), - }; - const tx = await this.createTransaction(); - - return await tx.call({...options, ...collectionCreationPrice}); - } -} - - -class EthGroup extends EthGroupBase { - DEFAULT_GAS = 2_500_000; - - createAccount() { - const web3 = this.helper.getWeb3(); - const account = web3.eth.accounts.create(); - web3.eth.accounts.wallet.add(account.privateKey); - return account.address; - } - - async createAccountWithBalance(donor: IKeyringPair, amount = 600n) { - const account = this.createAccount(); - await this.transferBalanceFromSubstrate(donor, account, amount); - - return account; - } - - async transferBalanceFromSubstrate(donor: IKeyringPair, recepient: string, amount = 100n, inTokens = true) { - return await this.helper.balance.transferToSubstrate(donor, evmToAddress(recepient), amount * (inTokens ? this.helper.balance.getOneTokenNominal() : 1n)); - } - - async getCollectionCreationFee(signer: string) { - const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer); - return await collectionHelper.methods.collectionCreationFee().call(); - } - - async sendEVM(signer: IKeyringPair, contractAddress: string, abi: string, value: string, gasLimit?: number) { - if(!gasLimit) gasLimit = this.DEFAULT_GAS; - const web3 = this.helper.getWeb3(); - // FIXME: can't send legacy transaction using tx.evm.call - const gasPrice = await web3.eth.getGasPrice(); - // TODO: check execution status - await this.helper.executeExtrinsic( - signer, - 'api.tx.evm.call', [this.helper.address.substrateToEth(signer.address), contractAddress, abi, value, gasLimit, gasPrice, null, null, []], - true, - ); - } - - async callEVM(signer: TEthereumAccount, contractAddress: string, abi: string) { - return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]); - } - - createCollectionMethodName(mode: TCollectionMode) { - switch (mode) { - case 'ft': - return 'createFTCollection'; - case 'nft': - return 'createNFTCollection'; - case 'rft': - return 'createRFTCollection'; - } - } - - createCollection(signer: string, data: CreateCollectionData, mergeDeprecated = false): CreateCollectionTransaction { - return new CreateCollectionTransaction(this.helper, signer, data, mergeDeprecated); - } - - createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { - return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send(); - } - - async createERC721MetadataCompatibleCollection(signer: string, data: CreateCollectionData, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { - const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer); - - const {collectionId, collectionAddress, events} = await this.createCollection(signer, data).send(); - - await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send(); - - return {collectionId, collectionAddress, events}; - } - - async createERC721MetadataCompatibleNFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { - const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer); - - const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send(); - - await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send(); - - return {collectionId, collectionAddress, events}; - } - - createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { - return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send(); - } - - createFungibleCollection(signer: string, name: string, _decimals: number, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { - return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'ft')).send(); - } - - async createERC721MetadataCompatibleRFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { - const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer); - - const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send(); - - await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send(); - - return {collectionId, collectionAddress, events}; - } - - async deployCollectorContract(signer: string): Promise { - return await this.helper.ethContract.deployByCode(signer, 'Collector', ` - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.6; - - contract Collector { - uint256 collected; - fallback() external payable { - giveMoney(); - } - function giveMoney() public payable { - collected += msg.value; - } - function getCollected() public view returns (uint256) { - return collected; - } - function getUnaccounted() public view returns (uint256) { - return address(this).balance - collected; - } - - function withdraw(address payable target) public { - target.transfer(collected); - collected = 0; - } - } - `); - } - - async deployFlipper(signer: string): Promise { - return await this.helper.ethContract.deployByCode(signer, 'Flipper', ` - // SPDX-License-Identifier: UNLICENSED - pragma solidity ^0.8.6; - - contract Flipper { - bool value = false; - function flip() public { - value = !value; - } - function getValue() public view returns (bool) { - return value; - } - } - `); - } - - async recordCallFee(user: string, call: () => Promise): Promise { - const before = await this.helper.balance.getEthereum(user); - await call(); - // In dev mode, the transaction might not finish processing in time - await this.helper.wait.newBlocks(1); - const after = await this.helper.balance.getEthereum(user); - - return before - after; - } - - normalizeEvents(events: any): NormalizedEvent[] { - const output = []; - for(const key of Object.keys(events)) { - if(key.match(/^[0-9]+$/)) { - output.push(events[key]); - } else if(Array.isArray(events[key])) { - output.push(...events[key]); - } else { - output.push(events[key]); - } - } - output.sort((a, b) => a.logIndex - b.logIndex); - return output.map(({address, event, returnValues}) => { - const args: { [key: string]: string } = {}; - for(const key of Object.keys(returnValues)) { - if(!key.match(/^[0-9]+$/)) { - args[key] = returnValues[key]; - } - } - return { - address, - event, - args, - }; - }); - } - - async calculateFee(address: ICrossAccountId, code: () => Promise): Promise { - const wrappedCode = async () => { - await code(); - // In dev mode, the transaction might not finish processing in time - await this.helper.wait.newBlocks(1); - }; - return await this.helper.arrange.calculcateFee(address, wrappedCode); - } -} - -class EthAddressGroup extends EthGroupBase { - extractCollectionId(address: string): number { - if(!(address.length === 42 || address.length === 40)) throw new Error('address wrong format'); - return parseInt(address.slice(address.length - 8), 16); - } - - fromCollectionId(collectionId: number): string { - if(collectionId >= 0xffffffff || collectionId < 0) throw new Error('collectionId overflow'); - return (Web3 as any).utils.toChecksumAddress(`0x17c4e6453cc49aaaaeaca894e6d9683e${collectionId.toString(16).padStart(8, '0')}`); - } - - extractTokenId(address: string): { collectionId: number, tokenId: number } { - if(!address.startsWith('0x')) - throw 'address not starts with "0x"'; - if(address.length > 42) - throw 'address length is more than 20 bytes'; - return { - collectionId: Number('0x' + address.substring(address.length - 16, address.length - 8)), - tokenId: Number('0x' + address.substring(address.length - 8)), - }; - } - - fromTokenId(collectionId: number, tokenId: number): string { - return this.helper.util.getTokenAddress({collectionId, tokenId}); - } - - normalizeAddress(address: string): string { - return '0x' + address.substring(address.length - 40); - } -} -export class EthPropertyGroup extends EthGroupBase { - property(key: string, value: string): EthProperty { - return [ - key, - '0x' + Buffer.from(value).toString('hex'), - ]; - } -} -export type EthUniqueHelperConstructor = new (...args: any[]) => EthUniqueHelper; - -export class EthCrossAccountGroup extends EthGroupBase { - createAccount(): CrossAddress { - return this.fromAddress(this.helper.eth.createAccount()); - } - - async createAccountWithBalance(donor: IKeyringPair, amount = 100n) { - return this.fromAddress(await this.helper.eth.createAccountWithBalance(donor, amount)); - } - - fromAddress(address: TEthereumAccount): CrossAddress { - return { - eth: address, - sub: '0', - }; - } - - fromAddr(address: TEthereumAccount): [string, string] { - return [ - address, - '0', - ]; - } - - fromKeyringPair(keyring: IKeyringPair): CrossAddress { - return { - eth: '0x0000000000000000000000000000000000000000', - sub: keyring.addressRaw, - }; - } -} - -export class FeeGas { - fee: number | bigint = 0n; - - gas: number | bigint = 0n; - - public static async build(helper: EthUniqueHelper, fee: bigint): Promise { - const instance = new FeeGas(); - instance.fee = instance.convertToTokens(fee); - instance.gas = await instance.convertToGas(fee, helper); - return instance; - } - - private async convertToGas(fee: bigint, helper: EthUniqueHelper): Promise { - const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice()); - return fee / gasPrice; - } - - private convertToTokens(value: bigint, nominal = 1_000_000_000_000_000_000n): number { - return Number((value * 1000n) / nominal) / 1000; - } -} - -class EthArrangeGroup extends ArrangeGroup { - declare helper: EthUniqueHelper; - - constructor(helper: EthUniqueHelper) { - super(helper); - this.helper = helper; - } - - async calculcateFeeGas(payer: ICrossAccountId, promise: () => Promise): Promise { - const fee = await this.calculcateFee(payer, promise); - return await FeeGas.build(this.helper, fee); - } -} -export class EthUniqueHelper extends DevUniqueHelper { - web3: Web3.default | null = null; - web3Provider: WebsocketProvider | null = null; - - eth: EthGroup; - ethAddress: EthAddressGroup; - ethCrossAccount: EthCrossAccountGroup; - ethNativeContract: NativeContractGroup; - ethContract: ContractGroup; - ethProperty: EthPropertyGroup; - declare arrange: EthArrangeGroup; - constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: { [key: string]: any } = {}) { - options.helperBase = options.helperBase ?? EthUniqueHelper; - - super(logger, options); - this.eth = new EthGroup(this); - this.ethAddress = new EthAddressGroup(this); - this.ethCrossAccount = new EthCrossAccountGroup(this); - this.ethNativeContract = new NativeContractGroup(this); - this.ethContract = new ContractGroup(this); - this.ethProperty = new EthPropertyGroup(this); - this.arrange = new EthArrangeGroup(this); - super.arrange = this.arrange; - } - - getWeb3(): Web3.default { - if(this.web3 === null) throw Error('Web3 not connected'); - return this.web3; - } - - connectWeb3(wsEndpoint: string) { - if(this.web3 !== null) return; - this.web3Provider = new (Web3 as any).providers.WebsocketProvider(wsEndpoint); - this.web3 = new (Web3 as any)(this.web3Provider); - } - - override async disconnect() { - if(this.web3 === null) return; - this.web3Provider?.connection.close(); - - await super.disconnect(); - } - - override clearApi() { - super.clearApi(); - this.web3 = null; - } - - override clone(helperCls: EthUniqueHelperConstructor, options?: { [key: string]: any; }): EthUniqueHelper { - const newHelper = super.clone(helperCls, options) as EthUniqueHelper; - newHelper.web3 = this.web3; - newHelper.web3Provider = this.web3Provider; - - return newHelper; - } -} --- a/js-packages/tests/src/fungible.test.ts +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect, requirePalletsOrSkip, Pallets} from './util/index.js'; - -const U128_MAX = (1n << 128n) - 1n; - -describe('integration test: Fungible functionality:', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); - }); - }); - - itSub('Create fungible collection and token', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'trest'}); - const defaultTokenId = await collection.getLastTokenId(); - expect(defaultTokenId).to.be.equal(0); - - await collection.mint(alice, U128_MAX); - const aliceBalance = await collection.getBalance({Substrate: alice.address}); - const itemCountAfter = await collection.getLastTokenId(); - - expect(itemCountAfter).to.be.equal(defaultTokenId); - expect(aliceBalance).to.be.equal(U128_MAX); - }); - - itSub('RPC method tokenOnewrs for fungible collection and token', async ({helper}) => { - const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; - const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => ({Substrate: keyring.address})); - - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - await collection.mint(alice, U128_MAX); - - await collection.transfer(alice, {Substrate: bob.address}, 1000n); - await collection.transfer(alice, ethAcc, 900n); - - for(let i = 0; i < 7; i++) { - await collection.transfer(alice, facelessCrowd[i], 1n); - } - - const owners = await collection.getTop10Owners(); - - // What to expect - expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]); - expect(owners.length).to.be.equal(10); - - const [eleven] = await helper.arrange.createAccounts([0n], donor); - expect(await collection.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true; - expect((await collection.getTop10Owners()).length).to.be.equal(10); - }); - - itSub('Transfer token', async ({helper}) => { - const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - await collection.mint(alice, 500n); - - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n); - expect(await collection.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true; - expect(await collection.transfer(alice, ethAcc, 140n)).to.be.true; - - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(300n); - expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(60n); - expect(await collection.getBalance(ethAcc)).to.be.equal(140n); - - await expect(collection.transfer(alice, {Substrate: bob.address}, 350n)).to.eventually.be.rejectedWith(/common\.TokenValueTooLow/); - }); - - itSub('Tokens multiple creation', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - await collection.mintWithOneOwner(alice, [ - {value: 500n}, - {value: 400n}, - {value: 300n}, - ]); - - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1200n); - }); - - itSub('Burn some tokens ', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - await collection.mint(alice, 500n); - - expect(await collection.doesTokenExist(0)).to.be.true; - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n); - expect(await collection.burnTokens(alice, 499n)).to.be.true; - expect(await collection.doesTokenExist(0)).to.be.true; - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n); - }); - - itSub('Burn all tokens ', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - await collection.mint(alice, 500n); - - expect(await collection.doesTokenExist(0)).to.be.true; - expect(await collection.burnTokens(alice, 500n)).to.be.true; - expect(await collection.doesTokenExist(0)).to.be.true; - - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(0n); - expect(await collection.getTotalPieces()).to.be.equal(0n); - }); - - itSub('Set allowance for token', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; - await collection.mint(alice, 100n); - - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(100n); - - expect(await collection.approveTokens(alice, {Substrate: bob.address}, 60n)).to.be.true; - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n); - expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(0n); - - expect(await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true; - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(80n); - expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(20n); - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n); - - await collection.burnTokensFrom(bob, {Substrate: alice.address}, 10n); - - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(70n); - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(30n); - expect(await collection.transferFrom(bob, {Substrate: alice.address}, ethAcc, 10n)).to.be.true; - expect(await collection.getBalance(ethAcc)).to.be.equal(10n); - }); -}); - -describe('Fungible negative tests', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.Fungible]); - - donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('Cannot transfer incorrect amount of tokens', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const nonExistingCollection = helper.ft.getCollectionObject(99999); - await collection.mint(alice, 10n, {Substrate: bob.address}); - - // 1. Alice cannot transfer more than 0 tokens if balance low: - await expect(collection.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow'); - await expect(collection.transfer(alice, {Substrate: charlie.address}, 100n)).to.be.rejectedWith('common.TokenValueTooLow'); - - // 2. Alice cannot transfer non-existing token: - await expect(nonExistingCollection.transfer(alice, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.CollectionNotFound'); - await expect(nonExistingCollection.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.CollectionNotFound'); - - // 3. Zero transfer allowed (EIP-20): - await collection.transfer(bob, {Substrate: charlie.address}, 0n); - // 3.1 even if the balance = 0 - await collection.transfer(alice, {Substrate: charlie.address}, 0n); - - expect(await collection.getBalance({Substrate: alice.address})).to.eq(0n); - expect(await collection.getBalance({Substrate: bob.address})).to.eq(10n); - expect(await collection.getBalance({Substrate: charlie.address})).to.eq(0n); - }); -}); --- a/js-packages/tests/src/getPropertiesRpc.test.ts +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from './util/index.js'; -import {UniqueHelper, UniqueNFTCollection} from '@unique/playgrounds/src/unique.js'; - -const collectionProps = [ - {key: 'col-0', value: 'col-0-value'}, - {key: 'col-1', value: 'col-1-value'}, -]; - -const tokenProps = [ - {key: 'tok-0', value: 'tok-0-value'}, - {key: 'tok-1', value: 'tok-1-value'}, -]; - -const tokPropPermission = { - mutable: false, - tokenOwner: true, - collectionAdmin: false, -}; - -const tokenPropPermissions = [ - { - key: 'tok-0', - permission: tokPropPermission, - }, - { - key: 'tok-1', - permission: tokPropPermission, - }, -]; - -describe('query properties RPC', () => { - let alice: IKeyringPair; - - const mintCollection = async (helper: UniqueHelper) => await helper.nft.mintCollection(alice, { - tokenPrefix: 'prps', - properties: collectionProps, - tokenPropertyPermissions: tokenPropPermissions, - }); - - const mintToken = async (collection: UniqueNFTCollection) => await collection.mintToken(alice, {Substrate: alice.address}, tokenProps); - - - before(async () => { - await usingPlaygrounds(async (_, privateKey) => { - alice = await privateKey({url: import.meta.url}); - }); - }); - - itSub('query empty collection key set', async ({helper}) => { - const collection = await mintCollection(helper); - const props = await collection.getProperties([]); - expect(props).to.be.empty; - }); - - itSub('query empty token key set', async ({helper}) => { - const collection = await mintCollection(helper); - const token = await mintToken(collection); - const props = await token.getProperties([]); - expect(props).to.be.empty; - }); - - itSub('query empty token key permissions set', async ({helper}) => { - const collection = await mintCollection(helper); - const propPermissions = await collection.getPropertyPermissions([]); - expect(propPermissions).to.be.empty; - }); - - itSub('query all collection props by null arg', async ({helper}) => { - const collection = await mintCollection(helper); - const props = await collection.getProperties(null); - expect(props).to.be.deep.equal(collectionProps); - }); - - itSub('query all token props by null arg', async ({helper}) => { - const collection = await mintCollection(helper); - const token = await mintToken(collection); - const props = await token.getProperties(null); - expect(props).to.be.deep.equal(tokenProps); - }); - - itSub('query empty token key permissions by null arg', async ({helper}) => { - const collection = await mintCollection(helper); - const propPermissions = await collection.getPropertyPermissions(null); - expect(propPermissions).to.be.deep.equal(tokenPropPermissions); - }); - - itSub('query all collection props by undefined arg', async ({helper}) => { - const collection = await mintCollection(helper); - const props = await collection.getProperties(); - expect(props).to.be.deep.equal(collectionProps); - }); - - itSub('query all token props by undefined arg', async ({helper}) => { - const collection = await mintCollection(helper); - const token = await mintToken(collection); - const props = await token.getProperties(); - expect(props).to.be.deep.equal(tokenProps); - }); - - itSub('query empty token key permissions by undefined arg', async ({helper}) => { - const collection = await mintCollection(helper); - const propPermissions = await collection.getPropertyPermissions(); - expect(propPermissions).to.be.deep.equal(tokenPropPermissions); - }); -}); - -[ - {mode: 'nft' as const}, - {mode: 'rft' as const}, -].map(testCase => - describe('negative properties', () => { - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (_, privateKey) => { - alice = await privateKey({url: import.meta.url}); - }); - }); - - itSub(`[${testCase.mode}] set token property for non-existent token`, async ({helper}) => { - const collection = await helper[testCase.mode].mintCollection(alice); - await collection.setTokenPropertyPermissions(alice, [{key: 'key', permission: {mutable: true, tokenOwner: true, collectionAdmin: true}}]); - await expect(collection.setTokenProperties(alice, 1, [{key: 'key', value: 'value'}])).to.be.rejectedWith('common.TokenNotFound'); - expect(await collection.getTokenProperties(1, ['key'])).to.be.empty; - }); - - itSub(`[${testCase.mode}] delete token property for non-existent token`, async ({helper}) => { - const collection = await helper[testCase.mode].mintCollection(alice); - await collection.setTokenPropertyPermissions(alice, [{key: 'key', permission: {mutable: true, tokenOwner: true, collectionAdmin: true}}]); - await expect(collection.deleteTokenProperties(alice, 1, ['key'])).to.be.rejectedWith('common.TokenNotFound'); - expect(await collection.getTokenProperties(1, ['key'])).to.be.empty; - }); - })); \ No newline at end of file --- a/js-packages/tests/src/governance/council.test.ts +++ /dev/null @@ -1,457 +0,0 @@ - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js'; -import {Event} from '@unique/playgrounds/src/unique.dev.js'; -import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, councilMotionDuration, democracyFastTrackVotingPeriod, fellowshipRankLimit, clearCouncil, clearTechComm, initTechComm, clearFellowship, dummyProposal, dummyProposalCall, initFellowship, defaultEnactmentMoment, fellowshipPropositionOrigin} from './util.js'; -import type {ICounselors} from './util.js'; - -describeGov('Governance: Council tests', () => { - let donor: IKeyringPair; - let counselors: ICounselors; - let sudoer: IKeyringPair; - - const moreThanHalfCouncilThreshold = 3; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.Council]); - - donor = await privateKey({url: import.meta.url}); - sudoer = await privateKey('//Alice'); - }); - }); - - beforeEach(async () => { - counselors = await initCouncil(donor, sudoer); - }); - - afterEach(async () => { - await clearCouncil(sudoer); - await clearTechComm(sudoer); - }); - - async function proposalFromMoreThanHalfCouncil(proposal: any) { - return await usingPlaygrounds(async (helper) => { - expect((await helper.callRpc('api.query.councilMembership.members')).toJSON().length).to.be.equal(5); - const proposeResult = await helper.council.collective.propose( - counselors.filip, - proposal, - moreThanHalfCouncilThreshold, - ); - - const councilProposedEvent = Event.Council.Proposed.expect(proposeResult); - const proposalIndex = councilProposedEvent.proposalIndex; - const proposalHash = councilProposedEvent.proposalHash; - - - await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true); - await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true); - await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true); - - return await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex); - }); - } - - async function proposalFromAllCouncil(proposal: any) { - return await usingPlaygrounds(async (helper) => { - expect((await helper.callRpc('api.query.councilMembership.members')).toJSON().length).to.be.equal(5); - const proposeResult = await helper.council.collective.propose( - counselors.filip, - proposal, - moreThanHalfCouncilThreshold, - ); - - const councilProposedEvent = Event.Council.Proposed.expect(proposeResult); - const proposalIndex = councilProposedEvent.proposalIndex; - const proposalHash = councilProposedEvent.proposalHash; - - - await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true); - await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true); - await helper.council.collective.vote(counselors.ildar, proposalHash, proposalIndex, true); - await helper.council.collective.vote(counselors.irina, proposalHash, proposalIndex, true); - await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true); - - return await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex); - }); - } - - itSub('>50% of Council can externally propose SuperMajorityAgainst', async ({helper}) => { - const forceSetBalanceReceiver = helper.arrange.createEmptyAccount(); - const forceSetBalanceTestValue = 20n * 10n ** 25n; - - const democracyProposal = await helper.constructApiCall('api.tx.balances.forceSetBalance', [ - forceSetBalanceReceiver.address, forceSetBalanceTestValue, - ]); - - const councilProposal = await helper.democracy.externalProposeDefaultCall(democracyProposal); - - const proposeResult = await helper.council.collective.propose( - counselors.filip, - councilProposal, - moreThanHalfCouncilThreshold, - ); - - const councilProposedEvent = Event.Council.Proposed.expect(proposeResult); - const proposalIndex = councilProposedEvent.proposalIndex; - const proposalHash = councilProposedEvent.proposalHash; - - await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true); - await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true); - await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true); - - await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex); - - const democracyStartedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); - const democracyReferendumIndex = democracyStartedEvent.referendumIndex; - const democracyThreshold = democracyStartedEvent.threshold; - - expect(democracyThreshold).to.be.equal('SuperMajorityAgainst'); - - await helper.democracy.vote(counselors.filip, democracyReferendumIndex, { - Standard: { - vote: { - aye: true, - conviction: 1, - }, - balance: 10_000n, - }, - }); - - await helper.democracy.vote(counselors.charu, democracyReferendumIndex, { - Standard: { - vote: { - aye: false, - conviction: 1, - }, - balance: 50_000n, - }, - }); - - const passedReferendumEvent = await helper.wait.expectEvent(democracyVotingPeriod, Event.Democracy.Passed); - expect(passedReferendumEvent.referendumIndex).to.be.equal(democracyReferendumIndex); - - await helper.wait.expectEvent(democracyEnactmentPeriod, Event.Scheduler.Dispatched); - const receiverBalance = await helper.balance.getSubstrate(forceSetBalanceReceiver.address); - expect(receiverBalance).to.be.equal(forceSetBalanceTestValue); - }); - - itSub('Council prime member vote is the default', async ({helper}) => { - const newTechCommMember = helper.arrange.createEmptyAccount(); - const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address); - const proposeResult = await helper.council.collective.propose( - counselors.filip, - addMemberProposal, - moreThanHalfCouncilThreshold, - ); - - const councilProposedEvent = Event.Council.Proposed.expect(proposeResult); - const proposalIndex = councilProposedEvent.proposalIndex; - const proposalHash = councilProposedEvent.proposalHash; - - await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true); - - await helper.wait.newBlocks(councilMotionDuration); - const closeResult = await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex); - const closeEvent = Event.Council.Closed.expect(closeResult); - const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as string[]; - expect(closeEvent.yes).to.be.equal(members.length); - }); - - itSub('Superuser can add a member', async ({helper}) => { - const newMember = helper.arrange.createEmptyAccount(); - await expect(helper.getSudo().council.membership.addMember(sudoer, newMember.address)).to.be.fulfilled; - - const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON(); - expect(members).to.contains(newMember.address); - }); - - itSub('Superuser can remove a member', async ({helper}) => { - await expect(helper.getSudo().council.membership.removeMember(sudoer, counselors.alex.address)).to.be.fulfilled; - - const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON(); - expect(members).to.not.contains(counselors.alex.address); - }); - - itSub('>50% Council can add TechComm member', async ({helper}) => { - const newTechCommMember = helper.arrange.createEmptyAccount(); - const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address); - - await proposalFromMoreThanHalfCouncil(addMemberProposal); - - const techCommMembers = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON(); - expect(techCommMembers).to.contains(newTechCommMember.address); - }); - - itSub('Council can remove TechComm member', async ({helper}) => { - const techComm = await initTechComm(donor, sudoer); - const removeMemberPrpoposal = helper.technicalCommittee.membership.removeMemberCall(techComm.andy.address); - await proposalFromMoreThanHalfCouncil(removeMemberPrpoposal); - - const techCommMembers = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON(); - expect(techCommMembers).to.not.contains(techComm.andy.address); - }); - - itSub.skip('Council member can add Fellowship member', async ({helper}) => { - const newFellowshipMember = helper.arrange.createEmptyAccount(); - await expect(helper.council.collective.execute( - counselors.alex, - helper.fellowship.collective.addMemberCall(newFellowshipMember.address), - )).to.be.fulfilled; - const fellowshipMembers = (await helper.callRpc('api.query.fellowshipCollective.members')).toJSON(); - expect(fellowshipMembers).to.contains(newFellowshipMember.address); - }); - - itSub('>50% Council can promote Fellowship member', async ({helper}) => { - const fellowship = await initFellowship(donor, sudoer); - const memberWithZeroRank = fellowship[0][0]; - - const proposal = helper.fellowship.collective.promoteCall(memberWithZeroRank.address); - await proposalFromMoreThanHalfCouncil(proposal); - const record = (await helper.callRpc('api.query.fellowshipCollective.members', [memberWithZeroRank.address])).toJSON(); - expect(record).to.be.deep.equal({rank: 1}); - - await clearFellowship(sudoer); - }); - - itSub('>50% Council can demote Fellowship member', async ({helper}) => { - const fellowship = await initFellowship(donor, sudoer); - const memberWithRankOne = fellowship[1][0]; - - const proposal = helper.fellowship.collective.demoteCall(memberWithRankOne.address); - await proposalFromMoreThanHalfCouncil(proposal); - - const record = (await helper.callRpc('api.query.fellowshipCollective.members', [memberWithRankOne.address])).toJSON(); - expect(record).to.be.deep.equal({rank: 0}); - - await clearFellowship(sudoer); - }); - - itSub('>50% Council can add\remove Fellowship member', async ({helper}) => { - try { - const newMember = helper.arrange.createEmptyAccount(); - - const proposalAdd = helper.fellowship.collective.addMemberCall(newMember.address); - const proposalRemove = helper.fellowship.collective.removeMemberCall(newMember.address, fellowshipRankLimit); - await expect(proposalFromMoreThanHalfCouncil(proposalAdd)).to.be.fulfilled; - expect(await helper.fellowship.collective.getMembers()).to.be.deep.contain(newMember.address); - await expect(proposalFromMoreThanHalfCouncil(proposalRemove)).to.be.fulfilled; - expect(await helper.fellowship.collective.getMembers()).to.be.not.deep.contain(newMember.address); - } - finally { - await clearFellowship(sudoer); - } - }); - - itSub('Council can blacklist Democracy proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - await expect(proposalFromAllCouncil(helper.democracy.blacklistCall(preimageHash, null))).to.be.fulfilled; - }); - - itSub('Sudo can blacklist Democracy proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - await expect(helper.getSudo().democracy.blacklist(sudoer, preimageHash)).to.be.fulfilled; - }); - - itSub('[Negative] Council cannot add Council member', async ({helper}) => { - const newCouncilMember = helper.arrange.createEmptyAccount(); - const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address); - - await expect(proposalFromAllCouncil(addMemberProposal)).to.be.rejected; - }); - - itSub('[Negative] Council cannot remove Council member', async ({helper}) => { - const removeMemberProposal = helper.council.membership.removeMemberCall(counselors.alex.address); - - await expect(proposalFromAllCouncil(removeMemberProposal)).to.be.rejected; - }); - - itSub('[Negative] Council cannot submit regular democracy proposal', async ({helper}) => { - const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n); - - await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/); - }); - - itSub('[Negative] Council cannot externally propose SimpleMajority', async ({helper}) => { - const councilProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper)); - - await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/); - }); - - itSub('[Negative] Council cannot externally propose SuperMajorityApprove', async ({helper}) => { - const councilProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper)); - - await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/); - }); - - itSub('[Negative] Council member cannot add/remove a Council member', async ({helper}) => { - const newCouncilMember = helper.arrange.createEmptyAccount(); - await expect(helper.council.collective.execute( - counselors.alex, - helper.council.membership.addMemberCall(newCouncilMember.address), - )).to.be.rejectedWith('BadOrigin'); - await expect(helper.council.collective.execute( - counselors.alex, - helper.council.membership.removeMemberCall(counselors.charu.address), - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] Council cannot set/clear Council prime member', async ({helper}) => { - const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address); - const proposalForClear = await helper.council.membership.clearPrimeCall(); - - await expect(proposalFromAllCouncil(proposalForSet)).to.be.rejectedWith(/BadOrigin/); - await expect(proposalFromAllCouncil(proposalForClear)).to.be.rejectedWith(/BadOrigin/); - - }); - - itSub('[Negative] Council member cannot set/clear Council prime member', async ({helper}) => { - await expect(helper.council.collective.execute( - counselors.alex, - helper.council.membership.setPrimeCall(counselors.charu.address), - )).to.be.rejectedWith('BadOrigin'); - await expect(helper.council.collective.execute( - counselors.alex, - helper.council.membership.clearPrimeCall(), - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] Council member cannot add/remove a TechComm member', async ({helper}) => { - const newTechCommMember = helper.arrange.createEmptyAccount(); - await expect(helper.council.collective.execute( - counselors.alex, - helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address), - )).to.be.rejectedWith('BadOrigin'); - await expect(helper.council.collective.execute( - counselors.alex, - helper.technicalCommittee.membership.removeMemberCall(newTechCommMember.address), - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] Council member cannot promote/demote a Fellowship member', async ({helper}) => { - const fellowship = await initFellowship(donor, sudoer); - const memberWithRankOne = fellowship[1][0]; - - await expect(helper.council.collective.execute( - counselors.alex, - helper.fellowship.collective.promoteCall(memberWithRankOne.address), - )).to.be.rejectedWith('BadOrigin'); - await expect(helper.council.collective.execute( - counselors.alex, - helper.fellowship.collective.demoteCall(memberWithRankOne.address), - )).to.be.rejectedWith('BadOrigin'); - await clearFellowship(sudoer); - }); - - itSub('[Negative] Council cannot fast-track Democracy proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); - - await expect(proposalFromAllCouncil(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0))) - .to.be.rejectedWith(/BadOrigin/); - }); - - itSub('[Negative] Council member cannot fast-track Democracy proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); - - await expect(helper.council.collective.execute( - counselors.alex, - helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0), - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] Council cannot cancel Democracy proposals', async ({helper}) => { - const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n); - const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex; - - await expect(proposalFromAllCouncil(helper.democracy.cancelProposalCall(proposalIndex))) - .to.be.rejectedWith(/BadOrigin/); - }); - - itSub('[Negative] Council member cannot cancel Democracy proposals', async ({helper}) => { - - const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n); - const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex; - - await expect(helper.council.collective.execute( - counselors.alex, - helper.democracy.cancelProposalCall(proposalIndex), - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] Council cannot cancel ongoing Democracy referendums', async ({helper}) => { - await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper)); - const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); - const referendumIndex = startedEvent.referendumIndex; - - await expect(proposalFromAllCouncil(helper.democracy.emergencyCancelCall(referendumIndex))) - .to.be.rejectedWith(/BadOrigin/); - }); - - itSub('[Negative] Council member cannot cancel ongoing Democracy referendums', async ({helper}) => { - await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper)); - const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); - const referendumIndex = startedEvent.referendumIndex; - - await expect(helper.council.collective.execute( - counselors.alex, - helper.democracy.emergencyCancelCall(referendumIndex), - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] Council cannot cancel Fellowship referendums', async ({helper}) => { - const fellowship = await initFellowship(donor, sudoer); - const fellowshipProposer = fellowship[5][0]; - const proposal = dummyProposal(helper); - - const submitResult = await helper.fellowship.referenda.submit( - fellowshipProposer, - fellowshipPropositionOrigin, - proposal, - defaultEnactmentMoment, - ); - - const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; - - await expect(proposalFromAllCouncil(helper.fellowship.referenda.cancelCall(referendumIndex))) - .to.be.rejectedWith(/BadOrigin/); - }); - - itSub('[Negative] Council member cannot cancel Fellowship referendums', async ({helper}) => { - const fellowship = await initFellowship(donor, sudoer); - const fellowshipProposer = fellowship[5][0]; - const proposal = dummyProposal(helper); - - const submitResult = await helper.fellowship.referenda.submit( - fellowshipProposer, - fellowshipPropositionOrigin, - proposal, - defaultEnactmentMoment, - ); - const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; - await expect(helper.council.collective.execute( - counselors.alex, - helper.fellowship.referenda.cancelCall(referendumIndex), - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] Council referendum cannot be closed until the voting threshold is met', async ({helper}) => { - const councilSize = (await helper.callRpc('api.query.councilMembership.members')).toJSON().length as any as number; - expect(councilSize).is.greaterThan(1); - const proposeResult = await helper.council.collective.propose( - counselors.filip, - dummyProposalCall(helper), - councilSize, - ); - - const councilProposedEvent = Event.Council.Proposed.expect(proposeResult); - const proposalIndex = councilProposedEvent.proposalIndex; - const proposalHash = councilProposedEvent.proposalHash; - - - await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true); - await expect(helper.council.collective.close(counselors.filip, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly'); - }); - -}); --- a/js-packages/tests/src/governance/democracy.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js'; -import {clearFellowship, democracyLaunchPeriod, democracyTrackMinRank, dummyProposalCall, fellowshipConfirmPeriod, fellowshipMinEnactPeriod, fellowshipPreparePeriod, fellowshipPropositionOrigin, initFellowship, voteUnanimouslyInFellowship} from './util.js'; -import {Event} from '@unique/playgrounds/src/unique.dev.js'; - -describeGov('Governance: Democracy tests', () => { - let regularUser: IKeyringPair; - let donor: IKeyringPair; - let sudoer: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.Democracy]); - - donor = await privateKey({url: import.meta.url}); - sudoer = await privateKey('//Alice'); - - [regularUser] = await helper.arrange.createAccounts([1000n], donor); - }); - }); - - itSub('Regular user can vote', async ({helper}) => { - const fellows = await initFellowship(donor, sudoer); - const rank1Proposer = fellows[1][0]; - - const democracyProposalCall = dummyProposalCall(helper); - const fellowshipProposal = { - Inline: helper.democracy.proposeCall(democracyProposalCall, 0n).method.toHex(), - }; - - const submitResult = await helper.fellowship.referenda.submit( - rank1Proposer, - fellowshipPropositionOrigin, - fellowshipProposal, - {After: 0}, - ); - - const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; - await voteUnanimouslyInFellowship(helper, fellows, democracyTrackMinRank, fellowshipReferendumIndex); - await helper.fellowship.referenda.placeDecisionDeposit(donor, fellowshipReferendumIndex); - - await helper.wait.expectEvent( - fellowshipPreparePeriod + fellowshipConfirmPeriod + fellowshipMinEnactPeriod, - Event.Democracy.Proposed, - ); - - const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); - const referendumIndex = startedEvent.referendumIndex; - - const ayeBalance = 10_000n; - - await helper.democracy.vote(regularUser, referendumIndex, { - Standard: { - vote: { - aye: true, - conviction: 1, - }, - balance: ayeBalance, - }, - }); - - const referendumInfo = await helper.democracy.referendumInfo(referendumIndex); - const tally = referendumInfo.ongoing.tally; - - expect(BigInt(tally.ayes)).to.be.equal(ayeBalance); - - await clearFellowship(sudoer); - }); - - itSub('[Negative] Regular user cannot submit a regular proposal', async ({helper}) => { - const submitResult = helper.democracy.propose(regularUser, dummyProposalCall(helper), 0n); - await expect(submitResult).to.be.rejectedWith(/BadOrigin/); - }); - - itSub('[Negative] Regular user cannot externally propose SuperMajorityAgainst', async ({helper}) => { - const submitResult = helper.democracy.externalProposeDefault(regularUser, dummyProposalCall(helper)); - await expect(submitResult).to.be.rejectedWith(/BadOrigin/); - }); - - itSub('[Negative] Regular user cannot externally propose SimpleMajority', async ({helper}) => { - const submitResult = helper.democracy.externalProposeMajority(regularUser, dummyProposalCall(helper)); - await expect(submitResult).to.be.rejectedWith(/BadOrigin/); - }); - - itSub('[Negative] Regular user cannot externally propose SuperMajorityApprove', async ({helper}) => { - const submitResult = helper.democracy.externalPropose(regularUser, dummyProposalCall(helper)); - await expect(submitResult).to.be.rejectedWith(/BadOrigin/); - }); -}); --- a/js-packages/tests/src/governance/fellowship.test.ts +++ /dev/null @@ -1,335 +0,0 @@ -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js'; -import {DevUniqueHelper, Event} from '@unique/playgrounds/src/unique.dev.js'; -import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyFastTrackVotingPeriod, fellowshipRankLimit, clearCouncil, clearTechComm, clearFellowship, defaultEnactmentMoment, dummyProposal, dummyProposalCall, fellowshipPropositionOrigin, initFellowship, initTechComm, voteUnanimouslyInFellowship, democracyTrackMinRank, fellowshipPreparePeriod, fellowshipConfirmPeriod, fellowshipMinEnactPeriod, democracyTrackId, hardResetFellowshipReferenda, hardResetDemocracy, hardResetGovScheduler} from './util.js'; -import type {ICounselors, ITechComms} from './util.js'; - -describeGov('Governance: Fellowship tests', () => { - let members: IKeyringPair[][]; - - let rank1Proposer: IKeyringPair; - - let sudoer: any; - let donor: any; - let counselors: ICounselors; - let techcomms: ITechComms; - - const submissionDeposit = 1000n; - - async function testBadFellowshipProposal( - helper: DevUniqueHelper, - proposalCall: any, - ) { - const badProposal = { - Inline: proposalCall.method.toHex(), - }; - const submitResult = await helper.fellowship.referenda.submit( - rank1Proposer, - fellowshipPropositionOrigin, - badProposal, - defaultEnactmentMoment, - ); - - const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; - await voteUnanimouslyInFellowship(helper, members, democracyTrackMinRank, referendumIndex); - await helper.fellowship.referenda.placeDecisionDeposit(donor, referendumIndex); - - const enactmentId = await helper.fellowship.referenda.enactmentEventId(referendumIndex); - const dispatchedEvent = await helper.wait.expectEvent( - fellowshipPreparePeriod + fellowshipConfirmPeriod + defaultEnactmentMoment.After, - Event.Scheduler.Dispatched, - (event: any) => event.id == enactmentId, - ); - - expect(dispatchedEvent.result.isErr, 'Bad Fellowship must fail') - .to.be.true; - - expect(dispatchedEvent.result.asErr.isBadOrigin, 'Bad Fellowship must fail with BadOrigin') - .to.be.true; - } - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.Democracy, Pallets.Fellowship, Pallets.TechnicalCommittee, Pallets.Council]); - - sudoer = await privateKey('//Alice'); - donor = await privateKey({url: import.meta.url}); - }); - - counselors = await initCouncil(donor, sudoer); - techcomms = await initTechComm(donor, sudoer); - members = await initFellowship(donor, sudoer); - - rank1Proposer = members[1][0]; - }); - - after(async () => { - await clearFellowship(sudoer); - await clearTechComm(sudoer); - await clearCouncil(sudoer); - await hardResetFellowshipReferenda(sudoer); - await hardResetDemocracy(sudoer); - await hardResetGovScheduler(sudoer); - }); - - itSub('FellowshipProposition can submit regular Democracy proposals', async ({helper}) => { - const democracyProposalCall = dummyProposalCall(helper); - const fellowshipProposal = { - Inline: helper.democracy.proposeCall(democracyProposalCall, 0n).method.toHex(), - }; - - const submitResult = await helper.fellowship.referenda.submit( - rank1Proposer, - fellowshipPropositionOrigin, - fellowshipProposal, - defaultEnactmentMoment, - ); - - const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; - await voteUnanimouslyInFellowship(helper, members, democracyTrackMinRank, fellowshipReferendumIndex); - await helper.fellowship.referenda.placeDecisionDeposit(donor, fellowshipReferendumIndex); - - const democracyProposed = await helper.wait.expectEvent( - fellowshipPreparePeriod + fellowshipConfirmPeriod + fellowshipMinEnactPeriod, - Event.Democracy.Proposed, - ); - - const democracyEnqueuedProposal = await helper.democracy.expectPublicProposal(democracyProposed.proposalIndex); - expect(democracyEnqueuedProposal.inline, 'Fellowship proposal expected to be in the Democracy') - .to.be.equal(democracyProposalCall.method.toHex()); - - await helper.wait.newBlocks(democracyVotingPeriod); - }); - - itSub('Fellowship (rank-1 or greater) member can submit Fellowship proposals on the Democracy track', async ({helper}) => { - for(let rank = 1; rank < fellowshipRankLimit; rank++) { - const rankMembers = members[rank]; - - for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) { - const member = rankMembers[memberIdx]; - const newDummyProposal = dummyProposal(helper); - - const submitResult = await helper.fellowship.referenda.submit( - member, - fellowshipPropositionOrigin, - newDummyProposal, - defaultEnactmentMoment, - ); - - const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; - const referendumInfo = await helper.fellowship.referenda.referendumInfo(referendumIndex); - expect(referendumInfo.ongoing.track, `${memberIdx}-th member of rank #${rank}: proposal #${referendumIndex} is on invalid track`) - .to.be.equal(democracyTrackId); - } - } - }); - - itSub(`Fellowship (rank-${democracyTrackMinRank} or greater) members can vote on the Democracy track`, async ({helper}) => { - const proposal = dummyProposal(helper); - - const submitResult = await helper.fellowship.referenda.submit( - rank1Proposer, - fellowshipPropositionOrigin, - proposal, - defaultEnactmentMoment, - ); - - const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; - - let expectedAyes = 0; - for(let rank = democracyTrackMinRank; rank < fellowshipRankLimit; rank++) { - const rankMembers = members[rank]; - - for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) { - const member = rankMembers[memberIdx]; - await helper.fellowship.collective.vote(member, referendumIndex, true); - expectedAyes += 1; - - const referendumInfo = await helper.fellowship.referenda.referendumInfo(referendumIndex); - expect(referendumInfo.ongoing.tally.bareAyes, `Vote from ${memberIdx}-th member of rank #${rank} isn't accounted`) - .to.be.equal(expectedAyes); - } - } - }); - - itSub('Fellowship rank vote strength is correct', async ({helper}) => { - const excessRankWeightTable = [ - 1, - 3, - 6, - 10, - 15, - 21, - ]; - - const proposal = dummyProposal(helper); - - const submitResult = await helper.fellowship.referenda.submit( - rank1Proposer, - fellowshipPropositionOrigin, - proposal, - defaultEnactmentMoment, - ); - - const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; - - for(let rank = democracyTrackMinRank; rank < fellowshipRankLimit; rank++) { - const rankMembers = members[rank]; - - for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) { - const member = rankMembers[memberIdx]; - - const referendumInfoBefore = await helper.fellowship.referenda.referendumInfo(referendumIndex); - const ayesBefore = referendumInfoBefore.ongoing.tally.ayes; - - await helper.fellowship.collective.vote(member, referendumIndex, true); - - const referendumInfoAfter = await helper.fellowship.referenda.referendumInfo(referendumIndex); - const ayesAfter = referendumInfoAfter.ongoing.tally.ayes; - - const expectedVoteWeight = excessRankWeightTable[rank - democracyTrackMinRank]; - const voteWeight = ayesAfter - ayesBefore; - - expect(voteWeight, `Vote weight of ${memberIdx}-th member of rank #${rank} is invalid`) - .to.be.equal(expectedVoteWeight); - } - } - }); - - itSub('[Negative] FellowshipProposition cannot externally propose SuperMajorityAgainst', async ({helper}) => { - await testBadFellowshipProposal(helper, helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper))); - }); - - itSub('[Negative] FellowshipProposition cannot externally propose SimpleMajority', async ({helper}) => { - await testBadFellowshipProposal(helper, helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper))); - }); - - itSub('[Negative] FellowshipProposition cannot externally propose SuperMajorityApprove', async ({helper}) => { - await testBadFellowshipProposal(helper, helper.democracy.externalProposeCall(dummyProposalCall(helper))); - }); - - itSub('[Negative] Fellowship (rank-0) member cannot submit Fellowship proposals on the Democracy track', async ({helper}) => { - const rank0Proposer = members[0][0]; - - const proposal = dummyProposal(helper); - - const submitResult = helper.fellowship.referenda.submit( - rank0Proposer, - fellowshipPropositionOrigin, - proposal, - defaultEnactmentMoment, - ); - - await expect(submitResult).to.be.rejectedWith(/BadOrigin/); - }); - - itSub('[Negative] Fellowship (rank-1 or greater) member cannot submit if no deposit can be provided', async ({helper}) => { - const poorMember = rank1Proposer; - - const balanceBefore = await helper.balance.getSubstrate(poorMember.address); - await helper.getSudo().balance.setBalanceSubstrate(sudoer, poorMember.address, submissionDeposit - 1n); - - const proposal = dummyProposal(helper); - - const submitResult = helper.fellowship.referenda.submit( - poorMember, - fellowshipPropositionOrigin, - proposal, - defaultEnactmentMoment, - ); - - await expect(submitResult).to.be.rejectedWith(/account balance too low/); - - await helper.getSudo().balance.setBalanceSubstrate(sudoer, poorMember.address, balanceBefore); - }); - - itSub(`[Negative] Fellowship (rank-${democracyTrackMinRank - 1} or less) members cannot vote on the Democracy track`, async ({helper}) => { - const proposal = dummyProposal(helper); - - const submitResult = await helper.fellowship.referenda.submit( - rank1Proposer, - fellowshipPropositionOrigin, - proposal, - defaultEnactmentMoment, - ); - - const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; - - for(let rank = 0; rank < democracyTrackMinRank; rank++) { - for(const member of members[rank]) { - const voteResult = helper.fellowship.collective.vote(member, referendumIndex, true); - await expect(voteResult).to.be.rejectedWith(/RankTooLow/); - } - } - }); - - itSub('[Negative] FellowshipProposition cannot add/remove a Council member', async ({helper}) => { - const [councilNonMember] = await helper.arrange.createAccounts([10n], donor); - - await testBadFellowshipProposal(helper, helper.council.membership.addMemberCall(councilNonMember.address)); - await testBadFellowshipProposal(helper, helper.council.membership.removeMemberCall(counselors.ildar.address)); - }); - - itSub('[Negative] FellowshipProposition cannot set/clear Council prime member', async ({helper}) => { - await testBadFellowshipProposal(helper, helper.council.membership.setPrimeCall(counselors.ildar.address)); - await testBadFellowshipProposal(helper, helper.council.membership.clearPrimeCall()); - }); - - itSub('[Negative] FellowshipProposition cannot add/remove a TechComm member', async ({helper}) => { - const [techCommNonMember] = await helper.arrange.createAccounts([10n], donor); - - await testBadFellowshipProposal(helper, helper.technicalCommittee.membership.addMemberCall(techCommNonMember.address)); - await testBadFellowshipProposal(helper, helper.technicalCommittee.membership.removeMemberCall(techcomms.constantine.address)); - }); - - itSub('[Negative] FellowshipProposition cannot add/remove a Fellowship member', async ({helper}) => { - const [fellowshipNonMember] = await helper.arrange.createAccounts([10n], donor); - - await testBadFellowshipProposal(helper, helper.fellowship.collective.addMemberCall(fellowshipNonMember.address)); - await testBadFellowshipProposal(helper, helper.fellowship.collective.removeMemberCall(rank1Proposer.address, 1)); - }); - - itSub('[Negative] FellowshipProposition cannot promote/demote a Fellowship member', async ({helper}) => { - await testBadFellowshipProposal(helper, helper.fellowship.collective.promoteCall(rank1Proposer.address)); - await testBadFellowshipProposal(helper, helper.fellowship.collective.demoteCall(rank1Proposer.address)); - }); - - itSub('[Negative] FellowshipProposition cannot fast-track Democracy proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - - await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); - - await testBadFellowshipProposal(helper, helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0)); - }); - - itSub('[Negative] FellowshipProposition cannot cancel Democracy proposals', async ({helper}) => { - const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n); - const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex; - - await testBadFellowshipProposal(helper, helper.democracy.cancelProposalCall(proposalIndex)); - }); - - itSub('[Negative] FellowshipProposition cannot cancel ongoing Democracy referendums', async ({helper}) => { - await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper)); - const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); - const referendumIndex = startedEvent.referendumIndex; - - await testBadFellowshipProposal(helper, helper.democracy.emergencyCancelCall(referendumIndex)); - }); - - itSub('[Negative] FellowshipProposition cannot blacklist Democracy proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - - await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); - - await testBadFellowshipProposal(helper, helper.democracy.blacklistCall(preimageHash, null)); - }); - - itSub('[Negative] FellowshipProposition cannot veto external proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - - await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); - - await testBadFellowshipProposal(helper, helper.democracy.vetoExternalCall(preimageHash)); - }); -}); --- a/js-packages/tests/src/governance/init.test.ts +++ /dev/null @@ -1,193 +0,0 @@ -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js'; -import {Event} from '@unique/playgrounds/src/unique.dev.js'; -import {democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, clearCouncil, clearTechComm, clearFellowship} from './util.js'; -import type {ICounselors, ITechComms} from './util.js'; - -describeGov('Governance: Initialization', () => { - let donor: IKeyringPair; - let sudoer: IKeyringPair; - let counselors: ICounselors; - let techcomms: ITechComms; - let coreDevs: any; - - const expectedAlexFellowRank = 7; - const expectedFellowRank = 6; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.Democracy, Pallets.Council, Pallets.TechnicalCommittee]); - - const councilMembers = await helper.council.membership.getMembers(); - const techcommMembers = await helper.technicalCommittee.membership.getMembers(); - expect(councilMembers.length == 0, 'The Council must be empty before the Gov Init'); - expect(techcommMembers.length == 0, 'The Technical Commettee must be empty before the Gov Init'); - - donor = await privateKey({url: import.meta.url}); - sudoer = await privateKey('//Alice'); - - const counselorsNum = 5; - const techCommsNum = 3; - const coreDevsNum = 2; - const [ - alex, - ildar, - charu, - filip, - irina, - - greg, - andy, - constantine, - - yaroslav, - daniel, - ] = await helper.arrange.createAccounts(new Array(counselorsNum + techCommsNum + coreDevsNum).fill(10_000n), donor); - - counselors = { - alex, - ildar, - charu, - filip, - irina, - }; - - techcomms = { - greg, - andy, - constantine, - }; - - coreDevs = { - yaroslav: yaroslav, - daniel: daniel, - }; - }); - }); - - itSub('Initialize Governance', async ({helper}) => { - const promoteFellow = (fellow: string, promotionsNum: number) => new Array(promotionsNum).fill(helper.fellowship.collective.promoteCall(fellow)); - - const expectFellowRank = async (fellow: string, expectedRank: number) => { - expect(await helper.fellowship.collective.getMemberRank(fellow)).to.be.equal(expectedRank); - }; - - console.log('\t- Setup the Prime of the Council via sudo'); - await helper.getSudo().utility.batchAll(sudoer, [ - helper.council.membership.addMemberCall(counselors.alex.address), - helper.council.membership.setPrimeCall(counselors.alex.address), - - helper.fellowship.collective.addMemberCall(counselors.alex.address), - ...promoteFellow(counselors.alex.address, expectedAlexFellowRank), - ]); - - let councilMembers = await helper.council.membership.getMembers(); - const councilPrime = await helper.council.collective.getPrimeMember(); - const alexFellowRank = await helper.fellowship.collective.getMemberRank(counselors.alex.address); - expect(councilMembers).to.be.deep.equal([counselors.alex.address]); - expect(councilPrime).to.be.equal(counselors.alex.address); - expect(alexFellowRank).to.be.equal(expectedAlexFellowRank); - - console.log('\t- The Council Prime initializes the Technical Commettee'); - const councilProposalThreshold = 1; - - await helper.council.collective.propose( - counselors.alex, - helper.utility.batchAllCall([ - helper.technicalCommittee.membership.addMemberCall(techcomms.greg.address), - helper.technicalCommittee.membership.addMemberCall(techcomms.andy.address), - helper.technicalCommittee.membership.addMemberCall(techcomms.constantine.address), - - helper.technicalCommittee.membership.setPrimeCall(techcomms.greg.address), - ]), - councilProposalThreshold, - ); - - const techCommMembers = await helper.technicalCommittee.membership.getMembers(); - const techCommPrime = await helper.technicalCommittee.membership.getPrimeMember(); - const expectedTechComms = [techcomms.greg.address, techcomms.andy.address, techcomms.constantine.address]; - expect(techCommMembers.length).to.be.equal(expectedTechComms.length); - expect(techCommMembers).to.containSubset(expectedTechComms); - expect(techCommPrime).to.be.equal(techcomms.greg.address); - - console.log('\t- The Council Prime initiates a referendum to add counselors'); - const returnPreimageHash = true; - const preimageHash = await helper.preimage.notePreimageFromCall(counselors.alex, helper.utility.batchAllCall([ - helper.council.membership.addMemberCall(counselors.ildar.address), - helper.council.membership.addMemberCall(counselors.charu.address), - helper.council.membership.addMemberCall(counselors.filip.address), - helper.council.membership.addMemberCall(counselors.irina.address), - - helper.fellowship.collective.addMemberCall(counselors.charu.address), - helper.fellowship.collective.addMemberCall(counselors.ildar.address), - helper.fellowship.collective.addMemberCall(counselors.irina.address), - helper.fellowship.collective.addMemberCall(counselors.filip.address), - helper.fellowship.collective.addMemberCall(techcomms.greg.address), - helper.fellowship.collective.addMemberCall(techcomms.andy.address), - helper.fellowship.collective.addMemberCall(techcomms.constantine.address), - helper.fellowship.collective.addMemberCall(coreDevs.yaroslav.address), - helper.fellowship.collective.addMemberCall(coreDevs.daniel.address), - - ...promoteFellow(counselors.charu.address, expectedFellowRank), - ...promoteFellow(counselors.ildar.address, expectedFellowRank), - ...promoteFellow(counselors.irina.address, expectedFellowRank), - ...promoteFellow(counselors.filip.address, expectedFellowRank), - ...promoteFellow(techcomms.greg.address, expectedFellowRank), - ...promoteFellow(techcomms.andy.address, expectedFellowRank), - ...promoteFellow(techcomms.constantine.address, expectedFellowRank), - ...promoteFellow(coreDevs.yaroslav.address, expectedFellowRank), - ...promoteFellow(coreDevs.daniel.address, expectedFellowRank), - ]), returnPreimageHash); - - await helper.council.collective.propose( - counselors.alex, - helper.democracy.externalProposeDefaultWithPreimageCall(preimageHash), - councilProposalThreshold, - ); - - console.log('\t- The referendum is being decided'); - const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); - - await helper.democracy.vote(counselors.filip, startedEvent.referendumIndex, { - Standard: { - vote: { - aye: true, - conviction: 1, - }, - balance: 10_000n, - }, - }); - - const passedReferendumEvent = await helper.wait.expectEvent(democracyVotingPeriod, Event.Democracy.Passed); - expect(passedReferendumEvent.referendumIndex).to.be.equal(startedEvent.referendumIndex); - - await helper.wait.expectEvent(democracyEnactmentPeriod, Event.Scheduler.Dispatched); - - councilMembers = await helper.council.membership.getMembers(); - const expectedCounselors = [ - counselors.alex.address, - counselors.ildar.address, - counselors.charu.address, - counselors.filip.address, - counselors.irina.address, - ]; - expect(councilMembers.length).to.be.equal(expectedCounselors.length); - expect(councilMembers).to.containSubset(expectedCounselors); - - await expectFellowRank(counselors.ildar.address, expectedFellowRank); - await expectFellowRank(counselors.charu.address, expectedFellowRank); - await expectFellowRank(counselors.filip.address, expectedFellowRank); - await expectFellowRank(counselors.irina.address, expectedFellowRank); - await expectFellowRank(techcomms.greg.address, expectedFellowRank); - await expectFellowRank(techcomms.andy.address, expectedFellowRank); - await expectFellowRank(techcomms.constantine.address, expectedFellowRank); - await expectFellowRank(coreDevs.yaroslav.address, expectedFellowRank); - await expectFellowRank(coreDevs.daniel.address, expectedFellowRank); - }); - - after(async function() { - await clearFellowship(sudoer); - await clearTechComm(sudoer); - await clearCouncil(sudoer); - }); -}); --- a/js-packages/tests/src/governance/technicalCommittee.test.ts +++ /dev/null @@ -1,383 +0,0 @@ -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js'; -import {Event} from '@unique/playgrounds/src/unique.dev.js'; -import {initCouncil, democracyLaunchPeriod, democracyFastTrackVotingPeriod, clearCouncil, clearTechComm, clearFellowship, defaultEnactmentMoment, dummyProposal, dummyProposalCall, fellowshipPropositionOrigin, initFellowship, initTechComm, hardResetFellowshipReferenda, hardResetDemocracy, hardResetGovScheduler} from './util.js'; -import type {ITechComms} from './util.js'; - -describeGov('Governance: Technical Committee tests', () => { - let sudoer: IKeyringPair; - let techcomms: ITechComms; - let donor: IKeyringPair; - let preImageHash: string; - - - const allTechCommitteeThreshold = 3; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.TechnicalCommittee]); - sudoer = await privateKey('//Alice'); - donor = await privateKey({url: import.meta.url}); - - techcomms = await initTechComm(donor, sudoer); - - const proposalCall = await helper.constructApiCall('api.tx.balances.forceSetBalance', [donor.address, 20n * 10n ** 25n]); - preImageHash = await helper.preimage.notePreimageFromCall(sudoer, proposalCall, true); - }); - }); - - after(async () => { - await usingPlaygrounds(async (helper) => { - await clearTechComm(sudoer); - - await helper.preimage.unnotePreimage(sudoer, preImageHash); - await hardResetFellowshipReferenda(sudoer); - await hardResetDemocracy(sudoer); - await hardResetGovScheduler(sudoer); - }); - }); - - function proposalFromAllCommittee(proposal: any) { - return usingPlaygrounds(async (helper) => { - expect((await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length).to.be.equal(allTechCommitteeThreshold); - const proposeResult = await helper.technicalCommittee.collective.propose( - techcomms.andy, - proposal, - allTechCommitteeThreshold, - ); - - const commiteeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult); - const proposalIndex = commiteeProposedEvent.proposalIndex; - const proposalHash = commiteeProposedEvent.proposalHash; - - - await helper.technicalCommittee.collective.vote(techcomms.andy, proposalHash, proposalIndex, true); - await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true); - await helper.technicalCommittee.collective.vote(techcomms.greg, proposalHash, proposalIndex, true); - - const closeResult = await helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex); - Event.TechnicalCommittee.Closed.expect(closeResult); - Event.TechnicalCommittee.Approved.expect(closeResult); - const {result} = Event.TechnicalCommittee.Executed.expect(closeResult); - expect(result).to.eq('Ok'); - - return closeResult; - }); - } - - itSub('TechComm can fast-track Democracy proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - await helper.wait.parachainBlockMultiplesOf(35n); - - await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); - - const fastTrackProposal = await proposalFromAllCommittee(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0)); - Event.Democracy.Started.expect(fastTrackProposal); - }); - - itSub('TechComm can cancel Democracy proposals', async ({helper}) => { - const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n); - const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex; - - const cancelProposal = await proposalFromAllCommittee(helper.democracy.cancelProposalCall(proposalIndex)); - Event.Democracy.ProposalCanceled.expect(cancelProposal); - }); - - itSub('TechComm can cancel ongoing Democracy referendums', async ({helper}) => { - await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper)); - const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); - const referendumIndex = startedEvent.referendumIndex; - - const emergencyCancelProposal = await proposalFromAllCommittee(helper.democracy.emergencyCancelCall(referendumIndex)); - Event.Democracy.Cancelled.expect(emergencyCancelProposal); - }); - - itSub('TechComm member can veto Democracy proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); - - const vetoExternalCall = await helper.technicalCommittee.collective.execute( - techcomms.andy, - helper.democracy.vetoExternalCall(preimageHash), - ); - Event.Democracy.Vetoed.expect(vetoExternalCall); - }); - - itSub('TechComm can cancel Fellowship referendums', async ({helper}) => { - const fellowship = await initFellowship(donor, sudoer); - const fellowshipProposer = fellowship[5][0]; - const proposal = dummyProposal(helper); - - const submitResult = await helper.fellowship.referenda.submit( - fellowshipProposer, - fellowshipPropositionOrigin, - proposal, - defaultEnactmentMoment, - ); - const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; - const cancelProposal = await proposalFromAllCommittee(helper.fellowship.referenda.cancelCall(referendumIndex)); - Event.FellowshipReferenda.Cancelled.expect(cancelProposal); - }); - - itSub.skip('TechComm member can add a Fellowship member', async ({helper}) => { - const newFellowshipMember = helper.arrange.createEmptyAccount(); - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - helper.fellowship.collective.addMemberCall(newFellowshipMember.address), - )).to.be.fulfilled; - const fellowshipMembers = (await helper.callRpc('api.query.fellowshipCollective.members')).toJSON(); - expect(fellowshipMembers).to.contains(newFellowshipMember.address); - await clearFellowship(sudoer); - }); - - itSub('[Negative] TechComm cannot submit regular democracy proposal', async ({helper}) => { - const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n); - - await expect(proposalFromAllCommittee(councilProposal)).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm cannot externally propose SuperMajorityAgainst', async ({helper}) => { - const commiteeProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper)); - - await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm cannot externally propose SimpleMajority', async ({helper}) => { - const commiteeProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper)); - - await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm cannot externally propose SuperMajorityApprove', async ({helper}) => { - const commiteeProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper)); - - await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm member cannot submit regular democracy proposal', async ({helper}) => { - const memberProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n); - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - memberProposal, - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm member cannot externally propose SuperMajorityAgainst', async ({helper}) => { - const memberProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper)); - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - memberProposal, - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm member cannot externally propose SimpleMajority', async ({helper}) => { - const memberProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper)); - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - memberProposal, - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm member cannot externally propose SuperMajorityApprove', async ({helper}) => { - const memberProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper)); - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - memberProposal, - )).to.be.rejectedWith('BadOrigin'); - }); - - - itSub.skip('[Negative] TechComm cannot promote/demote Fellowship member', async () => { - - }); - - itSub.skip('[Negative] TechComm member cannot promote/demote Fellowship member', async () => { - - }); - - itSub('[Negative] TechComm cannot add/remove a Council member', async ({helper}) => { - const newCouncilMember = helper.arrange.createEmptyAccount(); - const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address); - const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address); - - await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin'); - await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm member cannot add/remove a Council member', async ({helper}) => { - const newCouncilMember = helper.arrange.createEmptyAccount(); - const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address); - const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address); - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - addMemberProposal, - )).to.be.rejectedWith('BadOrigin'); - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - removeMemberProposal, - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm cannot set/clear Council prime member', async ({helper}) => { - const counselors = await initCouncil(donor, sudoer); - const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address); - const proposalForClear = await helper.council.membership.clearPrimeCall(); - - await expect(proposalFromAllCommittee(proposalForSet)).to.be.rejectedWith('BadOrigin'); - await expect(proposalFromAllCommittee(proposalForClear)).to.be.rejectedWith('BadOrigin'); - await clearCouncil(sudoer); - }); - - itSub('[Negative] TechComm member cannot set/clear Council prime member', async ({helper}) => { - const counselors = await initCouncil(donor, sudoer); - const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address); - const proposalForClear = await helper.council.membership.clearPrimeCall(); - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - proposalForSet, - )).to.be.rejectedWith('BadOrigin'); - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - proposalForClear, - )).to.be.rejectedWith('BadOrigin'); - await clearCouncil(sudoer); - }); - - itSub('[Negative] TechComm cannot add/remove a TechComm member', async ({helper}) => { - const newCommMember = helper.arrange.createEmptyAccount(); - const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address); - const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address); - - await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin'); - await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm member cannot add/remove a TechComm member', async ({helper}) => { - const newCommMember = helper.arrange.createEmptyAccount(); - const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address); - const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address); - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - addMemberProposal, - )).to.be.rejectedWith('BadOrigin'); - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - removeMemberProposal, - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm cannot remove a Fellowship member', async ({helper}) => { - const fellowship = await initFellowship(donor, sudoer); - - await expect(proposalFromAllCommittee(helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5))).to.be.rejectedWith('BadOrigin'); - await clearFellowship(sudoer); - }); - - itSub('[Negative] TechComm member cannot remove a Fellowship member', async ({helper}) => { - const fellowship = await initFellowship(donor, sudoer); - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5), - )).to.be.rejectedWith('BadOrigin'); - await clearFellowship(sudoer); - }); - - itSub('[Negative] TechComm member cannot fast-track Democracy proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0), - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm member cannot cancel Democracy proposals', async ({helper}) => { - const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n); - const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex; - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - helper.democracy.cancelProposalCall(proposalIndex), - )) - .to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm member cannot cancel ongoing Democracy referendums', async ({helper}) => { - await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper)); - const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started); - const referendumIndex = startedEvent.referendumIndex; - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - helper.democracy.emergencyCancelCall(referendumIndex), - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm cannot blacklist Democracy proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); - - await expect(proposalFromAllCommittee(helper.democracy.blacklistCall(preimageHash))).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm member cannot blacklist Democracy proposals', async ({helper}) => { - const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true); - await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash); - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - helper.democracy.blacklistCall(preimageHash), - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub.skip('[Negative] TechComm member cannot veto external Democracy proposals until the cool-off period pass', async () => { - - }); - - itSub('[Negative] TechComm member cannot cancel Fellowship referendums', async ({helper}) => { - const fellowship = await initFellowship(donor, sudoer); - const fellowshipProposer = fellowship[5][0]; - const proposal = dummyProposal(helper); - - const submitResult = await helper.fellowship.referenda.submit( - fellowshipProposer, - fellowshipPropositionOrigin, - proposal, - defaultEnactmentMoment, - ); - - const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex; - - await expect(helper.technicalCommittee.collective.execute( - techcomms.andy, - helper.fellowship.referenda.cancelCall(referendumIndex), - )).to.be.rejectedWith('BadOrigin'); - }); - - itSub('[Negative] TechComm referendum cannot be closed until the voting threshold is met', async ({helper}) => { - const committeeSize = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length as any as number; - expect(committeeSize).is.greaterThan(1); - const proposeResult = await helper.technicalCommittee.collective.propose( - techcomms.andy, - dummyProposalCall(helper), - committeeSize, - ); - - const committeeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult); - const proposalIndex = committeeProposedEvent.proposalIndex; - const proposalHash = committeeProposedEvent.proposalHash; - - await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true); - - await expect(helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly'); - }); -}); --- a/js-packages/tests/src/governance/util.ts +++ /dev/null @@ -1,224 +0,0 @@ -import type {IKeyringPair} from '@polkadot/types/types'; -import {xxhashAsHex} from '@polkadot/util-crypto'; -import type {u32} from '@polkadot/types-codec'; -import {usingPlaygrounds, expect} from '../util/index.js'; -import {UniqueHelper} from '@unique/playgrounds/src/unique.js'; -import {DevUniqueHelper} from '@unique/playgrounds/src/unique.dev.js'; - -export const democracyLaunchPeriod = 35; -export const democracyVotingPeriod = 35; -export const councilMotionDuration = 35; -export const democracyEnactmentPeriod = 40; -export const democracyFastTrackVotingPeriod = 5; - -export const fellowshipRankLimit = 7; -export const fellowshipPropositionOrigin = 'FellowshipProposition'; -export const fellowshipPreparePeriod = 3; -export const fellowshipConfirmPeriod = 3; -export const fellowshipMinEnactPeriod = 1; - -export const defaultEnactmentMoment = {After: 0}; - -export const democracyTrackId = 10; -export const democracyTrackMinRank = 3; -const twox128 = (data: any) => xxhashAsHex(data, 128); -export interface ICounselors { - alex: IKeyringPair; - ildar: IKeyringPair; - charu: IKeyringPair; - filip: IKeyringPair; - irina: IKeyringPair; -} -export interface ITechComms { - greg: IKeyringPair; - andy: IKeyringPair; - constantine: IKeyringPair; -} - -export async function initCouncil(donor: IKeyringPair, superuser: IKeyringPair) { - let counselors: IKeyringPair[] = []; - - await usingPlaygrounds(async (helper) => { - const [alex, ildar, charu, filip, irina] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n, 10_000n, 10_000n], donor); - const sudo = helper.getSudo(); - { - const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as []; - if(members.length != 0) { - await clearCouncil(superuser); - } - } - const expectedMembers = [alex, ildar, charu, filip, irina]; - for(const member of expectedMembers) { - await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.addMember', [member.address]); - } - await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.setPrime', [alex.address]); - { - const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON(); - expect(members).to.containSubset(expectedMembers.map((x: IKeyringPair) => x.address)); - expect(members.length).to.be.equal(expectedMembers.length); - } - - counselors = [alex, ildar, charu, filip, irina]; - }); - return { - alex: counselors[0], - ildar: counselors[1], - charu: counselors[2], - filip: counselors[3], - irina: counselors[4], - }; -} - -export async function clearCouncil(superuser: IKeyringPair) { - await usingPlaygrounds(async (helper) => { - let members = (await helper.callRpc('api.query.councilMembership.members')).toJSON(); - if(members.length) { - const sudo = helper.getSudo(); - for(const address of members) { - await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.removeMember', [address]); - } - members = (await helper.callRpc('api.query.councilMembership.members')).toJSON(); - } - expect(members).to.be.deep.equal([]); - }); -} - - -export async function initTechComm(donor: IKeyringPair, superuser: IKeyringPair) { - let techcomms: IKeyringPair[] = []; - - await usingPlaygrounds(async (helper) => { - const [greg, andy, constantine] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n], donor); - const sudo = helper.getSudo(); - { - const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON() as []; - if(members.length != 0) { - await clearTechComm(superuser); - } - } - await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [greg.address]); - await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [andy.address]); - await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [constantine.address]); - await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.setPrime', [greg.address]); - { - const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON(); - expect(members).to.containSubset([greg.address, andy.address, constantine.address]); - expect(members.length).to.be.equal(3); - } - - techcomms = [greg, andy, constantine]; - }); - - return { - greg: techcomms[0], - andy: techcomms[1], - constantine: techcomms[2], - }; -} - -export async function clearTechComm(superuser: IKeyringPair) { - await usingPlaygrounds(async (helper) => { - let members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON(); - if(members.length) { - const sudo = helper.getSudo(); - for(const address of members) { - await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.removeMember', [address]); - } - members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON(); - } - expect(members).to.be.deep.equal([]); - }); -} - -export async function initFellowship(donor: IKeyringPair, sudoer: IKeyringPair) { - const numMembersInRank = 3; - const memberBalance = 5000n; - const members: IKeyringPair[][] = []; - - await usingPlaygrounds(async (helper) => { - const currentFellows = await helper.getApi().query.fellowshipCollective.members.keys(); - - if(currentFellows.length != 0) { - await clearFellowship(sudoer); - } - for(let i = 0; i < fellowshipRankLimit; i++) { - const rankMembers = await helper.arrange.createAccounts( - Array(numMembersInRank).fill(memberBalance), - donor, - ); - - for(const member of rankMembers) { - await helper.getSudo().fellowship.collective.addMember(sudoer, member.address); - - for(let rank = 0; rank < i; rank++) { - await helper.getSudo().fellowship.collective.promote(sudoer, member.address); - } - } - - members.push(rankMembers); - } - }); - - return members; -} - -export async function clearFellowship(sudoer: IKeyringPair) { - await usingPlaygrounds(async (helper) => { - const fellowship = (await helper.getApi().query.fellowshipCollective.members.keys()) - .map((key) => key.args[0].toString()); - for(const member of fellowship) { - await helper.getSudo().fellowship.collective.removeMember(sudoer, member, fellowshipRankLimit); - } - }); -} - -export async function clearFellowshipReferenda(sudoer: IKeyringPair) { - await usingPlaygrounds(async (helper) => { - const proposalsCount = (await helper.getApi().query.fellowshipReferenda.referendumCount()) as u32; - for(let i = 0; i < proposalsCount.toNumber(); i++) { - await helper.getSudo().fellowship.referenda.cancel(sudoer, i); - } - }); -} - -export async function hardResetFellowshipReferenda(sudoer: IKeyringPair) { - await usingPlaygrounds(async (helper) => { - const api = helper.getApi(); - const prefix = twox128('FellowshipReferenda'); - await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100))); - }); -} - -export async function hardResetDemocracy(sudoer: IKeyringPair) { - await usingPlaygrounds(async (helper) => { - const api = helper.getApi(); - const prefix = twox128('Democracy'); - await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100))); - }); -} - -export async function hardResetGovScheduler(sudoer: IKeyringPair) { - await usingPlaygrounds(async (helper) => { - const api = helper.getApi(); - const prefix = twox128('GovScheduler'); - await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 500))); - }); -} - -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); - } - } -} - -export function dummyProposalCall(helper: UniqueHelper) { - return helper.constructApiCall('api.tx.system.remark', ['dummy proposal' + (new Date()).getTime()]); -} - -export function dummyProposal(helper: UniqueHelper) { - return { - Inline: dummyProposalCall(helper).method.toHex(), - }; -} --- a/js-packages/tests/src/inflation.seqtest.ts +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, usingPlaygrounds} from './util/index.js'; - -// todo:playgrounds requires sudo, look into on the later stage -describe('integration test: Inflation', () => { - let superuser: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (_, privateKey) => { - superuser = await privateKey('//Alice'); - }); - }); - - itSub('First year inflation is 10%', async ({helper}) => { - // Make sure non-sudo can't start inflation - const [bob] = await helper.arrange.createAccounts([10n], superuser); - - await expect(helper.executeExtrinsic(bob, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/); - - // Make sure superuser can't start inflation without explicit sudo - await expect(helper.executeExtrinsic(superuser, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/); - - // Start inflation on relay block 1 (Alice is sudo) - const tx = helper.constructApiCall('api.tx.inflation.startInflation', [1]); - await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [tx])).to.not.be.rejected; - - const blockInterval = (helper.getApi().consts.inflation.inflationBlockInterval as any).toBigInt(); - const totalIssuanceStart = ((await helper.callRpc('api.query.inflation.startingYearTotalIssuance', [])) as any).toBigInt(); - const blockInflation = (await helper.callRpc('api.query.inflation.blockInflation', []) as any).toBigInt(); - - const YEAR = 5259600n; // 6-second block. Blocks in one year - // const YEAR = 2629800n; // 12-second block. Blocks in one year - - const totalExpectedInflation = totalIssuanceStart / 10n; - const totalActualInflation = blockInflation * YEAR / blockInterval; - - const tolerance = 0.00001; // Relative difference per year between theoretical and actual inflation - const expectedInflation = totalExpectedInflation / totalActualInflation - 1n; - - expect(Math.abs(Number(expectedInflation))).to.be.lessThanOrEqual(tolerance); - }); -}); --- a/js-packages/tests/src/limits.test.ts +++ /dev/null @@ -1,474 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js'; - -describe('Number of tokens per address (NFT)', () => { - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - - itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {}); - await collection.setLimits(alice, {accountTokenOwnershipLimit: 20}); - - for(let i = 0; i < 10; i++){ - await expect(collection.mintToken(alice)).to.be.not.rejected; - } - await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); - for(let i = 1; i < 11; i++) { - await expect(collection.burnToken(alice, i)).to.be.not.rejected; - } - await collection.burn(alice); - }); - - itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {}); - await collection.setLimits(alice, {accountTokenOwnershipLimit: 1}); - - await collection.mintToken(alice); - await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); - - await collection.burnToken(alice, 1); - await expect(collection.burn(alice)).to.be.not.rejected; - }); -}); - -describe('Number of tokens per address (ReFungible)', () => { - let alice: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - - itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {}); - await collection.setLimits(alice, {accountTokenOwnershipLimit: 20}); - - for(let i = 0; i < 10; i++){ - await expect(collection.mintToken(alice, 10n)).to.be.not.rejected; - } - await expect(collection.mintToken(alice, 10n)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); - for(let i = 1; i < 11; i++) { - await expect(collection.burnToken(alice, i, 10n)).to.be.not.rejected; - } - await collection.burn(alice); - }); - - itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {}); - await collection.setLimits(alice, {accountTokenOwnershipLimit: 1}); - - await collection.mintToken(alice); - await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); - - await collection.burnToken(alice, 1); - await expect(collection.burn(alice)).to.be.not.rejected; - }); -}); - -// todo:playgrounds skipped ~ postponed -describe.skip('Sponsor timeout (NFT) (only for special chain limits test)', () => { - /*let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); - }); - }); - - itSub.skip('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7}); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); - await setCollectionSponsorExpectSuccess(collectionId, alice.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); - await transferExpectSuccess(collectionId, tokenId, alice, bob); - const aliceBalanceBefore = await getFreeBalance(alice); - - // check setting SponsorTimeout = 5, fail - await waitNewBlocks(5); - await transferExpectSuccess(collectionId, tokenId, bob, charlie); - const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); - - // check setting SponsorTimeout = 7, success - await waitNewBlocks(2); // 5 + 2 - await transferExpectSuccess(collectionId, tokenId, charlie, bob); - const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; - //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); - await destroyCollectionExpectSuccess(collectionId); - }); - - itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => { - - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1}); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); - await setCollectionSponsorExpectSuccess(collectionId, alice.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); - await transferExpectSuccess(collectionId, tokenId, alice, bob); - const aliceBalanceBefore = await getFreeBalance(alice); - - // check setting SponsorTimeout = 1, fail - await waitNewBlocks(1); - await transferExpectSuccess(collectionId, tokenId, bob, charlie); - const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); - - // check setting SponsorTimeout = 5, success - await waitNewBlocks(4); - await transferExpectSuccess(collectionId, tokenId, charlie, bob); - const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; - //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); - await destroyCollectionExpectSuccess(collectionId); - }); -}); - -describe.skip('Sponsor timeout (Fungible) (only for special chain limits test)', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); - }); - }); - - itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7}); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible'); - await setCollectionSponsorExpectSuccess(collectionId, alice.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); - await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible'); - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); - const aliceBalanceBefore = await getFreeBalance(alice); - - // check setting SponsorTimeout = 5, fail - await waitNewBlocks(5); - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); - const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); - - // check setting SponsorTimeout = 7, success - await waitNewBlocks(2); // 5 + 2 - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); - const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; - //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); - - await destroyCollectionExpectSuccess(collectionId); - }); - - itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => { - - const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1}); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible'); - await setCollectionSponsorExpectSuccess(collectionId, alice.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); - await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible'); - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); - const aliceBalanceBefore = await getFreeBalance(alice); - - // check setting SponsorTimeout = 1, fail - await waitNewBlocks(1); - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); - const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); - - // check setting SponsorTimeout = 5, success - await waitNewBlocks(4); - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); - const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; - //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); - - await destroyCollectionExpectSuccess(collectionId); - }); -}); - -describe.skip('Sponsor timeout (ReFungible) (only for special chain limits test)', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); - }); - }); - - itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7}); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'ReFungible'); - await setCollectionSponsorExpectSuccess(collectionId, alice.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); - await transferExpectSuccess(collectionId, tokenId, alice, bob, 100, 'ReFungible'); - const aliceBalanceBefore = await getFreeBalance(alice); - - // check setting SponsorTimeout = 5, fail - await waitNewBlocks(5); - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible'); - const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); - - // check setting SponsorTimeout = 7, success - await waitNewBlocks(2); // 5 + 2 - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible'); - const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; - //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); - await destroyCollectionExpectSuccess(collectionId); - }); - - itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => { - - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1}); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); - await setCollectionSponsorExpectSuccess(collectionId, alice.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); - await transferExpectSuccess(collectionId, tokenId, alice, bob); - const aliceBalanceBefore = await getFreeBalance(alice); - - // check setting SponsorTimeout = 1, fail - await waitNewBlocks(1); - await transferExpectSuccess(collectionId, tokenId, bob, charlie); - const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore); - - // check setting SponsorTimeout = 5, success - await waitNewBlocks(4); - await transferExpectSuccess(collectionId, tokenId, charlie, bob); - const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice); - expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; - //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); - await destroyCollectionExpectSuccess(collectionId); - });*/ -}); - -describe('Collection zero limits (NFT)', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - }); - }); - - itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {}); - await collection.setLimits(alice, {accountTokenOwnershipLimit: 0}); - - for(let i = 0; i < 10; i++){ - await collection.mintToken(alice); - } - await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); - }); - - itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {}); - await collection.setLimits(alice, {sponsorTransferTimeout: 0}); - const token = await collection.mintToken(alice); - - await collection.setSponsor(alice, alice.address); - await collection.confirmSponsorship(alice); - - await token.transfer(alice, {Substrate: bob.address}); - const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); - - // check setting SponsorTimeout = 0, success with next block - await helper.wait.newBlocks(1); - await token.transfer(bob, {Substrate: charlie.address}); - const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address); - expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true; - }); -}); - -describe('Collection zero limits (Fungible)', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - }); - }); - - itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {}); - await collection.setLimits(alice, {sponsorTransferTimeout: 0}); - await collection.mint(alice, 3n); - - await collection.setSponsor(alice, alice.address); - await collection.confirmSponsorship(alice); - - await collection.transfer(alice, {Substrate: bob.address}, 2n); - const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); - - // check setting SponsorTimeout = 0, success with next block - await helper.wait.newBlocks(1); - await collection.transfer(bob, {Substrate: charlie.address}); - const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address); - expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true; - }); -}); - -describe('Collection zero limits (ReFungible)', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - }); - }); - - itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {}); - await collection.setLimits(alice, {accountTokenOwnershipLimit: 0}); - for(let i = 0; i < 10; i++){ - await collection.mintToken(alice); - } - await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); - }); - - itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {}); - await collection.setLimits(alice, {sponsorTransferTimeout: 0}); - const token = await collection.mintToken(alice, 3n); - - await collection.setSponsor(alice, alice.address); - await collection.confirmSponsorship(alice); - - await token.transfer(alice, {Substrate: bob.address}, 2n); - const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); - - // check setting SponsorTimeout = 0, success with next block - await helper.wait.newBlocks(1); - await token.transfer(bob, {Substrate: charlie.address}); - const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address); - expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true; - }); -}); - -describe('Effective collection limits (NFT)', () => { - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - - itSub('Effective collection limits', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {}); - await collection.setLimits(alice, {ownerCanTransfer: true}); - - { - // Check that limits are undefined - const collectionInfo = await collection.getData(); - const limits = collectionInfo?.raw.limits; - expect(limits).to.be.any; - - expect(limits.accountTokenOwnershipLimit).to.be.null; - expect(limits.sponsoredDataSize).to.be.null; - expect(limits.sponsoredDataRateLimit).to.be.null; - expect(limits.tokenLimit).to.be.null; - expect(limits.sponsorTransferTimeout).to.be.null; - expect(limits.sponsorApproveTimeout).to.be.null; - expect(limits.ownerCanTransfer).to.be.true; - expect(limits.ownerCanDestroy).to.be.null; - expect(limits.transfersEnabled).to.be.null; - } - - { // Check that limits is undefined for non-existent collection - const limits = await helper.collection.getEffectiveLimits(999999); - expect(limits).to.be.null; - } - - { // Check that default values defined for collection limits - const limits = await collection.getEffectiveLimits(); - - expect(limits.accountTokenOwnershipLimit).to.be.eq(100000); - expect(limits.sponsoredDataSize).to.be.eq(2048); - expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null}); - expect(limits.tokenLimit).to.be.eq(4294967295); - expect(limits.sponsorTransferTimeout).to.be.eq(5); - expect(limits.sponsorApproveTimeout).to.be.eq(5); - expect(limits.ownerCanTransfer).to.be.true; - expect(limits.ownerCanDestroy).to.be.true; - expect(limits.transfersEnabled).to.be.true; - } - - { - // Check the values for collection limits - await collection.setLimits(alice, { - accountTokenOwnershipLimit: 99_999, - sponsoredDataSize: 1024, - tokenLimit: 123, - transfersEnabled: false, - }); - - const limits = await collection.getEffectiveLimits(); - - expect(limits.accountTokenOwnershipLimit).to.be.eq(99999); - expect(limits.sponsoredDataSize).to.be.eq(1024); - expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null}); - expect(limits.tokenLimit).to.be.eq(123); - expect(limits.sponsorTransferTimeout).to.be.eq(5); - expect(limits.sponsorApproveTimeout).to.be.eq(5); - expect(limits.ownerCanTransfer).to.be.true; - expect(limits.ownerCanDestroy).to.be.true; - expect(limits.transfersEnabled).to.be.false; - } - }); -}); --- a/js-packages/tests/src/maintenance.seqtest.ts +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {ApiPromise} from '@polkadot/api'; -import {expect, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js'; -import {itEth} from './eth/util/index.js'; -import {main as correctState} from './migrations/correctStateAfterMaintenance.js'; -import type {PalletBalancesIdAmount} from '@polkadot/types/lookup'; -import type {Vec} from '@polkadot/types-codec'; - -async function maintenanceEnabled(api: ApiPromise): Promise { - return (await api.query.maintenance.enabled()).toJSON() as boolean; -} - -describe('Integration Test: Maintenance Functionality', () => { - let superuser: IKeyringPair; - let donor: IKeyringPair; - let bob: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.Maintenance]); - superuser = await privateKey('//Alice'); - donor = await privateKey({url: import.meta.url}); - [bob] = await helper.arrange.createAccounts([10000n], donor); - - }); - }); - - describe('Maintenance Mode', () => { - before(async function() { - await usingPlaygrounds(async (helper) => { - if(await maintenanceEnabled(helper.getApi())) { - console.warn('\tMaintenance mode was left enabled BEFORE the test suite! Disabling it now.'); - await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled; - } - }); - }); - - itSub('Allows superuser to enable and disable maintenance mode - and disallows anyone else', async ({helper}) => { - // Make sure non-sudo can't enable maintenance mode - await expect(helper.executeExtrinsic(superuser, 'api.tx.maintenance.enable', []), 'on commoner enabling MM') - .to.be.rejectedWith(/BadOrigin/); - - // Set maintenance mode - 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; - - // Make sure non-sudo can't disable maintenance mode - await expect(helper.executeExtrinsic(bob, 'api.tx.maintenance.disable', []), 'on commoner disabling MM') - .to.be.rejectedWith(/BadOrigin/); - - // Disable maintenance mode - 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; - }); - - itSub('MM blocks unique pallet calls', async ({helper}) => { - // Can create an NFT collection before enabling the MM - const nftCollection = await helper.nft.mintCollection(bob, { - tokenPropertyPermissions: [{ - key: 'test', permission: { - collectionAdmin: true, - tokenOwner: true, - mutable: true, - }, - }], - }); - - // Can mint an NFT before enabling the MM - const nft = await nftCollection.mintToken(bob); - - // Can create an FT collection before enabling the MM - const ftCollection = await helper.ft.mintCollection(superuser); - - // Can mint an FT before enabling the MM - await expect(ftCollection.mint(superuser)).to.be.fulfilled; - - 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; - - // Unable to create a collection when the MM is enabled - await expect(helper.nft.mintCollection(superuser), 'cudo forbidden stuff') - .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/); - - // Unable to set token properties when the MM is enabled - await expect(nft.setProperties( - bob, - [{key: 'test', value: 'test-val'}], - )).to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/); - - // Unable to mint an NFT when the MM is enabled - await expect(nftCollection.mintToken(superuser)) - .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/); - - // Unable to mint an FT when the MM is enabled - await expect(ftCollection.mint(superuser)) - .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; - - // Can create a collection after disabling the MM - await expect(helper.nft.mintCollection(bob), 'MM is disabled, the collection should be created').to.be.fulfilled; - - // Can set token properties after disabling the MM - await nft.setProperties(bob, [{key: 'test', value: 'test-val'}]); - - // Can mint an NFT after disabling the MM - await nftCollection.mintToken(bob); - - // Can mint an FT after disabling the MM - await ftCollection.mint(superuser); - }); - - itSub.ifWithPallets('MM blocks unique pallet calls (Re-Fungible)', [Pallets.ReFungible], async ({helper}) => { - // Can create an RFT collection before enabling the MM - const rftCollection = await helper.rft.mintCollection(superuser); - - // Can mint an RFT before enabling the MM - await expect(rftCollection.mintToken(superuser)).to.be.fulfilled; - - 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; - - // Unable to mint an RFT when the MM is enabled - await expect(rftCollection.mintToken(superuser)) - .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; - - // Can mint an RFT after disabling the MM - await rftCollection.mintToken(superuser); - }); - - itSub('MM allows native token transfers and RPC calls', async ({helper}) => { - // We can use RPC before the MM is enabled - const totalCount = await helper.collection.getTotalCount(); - - // We can transfer funds before the MM is enabled - await expect(helper.balance.transferToSubstrate(superuser, bob.address, 2n)).to.be.fulfilled; - - 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; - - // RPCs work while in maintenance - expect(await helper.collection.getTotalCount()).to.be.deep.equal(totalCount); - - // We still able to transfer funds - await expect(helper.balance.transferToSubstrate(bob, superuser.address, 1n)).to.be.fulfilled; - - 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; - - // RPCs work after maintenance - expect(await helper.collection.getTotalCount()).to.be.deep.equal(totalCount); - - // Transfers work after maintenance - await expect(helper.balance.transferToSubstrate(bob, superuser.address, 1n)).to.be.fulfilled; - }); - - itSched.ifWithPallets('MM blocks scheduled calls and the scheduler itself', [Pallets.UniqueScheduler], async (scheduleKind, {helper}) => { - const collection = await helper.nft.mintCollection(bob); - - const nftBeforeMM = await collection.mintToken(bob); - const nftDuringMM = await collection.mintToken(bob); - const nftAfterMM = await collection.mintToken(bob); - - const [ - scheduledIdBeforeMM, - scheduledIdDuringMM, - scheduledIdBunkerThroughMM, - scheduledIdAttemptDuringMM, - scheduledIdAfterMM, - ] = scheduleKind == 'named' - ? helper.arrange.makeScheduledIds(5) - : new Array(5); - - const blocksToWait = 6; - - // Scheduling works before the maintenance - 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 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 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; - - await helper.wait.newBlocks(blocksToWait + 1); - // The owner should NOT change since the scheduled transaction should be rejected - 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(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 helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAfterMM}) - .nft.transferToken(bob, collection.collectionId, nftAfterMM.tokenId, {Substrate: superuser.address}); - - await helper.wait.newBlocks(blocksToWait + 1); - - expect(await nftAfterMM.getOwner()).to.be.deep.equal({Substrate: superuser.address}); - // The owner of the token scheduled for transaction *before* maintenance should now change *after* maintenance - expect(await nftDuringMM.getOwner()).to.be.deep.equal({Substrate: superuser.address}); - }); - - itEth('Disallows Ethereum transactions to execute while in maintenance', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = helper.eth.createAccount(); - - const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'B', 'C', ''); - - // Set maintenance mode - 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; - - const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - const tokenId = await contract.methods.nextTokenId().call(); - expect(tokenId).to.be.equal('1'); - - await expect(contract.methods.mintWithTokenURI(receiver, 'Test URI').send()) - .to.be.rejectedWith(/Returned error: unknown error/); - - await expect(contract.methods.ownerOf(tokenId).call()).rejectedWith(/token not found/); - - // Disable maintenance mode - 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; - }); - - itSub('Allows to enable and disable MM repeatedly', async ({helper}) => { - // Set maintenance mode - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []); - 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; - - // Disable maintenance mode - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []); - 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; - }); - - afterEach(async () => { - await usingPlaygrounds(async helper => { - if(helper.fetchMissingPalletNames([Pallets.Maintenance]).length != 0) return; - if(await maintenanceEnabled(helper.getApi())) { - console.warn('\tMaintenance mode was left enabled AFTER a test has finished! Be careful. Disabling it now.'); - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []); - } - expect(await maintenanceEnabled(helper.getApi()), 'Disastrous! Exited the test suite with maintenance mode on.').to.be.false; - }); - }); - }); - - describe('Integration Test: Maintenance mode & App Promo', () => { - let superuser: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.Maintenance]); - superuser = await privateKey('//Alice'); - }); - }); - - describe('Test AppPromo script for check state after Maintenance mode', () => { - before(async function () { - await usingPlaygrounds(async (helper) => { - if(await maintenanceEnabled(helper.getApi())) { - console.warn('\tMaintenance mode was left enabled BEFORE the test suite! Disabling it now.'); - await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled; - } - }); - }); - itSub('Can find and fix inconsistent state', async ({helper}) => { - const api = helper.getApi(); - - await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [api.tx.system.setStorage([ - // pendingUnstake(1 -> [superuser.address, 100UNQ]) - ['0x42b67acb8bd223c60d0c8f621ffefc0ae280fa2db99bd3827aac976de75af95f5153cb1f00942ff401000000', - '0x04d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000010632d5ec76b0500000000000000'], - // pendingUnstake(2 -> [superuser.address, 100UNQ]) - ['0x42b67acb8bd223c60d0c8f621ffefc0ae280fa2db99bd3827aac976de75af95f9eb2dcce60f37a2702000000', - '0x04d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000010632d5ec76b0500000000000000'], - // Balances.freezes(superuser.address -> freeze with app promo id and 200 UNQ ) - ['0xc2261276cc9d1f8598ea4b6a74b15c2fb1c0eb12e038e5c7f91e120ed4b7ebf1de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d', - '0x046170707374616b656170707374616b65000020c65abc8ed70a00000000000000'], - ])]); - - expect((await api.query.appPromotion.pendingUnstake(1)).toJSON()).to.be.deep.equal([[superuser.address, '0x00000000000000056bc75e2d63100000']]); - expect((await api.query.appPromotion.pendingUnstake(2)).toJSON()).to.be.deep.equal([[superuser.address, '0x00000000000000056bc75e2d63100000']]); - expect((await api.query.balances.freezes(superuser.address) as Vec) - .map(lock => ({id: lock.id.toUtf8(), amount: lock.amount.toBigInt()}))) - .to.be.deep.equal([{id: 'appstakeappstake', amount: 200000000000000000000n}]); - await correctState(); - - expect((await api.query.appPromotion.pendingUnstake(1)).toJSON()).to.be.deep.equal([]); - expect((await api.query.appPromotion.pendingUnstake(2)).toJSON()).to.be.deep.equal([]); - expect((await api.query.balances.freezes(superuser.address)).toJSON()).to.be.deep.equal([]); - - }); - - itSub('(!negative test!) Only works when Maintenance mode is disabled', async({helper}) => { - await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', [])).to.be.fulfilled; - await expect(correctState()).to.be.rejectedWith('The network is still in maintenance mode'); - await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled; - }); - }); - }); - - after(async () => { - await usingPlaygrounds(async(helper) => { - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []); - }); - }); -}); --- a/js-packages/tests/src/migrations/942057-appPromotion/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Update Procedure - -- Enable maintenance mode -- [Collect migration data using ChainQL](#stakers-data-loading) -- ❗️❗️❗️ Initiate the runtime upgrade only at this point ❗️❗️❗️ -- Wait for the upgrade to complete -- [Execute offchain migration](#execute-offchain-migration) -- Disable maintenance mode - -## Stakers Data Loading - -Set the environment variable (WS_RPC). For example, ws://localhost:9944. Execute the following command: - -```sh -chainql --tla-str=chainUrl= stakersParser.jsonnet > output.json -``` - -where `` - is the network address. - -Example for Opal: - -```sh -chainql --tla-str=chainUrl=wss://eu-ws-opal.unique.network:443 stakersParser.jsonnet > output.json -``` - -To install chainql, execute the following command: - -```sh -cargo install chainql -``` - -## Execute offchain migration - -To run, you need to set an environment variables: -- `SUPERUSER_SEED` – the sudo key seed. -- `WS_RPC` – the network address - -Run the migration by executing the following command: - -```sh -npx ts-node --esm executeMigration.ts -``` --- a/js-packages/tests/src/migrations/942057-appPromotion/collectData.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {exec} from 'child_process'; -import path from 'path'; -import {dirname} from 'path'; -import {fileURLToPath} from 'url'; - -export const collectData = () => { - const dirName = dirname(fileURLToPath(import.meta.url)); - - const pathToScript = path.resolve(dirName, './stakersParser.jsonnet'); - const outputPath = path.resolve(dirName, './output.json'); - exec(`chainql --tla-str=chainUrl=ws://127.0.0.1:9944 ${pathToScript} > ${outputPath}`); -}; --- a/js-packages/tests/src/migrations/942057-appPromotion/executeMigration.ts +++ /dev/null @@ -1,11 +0,0 @@ -import {migrateLockedToFreeze} from './lockedToFreeze.js'; - - -const WS_RPC = process.env.WS_RPC || 'wss://ws-opal.unique.network:443'; -const SUPERUSER_SEED = process.env.SUPERUSER_SEED || ''; - -migrateLockedToFreeze({ - wsEndpoint: WS_RPC, - donorSeed: SUPERUSER_SEED, -}) - .catch(console.error); \ No newline at end of file --- a/js-packages/tests/src/migrations/942057-appPromotion/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type {Migration} from '../../util/frankensteinMigrate.js'; -import {collectData} from './collectData.js'; -import {migrateLockedToFreeze} from './lockedToFreeze.js'; - -export const migration: Migration = { - async before() { - await collectData(); - }, - async after() { - await migrateLockedToFreeze(); - }, -}; \ No newline at end of file --- a/js-packages/tests/src/migrations/942057-appPromotion/lockedToFreeze.ts +++ /dev/null @@ -1,259 +0,0 @@ -// import { usingApi, privateKey, onlySign } from "./../../load/lib"; -import * as fs from 'fs'; -import {usingPlaygrounds} from '../../util/index.js'; -import path, {dirname} from 'path'; -import {isInteger, parse} from 'lossless-json'; -import {fileURLToPath} from 'url'; -import config from '../../config.js'; - - -const WS_ENDPOINT = config.substrateUrl; -const DONOR_SEED = '//Alice'; -const UPDATE_IF_VERSION = 942057; - -export function customNumberParser(value: any) { - return isInteger(value) ? BigInt(value) : parseFloat(value); -} - -export const migrateLockedToFreeze = async(options: { wsEndpoint: string; donorSeed: string } = { - wsEndpoint: WS_ENDPOINT, - donorSeed: DONOR_SEED, -}) => { - await usingPlaygrounds(async (helper, privateKey) => { - const api = helper.getApi(); - // 1. Check version equal 942057 or skip - console.log((api.consts.system.version as any).specVersion.toNumber()); - if((api.consts.system.version as any).specVersion.toNumber() != UPDATE_IF_VERSION) { - console.log("Version isn't 942057."); - return; - } - - // 2. Get sudo signer - const signer = await privateKey(options.donorSeed); - console.log('2. Getting sudo:', signer.address); - - // 3. Parse data to migrate - console.log('3. Parsing chainql results...'); - const dirName = dirname(fileURLToPath(import.meta.url)); - const parsingResult = parse(fs.readFileSync(path.resolve(dirName, 'output.json'), 'utf-8'), undefined, customNumberParser); - - const chainqlImportData = parsingResult as { - address: string; - balance: string; - account: { - fee_frozen: string, - free: string, - misc_frozen: string, - reserved: string, - }, - locks: { - amount: string, - id: string, - }[], - stakes: object, - unstakes: object, - }[]; - testChainqlData(chainqlImportData); - - const stakers = chainqlImportData.map((i) => i.address); - - // 3.1 Split into chunks by 100 - console.log('3.1 Splitting into chunks...'); - const stakersChunks = chunk(stakers, 100); - console.log('3.1 Done, total chunks:', stakersChunks.length); - - // 4. Get signer/sudo nonce - console.log('4. Getting sudo nonce...'); - const signerAccount = ( - await api.query.system.account(signer.address) - ).toJSON() as any; - - let nonce: number = signerAccount.nonce; - console.log('4. Sudo nonce is:', nonce); - - // 5. Only sign upgradeAccounts-transactions for each chunk - console.log('5. Signing transactions...'); - const signedTxs = []; - for(const chunk of stakersChunks) { - const tx = api.tx.sudo.sudo(api.tx.appPromotion.upgradeAccounts(chunk)); - const signed = tx.sign(signer, { - blockHash: api.genesisHash, - genesisHash: api.genesisHash, - runtimeVersion: api.runtimeVersion, - nonce: nonce++, - }); - signedTxs.push(signed); - } - - // 6. Send all signed transactions - console.log('6. Sending transactions...'); - const promises = signedTxs.map((tx) => api.rpc.author.submitAndWatchExtrinsic(tx)); - // 6.1 Wait all transactions settled - console.log('6.1 Waiting all transactions settled...'); - const res = await Promise.allSettled(promises); - - console.log('Wait 5 blocks for transactions to be included in a block...'); - await helper.wait.newBlocks(5); - // 6.2 Filter failed transactions - console.log('6.2 Getting failed transactions...'); - const failedTx = res.filter((r) => r.status == 'rejected') as PromiseRejectedResult[]; - console.log('6.2. total failedTxs:', failedTx.length); - - // 6.3 Log the reasons of failed tx - for(const tx of failedTx) { - console.log(tx.reason); - } - - // 7. Check balances for 10 blocks: - console.log('7. Check balances...'); - let blocksLeft = 10; - let notMigrated = stakers; - const suspiciousAccounts = []; - do { - console.log('blocks left:', blocksLeft); - const _notMigrated: string[] = []; - console.log('accounts to migrate...', notMigrated.length); - for(const accountToMigrate of notMigrated) { - let accountMigrated = true; - // 7.0 get data from chainql: - const oldAccount = chainqlImportData.find(acc => acc.address === accountToMigrate); - if(!oldAccount) { - console.log('Cannot find old account data for', accountMigrated); - accountMigrated = false; - _notMigrated.push(accountToMigrate); - continue; - } - - // 7.1 system.account - const balance = await api.query.system.account(accountToMigrate) as any; - // new balances - const free = balance.data.free; - const reserved = balance.data.reserved; - const frozen = balance.data.frozen; - // old balances - const oldFree = oldAccount.account.free; - const oldReserved = oldAccount.account.reserved; - const oldFrozen = oldAccount.account.fee_frozen; - // asserts new = old - if(oldFree.toString() !== free.toString()) { - console.log('Old free !== New free, which is probably not a problem', oldFree.toString(), free.toString()); - suspiciousAccounts.push(accountToMigrate); - } - if(oldFrozen.toString() !== frozen.toString()) { - console.log('Old frozen !== New frozen, which is probably not a problem', oldFrozen.toString(), frozen.toString()); - suspiciousAccounts.push(accountToMigrate); - } - if(oldReserved.toString() !== reserved.toString()) { - console.log('Old reserved !== New reserved, which is probably not a problem', oldReserved.toString(), reserved.toString()); - suspiciousAccounts.push(accountToMigrate); - } - - // 7.2 balances.locks: no id appstake - const locks = await helper.balance.getLocked(accountToMigrate); - const appPromoLocks = locks.filter(lock => lock.id === 'appstake'); - if(appPromoLocks.length !== 0) { - console.log('Account still has app-promo lock'); - accountMigrated = false; - } - - // 7.3 balances.freezes set... - let freezes = await api.query.balances.freezes(accountToMigrate) as any; - freezes = freezes.map((freez: any) => ({id: freez.id.toString(), amount: freez.amount.toString()})); - if(!freezes) { - console.log('Account does not have freezes'); - accountMigrated = false; - } else { - const oldAppPromoLocks = oldAccount.locks.filter(l => l.id === '0x6170707374616b65'); // get app promo locks - // should be only one freez for each account - if(freezes.length !== 1) { - console.log('freezes.length !== 1 and old appPromoLocks.length', freezes.length, oldAppPromoLocks.length); - accountMigrated = false; - } else { - const appPromoFreez = freezes[0]; - // freez-amount should be equal to migrated lock amount - if(appPromoFreez.amount.toString() !== oldAppPromoLocks[0].amount.toString()) { - console.log('freezes amount !== old appPromoLocks amount', appPromoFreez.amount.toString(), oldAppPromoLocks[0].amount.toString()); - accountMigrated = false; - } - // freez id should be correct - if(appPromoFreez.id !== '0x6170707374616b656170707374616b65') { - console.log('Got freez with incorrect id:', appPromoFreez.id); - accountMigrated = false; - } - } - } - - // 7.4 Stakes number the same - const stakesNumber = await helper.staking.getStakesNumber({Substrate: accountToMigrate}); - const oldStakesNumber = oldAccount.stakes ? Object.keys(oldAccount.stakes).length : 0; - if(stakesNumber.toString() !== oldStakesNumber.toString()) { - console.log('Old stakes number !== New stakes number', oldStakesNumber, stakesNumber); - accountMigrated = false; - } - - // 7.5 Total pendingUnstake + total staked = old locked - const pendingUnstakes = await helper.staking.getPendingUnstake({Substrate: accountToMigrate}); - const totalStaked = await helper.staking.getTotalStaked({Substrate: accountToMigrate}); - const totalBalanceInAppPromo = pendingUnstakes + totalStaked; - if(totalBalanceInAppPromo.toString() !== oldAccount.balance.toString()) { - console.log('totalBalanceInAppPromo !== old locked in app promo', totalBalanceInAppPromo.toString(), oldAccount.balance.toString()); - accountMigrated = false; - } - - // 8 Add to not-migrated - if(!accountMigrated) { - console.log('Add to not migrated:', accountToMigrate); - _notMigrated.push(accountToMigrate); - } - } - - blocksLeft--; - notMigrated = _notMigrated; - await helper.wait.newBlocks(1); - } while(blocksLeft > 0 && notMigrated.length !== 0); - - console.log('Not migrated accounts...', notMigrated.length); - if(suspiciousAccounts.length > 0) { - console.log('Saving suspicious accounts to suspicious.json:'); - fs.writeFileSync('./suspicious.json', JSON.stringify(suspiciousAccounts)); - } - if(notMigrated.length > 0) { - console.log('Saving not migrated list to failed.json:'); - notMigrated.forEach(console.log); - fs.writeFileSync('./failed.json', JSON.stringify(notMigrated)); - process.exit(1); - } else { - console.log('Migration success'); - } - }, options.wsEndpoint); -}; - -const chunk = (arr: T[], size: number) => - Array.from({length: Math.ceil(arr.length / size)}, (_: any, i: number) => - arr.slice(i * size, i * size + size)); - -const testChainqlData = (data: any) => { - const wrongData = []; - for(const account of data) { - try { - if(account.address == null) throw Error('no address in data'); - if(account.balance == null) throw Error('no balance in data'); - if(account.account == null) throw Error('no account in data'); - if(account.account.fee_frozen == null) throw Error('no account.fee_frozen in data'); - if(account.account.misc_frozen == null) throw Error('no account.misc_frozen in data'); - if(account.account.free == null) throw Error('no account.free in data'); - if(account.account.reserved == null) throw Error('no account.reserved in data'); - if(account.locks == null) throw Error('no locks in data'); - if(account.locks[0].amount == null) throw Error('no locks.amount in data'); - if(account.locks[0].id == null) throw Error('no locks.id in data'); - } catch (error) { - wrongData.push(account.address); - console.log((error as Error).message, account.address); - } - if(wrongData.length > 0) { - console.log(data); - throw Error('Chainql data not correct'); - } - } - console.log('Chainql data correct'); -}; --- a/js-packages/tests/src/migrations/942057-appPromotion/stakersParser.jsonnet +++ /dev/null @@ -1,29 +0,0 @@ -function(chainUrl) - - local - state = cql.chain(chainUrl).latest, - locked_balances = state.Balances.Locks._preloadKeys, - accountsRaw = state.System.Account._preloadKeys, - stakers = state.AppPromotion.Staked._preloadKeys, - unstakes = state.AppPromotion.PendingUnstake._preloadKeys, - locks = [ - { [k]: std.filter(function(l) l.id == '0x6170707374616b65', locked_balances[k]) } - for k in std.objectFields(locked_balances) - ], - unstakersData = [ - { [pair[0]]: { block: block, value: pair[1] } } - - for block in std.objectFields(unstakes) - for pair in unstakes[block] - ], - non_empty_locks = std.prune(locks) - ; - - std.map(function(a) { - address: std.objectFields(a)[0], - balance: a[std.objectFields(a)[0]][0].amount, - account: accountsRaw[std.objectFields(a)[0]].data, - locks: locked_balances[std.objectFields(a)[0]], - stakes: if std.objectHas(stakers, std.objectFields(a)[0]) then stakers[std.objectFields(a)[0]] else null, - unstakes: std.filterMap(function(b) std.objectHas(b, std.objectFields(a)[0]), function(c) std.objectValues(c)[0], unstakersData), - }, non_empty_locks) --- a/js-packages/tests/src/migrations/correctStateAfterMaintenance.ts +++ /dev/null @@ -1,69 +0,0 @@ -import config from '../config.js'; -import {usingPlaygrounds} from '../util/index.js'; -import type {u32} from '@polkadot/types-codec'; - -const WS_ENDPOINT = config.substrateUrl; -const DONOR_SEED = '//Alice'; - -export const main = async(options: { wsEndpoint: string; donorSeed: string } = { - wsEndpoint: WS_ENDPOINT, - donorSeed: DONOR_SEED, -}) => { - await usingPlaygrounds(async (helper, privateKey) => { - const api = helper.getApi(); - - if((await api.query.maintenance.enabled()).valueOf()) { - throw Error('The network is still in maintenance mode'); - } - - const pendingBlocks = ( - await api.query.appPromotion.pendingUnstake.entries() - ).map(([k, _v]) => - (k.args[0] as u32).toNumber()); - - const currentBlock = (await api.query.system.number() as u32).toNumber(); - - const filteredBlocks = pendingBlocks.filter(b => currentBlock > b); - - if(filteredBlocks.length != 0) { - console.log(`During maintenance mode, ${filteredBlocks.length} block(s) were not processed. Number(s): ${filteredBlocks}`); - } else { - console.log('Nothing to change'); - return; - } - - const skippedBlocks = chunk(filteredBlocks, 10); - - const signer = await privateKey(options.donorSeed); - - const txs = skippedBlocks.map((b) => - api.tx.sudo.sudo(api.tx.appPromotion.forceUnstake(b))); - - - const promises = txs.map((tx) => () => helper.signTransaction(signer, tx)); - - await Promise.allSettled(promises.map((p) => p())); - - const failedBlocks: number[] = []; - let isSuccess = true; - - for(const b of filteredBlocks) { - if(((await api.query.appPromotion.pendingUnstake(b)).toJSON() as any[]).length != 0) { - failedBlocks.push(b); - isSuccess = false; - } - } - - if(isSuccess) { - console.log('Done. %d block(s) were processed.', filteredBlocks.length); - } else { - throw new Error(`Something went wrong. Block(s) have not been processed: ${failedBlocks}`); - } - - - }, options.wsEndpoint); -}; - -const chunk = (arr: T[], size: number) => - Array.from({length: Math.ceil(arr.length / size)}, (_: any, i: number) => - arr.slice(i * size, i * size + size)); --- a/js-packages/tests/src/migrations/runCheckState.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {main} from './correctStateAfterMaintenance.js'; - -main({ - wsEndpoint: process.env.WS_RPC!, - donorSeed: process.env.SUPERUSER_SEED!, -}).then(() => process.exit(0)) - .catch((e) => { - console.error(e); - process.exit(1); - }); --- a/js-packages/tests/src/nativeFungible.test.ts +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2019-2023 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, usingPlaygrounds} from './util/index.js'; - -describe('Native fungible', () => { - let root: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - root = await privateKey('//Alice'); - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - }); - }); - - itSub('destroy_collection()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.burn(alice)).to.be.rejectedWith('common.UnsupportedOperation'); - await expect(helper.executeExtrinsic(alice, 'api.tx.unique.destroyCollection', [0])).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('add_to_allow_list()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.addToAllowList(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('remove_from_allow_list()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.removeFromAllowList(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('change_collection_owner()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.changeOwner(alice, bob.address)).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('add_collection_admin()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.addAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('remove_collection_admin()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.removeAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('set_collection_sponsor()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.setSponsor(alice, bob.address)).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('confirm_sponsorship()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.confirmSponsorship(alice)).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('remove_collection_sponsor()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.removeSponsor(alice)).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('create_item()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.mint(alice, 100n, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('set_collection_properties()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.setProperties(alice, [{key: 'value'}])).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('delete_collection_properties()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.deleteProperties(alice, ['key'])).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('set_token_properties()', async ({helper}) => { - await expect(helper.executeExtrinsic( - alice, - 'api.tx.unique.setTokenProperties', - [0, 0, [{key: 'value'}]], - true, - )).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('delete_token_properties()', async ({helper}) => { - await expect(helper.executeExtrinsic( - alice, - 'api.tx.unique.deleteTokenProperties', - [0, 0, ['key']], - true, - )).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('set_transfers_enabled_flag()', async ({helper}) => { - await expect(helper.executeExtrinsic( - alice, - 'api.tx.unique.setTransfersEnabledFlag', - [0, true], - true, - )).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('burn_item()', async ({helper}) => { - await expect(helper.executeExtrinsic( - alice, - 'api.tx.unique.burnItem', - [0, 0, 100n], - true, - )).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('burn_from()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.burnTokens(alice, 100n)).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('transfer()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - const balanceAliceBefore = await helper.balance.getSubstrate(alice.address); - const balanceBobBefore = await helper.balance.getSubstrate(bob.address); - await collection.transfer(alice, {Substrate: bob.address}, 100n); - const balanceAliceAfter = await helper.balance.getSubstrate(alice.address); - const balanceBobAfter = await helper.balance.getSubstrate(bob.address); - expect(balanceAliceBefore - balanceAliceAfter > 100n).to.be.true; - expect(balanceBobAfter - balanceBobBefore === 100n).to.be.true; - }); - - itSub('transfer_from()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - const balanceAliceBefore = await helper.balance.getSubstrate(alice.address); - const balanceBobBefore = await helper.balance.getSubstrate(bob.address); - - await collection.transferFrom(alice, {Substrate: alice.address}, {Substrate: bob.address}, 100n); - - const balanceAliceAfter = await helper.balance.getSubstrate(alice.address); - const balanceBobAfter = await helper.balance.getSubstrate(bob.address); - expect(balanceAliceBefore - balanceAliceAfter > 100n).to.be.true; - expect(balanceBobAfter - balanceBobBefore === 100n).to.be.true; - - await expect(collection.transferFrom(alice, {Substrate: bob.address}, {Substrate: alice.address}, 100n)).to.be.rejectedWith('common.ApprovedValueTooLow'); - }); - - itSub('approve()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.approveTokens(alice, {Substrate: bob.address}, 100n)).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('approve_from()', async ({helper}) => { - await expect(helper.executeExtrinsic( - alice, - 'api.tx.unique.approveFrom', - [{Substrate: alice.address}, {Substrate: bob.address}, 0, 0, 100n], - true, - )).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('set_collection_limits()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.setLimits(alice, {accountTokenOwnershipLimit: 1})).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('set_collection_permissions()', async ({helper}) => { - const collection = helper.ft.getCollectionObject(0); - await expect(collection.setPermissions(alice, {access: 'AllowList'})).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('repartition()', async ({helper}) => { - await expect(helper.executeExtrinsic( - alice, - 'api.tx.unique.repartition', - [0, 0, 100n], - true, - )).to.be.rejectedWith('unique.RepartitionCalledOnNonRefungibleCollection'); - }); - - itSub('force_repair_collection()', async ({helper}) => { - await expect(helper.executeExtrinsic( - alice, - 'api.tx.unique.forceRepairCollection', - [0], - true, - )).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('force_repair_item()', async ({helper}) => { - await expect(helper.getSudo().executeExtrinsic( - root, - 'api.tx.unique.forceRepairItem', - [0, 0], - true, - )).to.be.rejectedWith('common.UnsupportedOperation'); - }); - - itSub('Nest into NFT token()', async ({helper}) => { - const nftCollection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const targetToken = await nftCollection.mintToken(alice); - - const collection = helper.ft.getCollectionObject(0); - await collection.transfer(alice, targetToken.nestingAccount(), 100n); - - await collection.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 100n); - }); -}); \ No newline at end of file --- a/js-packages/tests/src/nesting/collectionProperties.seqtest.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js'; - -describe('Integration Test: Collection Properties with sudo', () => { - let superuser: IKeyringPair; - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - superuser = await privateKey('//Alice'); - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([100n], donor); - }); - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'ft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => { - before(async function() { - // eslint-disable-next-line require-await - await usingPlaygrounds(async helper => { - requirePalletsOrSkip(this, helper, testSuite.requiredPallets); - }); - }); - - itSub('Repairing an unbroken collection\'s properties preserves the consumed space', async({helper}) => { - const properties = [ - {key: 'sea-creatures', value: 'mermaids'}, - {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'}, - ]; - const collection = await helper[testSuite.mode].mintCollection(alice, {properties}); - - const newProperty = {key: 'space', value: ' '.repeat(4096)}; - await collection.setProperties(alice, [newProperty]); - const originalSpace = await collection.getPropertiesConsumedSpace(); - expect(originalSpace).to.be.equal(sizeOfProperty(properties[0]) + sizeOfProperty(properties[1]) + sizeOfProperty(newProperty)); - - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true); - const recomputedSpace = await collection.getPropertiesConsumedSpace(); - expect(recomputedSpace).to.be.equal(originalSpace); - }); - })); -}); --- a/js-packages/tests/src/nesting/collectionProperties.test.ts +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js'; - -describe('Integration Test: Collection Properties', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([200n, 10n], donor); - }); - }); - - itSub('Properties are initially empty', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice); - expect(await collection.getProperties()).to.be.empty; - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'ft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => { - before(async function() { - // eslint-disable-next-line require-await - await usingPlaygrounds(async helper => { - requirePalletsOrSkip(this, helper, testSuite.requiredPallets); - }); - }); - - itSub('Sets properties for a collection', async ({helper}) => { - const collection = await helper[testSuite.mode].mintCollection(alice); - - // As owner - await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}])).to.be.fulfilled; - - await collection.addAdmin(alice, {Substrate: bob.address}); - - // As administrator - await expect(collection.setProperties(bob, [{key: 'black_hole'}])).to.be.fulfilled; - - const properties = await collection.getProperties(); - expect(properties).to.include.deep.members([ - {key: 'electron', value: 'come bond'}, - {key: 'black_hole', value: ''}, - ]); - }); - - itSub('Check valid names for collection properties keys', async ({helper}) => { - const collection = await helper[testSuite.mode].mintCollection(alice); - - // alpha symbols - await expect(collection.setProperties(alice, [{key: 'answer'}])).to.be.fulfilled; - - // numeric symbols - await expect(collection.setProperties(alice, [{key: '451'}])).to.be.fulfilled; - - // underscore symbol - await expect(collection.setProperties(alice, [{key: 'black_hole'}])).to.be.fulfilled; - - // dash symbol - await expect(collection.setProperties(alice, [{key: '-'}])).to.be.fulfilled; - - // dot symbol - await expect(collection.setProperties(alice, [{key: 'once.in.a.long.long.while...', value: 'you get a little lost'}])).to.be.fulfilled; - - const properties = await collection.getProperties(); - expect(properties).to.include.deep.members([ - {key: 'answer', value: ''}, - {key: '451', value: ''}, - {key: 'black_hole', value: ''}, - {key: '-', value: ''}, - {key: 'once.in.a.long.long.while...', value: 'you get a little lost'}, - ]); - }); - - itSub('Changes properties of a collection', async ({helper}) => { - const collection = await helper[testSuite.mode].mintCollection(alice); - - await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: ''}])).to.be.fulfilled; - - // Mutate the properties - await expect(collection.setProperties(alice, [{key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled; - - const properties = await collection.getProperties(); - expect(properties).to.include.deep.members([ - {key: 'electron', value: 'come bond'}, - {key: 'black_hole', value: 'LIGO'}, - ]); - }); - - itSub('Deletes properties of a collection', async ({helper}) => { - const collection = await helper[testSuite.mode].mintCollection(alice); - - await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled; - - await expect(collection.deleteProperties(alice, ['electron'])).to.be.fulfilled; - - const properties = await collection.getProperties(['black_hole', 'electron']); - expect(properties).to.be.deep.equal([ - {key: 'black_hole', value: 'LIGO'}, - ]); - }); - - itSub('Allows modifying a collection property multiple times with the same size', async({helper}) => { - const propKey = 'tok-prop'; - - const collection = await helper[testSuite.mode].mintCollection(alice); - - const maxCollectionPropertiesSize = 40960; - - const propDataSize = 4096; - - let propDataChar = 'a'; - const makeNewPropData = () => { - propDataChar = String.fromCharCode(propDataChar.charCodeAt(0) + 1); - return `${propDataChar}`.repeat(propDataSize); - }; - - const property = {key: propKey, value: makeNewPropData()}; - await collection.setProperties(alice, [property]); - const originalSpace = await collection.getPropertiesConsumedSpace(); - expect(originalSpace).to.be.equal(sizeOfProperty(property)); - - const sameSizePropertiesPossibleNum = maxCollectionPropertiesSize / propDataSize; - - // It is possible to modify a property as many times as needed. - // It will not consume any additional space. - for(let i = 0; i < sameSizePropertiesPossibleNum + 1; i++) { - await collection.setProperties(alice, [{key: propKey, value: makeNewPropData()}]); - const consumedSpace = await collection.getPropertiesConsumedSpace(); - expect(consumedSpace).to.be.equal(originalSpace); - } - }); - - itSub('Adding then removing a collection property doesn\'t change the consumed space', async({helper}) => { - const propKey = 'tok-prop'; - - const collection = await helper[testSuite.mode].mintCollection(alice); - const originalSpace = await collection.getPropertiesConsumedSpace(); - - const propDataSize = 4096; - const propData = 'a'.repeat(propDataSize); - - const property = {key: propKey, value: propData}; - await collection.setProperties(alice, [property]); - let consumedSpace = await collection.getPropertiesConsumedSpace(); - expect(consumedSpace).to.be.equal(sizeOfProperty(property)); - - await collection.deleteProperties(alice, [propKey]); - consumedSpace = await collection.getPropertiesConsumedSpace(); - expect(consumedSpace).to.be.equal(originalSpace); - }); - - itSub('Modifying a collection property with different sizes correctly changes the consumed space', async({helper}) => { - const propKey = 'tok-prop'; - - const collection = await helper[testSuite.mode].mintCollection(alice); - const originalSpace = await collection.getPropertiesConsumedSpace(); - - const initProp = {key: propKey, value: 'a'.repeat(4096)}; - const biggerProp = {key: propKey, value: 'b'.repeat(5000)}; - const smallerProp = {key: propKey, value: 'c'.repeat(4000)}; - - let consumedSpace; - let expectedConsumedSpaceDiff; - - await collection.setProperties(alice, [initProp]); - consumedSpace = await collection.getPropertiesConsumedSpace(); - expectedConsumedSpaceDiff = sizeOfProperty(initProp) - originalSpace; - expect(consumedSpace).to.be.equal(originalSpace + expectedConsumedSpaceDiff); - - await collection.setProperties(alice, [biggerProp]); - consumedSpace = await collection.getPropertiesConsumedSpace(); - expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(initProp); - expect(consumedSpace).to.be.equal(sizeOfProperty(initProp) + expectedConsumedSpaceDiff); - - await collection.setProperties(alice, [smallerProp]); - consumedSpace = await collection.getPropertiesConsumedSpace(); - expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(smallerProp); - expect(consumedSpace).to.be.equal(sizeOfProperty(biggerProp) - expectedConsumedSpaceDiff); - }); - })); -}); - -describe('Negative Integration Test: Collection Properties', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([1000n, 100n], donor); - }); - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'ft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => { - before(async function() { - // eslint-disable-next-line require-await - await usingPlaygrounds(async helper => { - requirePalletsOrSkip(this, helper, testSuite.requiredPallets); - }); - }); - - itSub('Fails to set properties in a collection if not its onwer/administrator', async ({helper}) => { - const collection = await helper[testSuite.mode].mintCollection(alice); - - await expect(collection.setProperties(bob, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}])) - .to.be.rejectedWith(/common\.NoPermission/); - - expect(await collection.getProperties()).to.be.empty; - }); - - itSub('Fails to set properties that exceed the limits', async ({helper}) => { - const collection = await helper[testSuite.mode].mintCollection(alice); - - const spaceLimit = (helper.getApi().consts.unique.maxCollectionPropertiesSize as any).toNumber(); - - // Mute the general tx parsing error, too many bytes to process - { - console.error = () => {}; - await expect(collection.setProperties(alice, [ - {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}, - ])).to.be.rejected; - } - - expect(await collection.getProperties(['electron'])).to.be.empty; - - await expect(collection.setProperties(alice, [ - {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, - {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, - ])).to.be.rejectedWith(/common\.NoSpaceForProperty/); - - expect(await collection.getProperties(['electron', 'black_hole'])).to.be.empty; - }); - - itSub('Fails to set more properties than it is allowed', async ({helper}) => { - const collection = await helper[testSuite.mode].mintCollection(alice); - - const propertiesToBeSet = []; - for(let i = 0; i < 65; i++) { - propertiesToBeSet.push({ - key: 'electron_' + i, - value: Math.random() > 0.5 ? 'high' : 'low', - }); - } - - await expect(collection.setProperties(alice, propertiesToBeSet)). - to.be.rejectedWith(/common\.PropertyLimitReached/); - - expect(await collection.getProperties()).to.be.empty; - }); - - itSub('Fails to set properties with invalid names', async ({helper}) => { - const collection = await helper[testSuite.mode].mintCollection(alice); - - const invalidProperties = [ - [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}], - [{key: 'Mr/Sandman', value: 'Bring me a gene'}], - [{key: 'déjà vu', value: 'hmm...'}], - ]; - - for(let i = 0; i < invalidProperties.length; i++) { - await expect( - collection.setProperties(alice, invalidProperties[i]), - `on rejecting the new badly-named property #${i}`, - ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/); - } - - await expect( - collection.setProperties(alice, [{key: '', value: 'nothing must not exist'}]), - 'on rejecting an unnamed property', - ).to.be.rejectedWith(/common\.EmptyPropertyKey/); - - await expect( - collection.setProperties(alice, [{key: 'CRISPR-Cas9', value: 'rewriting nature!'}]), - 'on setting the correctly-but-still-badly-named property', - ).to.be.fulfilled; - - const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat(''); - - const properties = await collection.getProperties(keys); - expect(properties).to.be.deep.equal([ - {key: 'CRISPR-Cas9', value: 'rewriting nature!'}, - ]); - - for(let i = 0; i < invalidProperties.length; i++) { - await expect( - collection.deleteProperties(alice, invalidProperties[i].map(propertySet => propertySet.key)), - `on trying to delete the non-existent badly-named property #${i}`, - ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/); - } - }); - - itSub('Forbids to repair a collection if called with non-sudo', async({helper}) => { - const collection = await helper[testSuite.mode].mintCollection(alice, {properties: [ - {key: 'sea-creatures', value: 'mermaids'}, - {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'}, - ]}); - - await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true)) - .to.be.rejectedWith(/BadOrigin/); - }); - })); -}); --- a/js-packages/tests/src/nesting/graphs.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, usingPlaygrounds} from '../util/index.js'; -import {UniqueHelper, UniqueNFTCollection, UniqueNFToken} from '@unique/playgrounds/src/unique.js'; - -/** - * ```dot - * 4 -> 3 -> 2 -> 1 - * 7 -> 6 -> 5 -> 2 - * 8 -> 5 - * ``` - */ -async function buildComplexObjectGraph(helper: UniqueHelper, sender: IKeyringPair): Promise<[UniqueNFTCollection,UniqueNFToken[]]> { - const collection = await helper.nft.mintCollection(sender, {permissions: {nesting: {tokenOwner: true}}}); - const tokens = await collection.mintMultipleTokens(sender, Array(8).fill({owner: {Substrate: sender.address}})); - - await tokens[7].nest(sender, tokens[4]); - await tokens[6].nest(sender, tokens[5]); - await tokens[5].nest(sender, tokens[4]); - await tokens[4].nest(sender, tokens[1]); - await tokens[3].nest(sender, tokens[2]); - await tokens[2].nest(sender, tokens[1]); - await tokens[1].nest(sender, tokens[0]); - - return [collection, tokens]; -} - -describe('Graphs', () => { - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([10n], donor); - }); - }); - - itSub('Ouroboros can\'t be created in a complex graph', async ({helper}) => { - const [collection, tokens] = await buildComplexObjectGraph(helper, alice); - - await collection.setPermissions(alice, {nesting: {collectionAdmin: false, tokenOwner: true}}); - - // [token owner] to self - await expect( - tokens[0].nest(alice, tokens[0]), - '[token owner] self-nesting is forbidden', - ).to.be.rejectedWith(/structure\.OuroborosDetected/); - // [token owner] to nested part of graph - await expect( - tokens[0].nest(alice, tokens[4]), - '[token owner] cannot nest the root node into an internal node', - ).to.be.rejectedWith(/structure\.OuroborosDetected/); - await expect( - tokens[1].transferFrom(alice, tokens[0].nestingAccount(), tokens[7].nestingAccount()), - '[token owner] cannot nest higher internal node into lower internal node', - ).to.be.rejectedWith(/structure\.OuroborosDetected/); - - await collection.setPermissions(alice, {nesting: {collectionAdmin: true, tokenOwner: false}}); - - // [collection owner] to self - await expect( - tokens[0].nest(alice, tokens[0]), - '[collection owner] self-nesting is forbidden', - ).to.be.rejectedWith(/structure\.OuroborosDetected/); - // [collection owner] to nested part of graph - await expect( - tokens[0].nest(alice, tokens[4]), - '[collection owner] cannot nest the root node into an internal node', - ).to.be.rejectedWith(/structure\.OuroborosDetected/); - }); -}); --- a/js-packages/tests/src/nesting/propertyPermissions.test.ts +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, Pallets, usingPlaygrounds, expect} from '../util/index.js'; -import {UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/src/unique.js'; - -describe('Integration Test: Access Rights to Token Properties', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); - }); - }); - - itSub('Reads access rights to properties of a collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice); - const propertyRights = (await helper.callRpc('api.query.common.collectionPropertyPermissions', [collection.collectionId])).toJSON(); - expect(propertyRights).to.be.empty; - }); - - async function testSetsAccessRightsToProperties(collection: UniqueNFTCollection | UniqueRFTCollection) { - await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true}}])) - .to.be.fulfilled; - - await collection.addAdmin(alice, {Substrate: bob.address}); - - await expect(collection.setTokenPropertyPermissions(bob, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}])) - .to.be.fulfilled; - - const propertyRights = await collection.getPropertyPermissions(['skullduggery', 'mindgame']); - expect(propertyRights).to.include.deep.members([ - {key: 'skullduggery', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}, - {key: 'mindgame', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}}, - ]); - } - - itSub('Sets access rights to properties of a collection (NFT)', async ({helper}) => { - await testSetsAccessRightsToProperties(await helper.nft.mintCollection(alice)); - }); - - itSub.ifWithPallets('Sets access rights to properties of a collection (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - await testSetsAccessRightsToProperties(await helper.rft.mintCollection(alice)); - }); - - async function testChangesAccessRightsToProperty(collection: UniqueNFTCollection | UniqueRFTCollection) { - await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}])) - .to.be.fulfilled; - - await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}])) - .to.be.fulfilled; - - const propertyRights = await collection.getPropertyPermissions(); - expect(propertyRights).to.be.deep.equal([ - {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}}, - ]); - } - - itSub('Changes access rights to properties of a NFT collection', async ({helper}) => { - await testChangesAccessRightsToProperty(await helper.nft.mintCollection(alice)); - }); - - itSub.ifWithPallets('Changes access rights to properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => { - await testChangesAccessRightsToProperty(await helper.rft.mintCollection(alice)); - }); -}); - -describe('Negative Integration Test: Access Rights to Token Properties', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); - }); - }); - - async function testPreventsFromSettingAccessRightsNotAdminOrOwner(collection: UniqueNFTCollection | UniqueRFTCollection) { - await expect(collection.setTokenPropertyPermissions(bob, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}])) - .to.be.rejectedWith(/common\.NoPermission/); - - const propertyRights = await collection.getPropertyPermissions(['skullduggery']); - expect(propertyRights).to.be.empty; - } - - itSub('Prevents from setting access rights to properties of a NFT collection if not an onwer/admin', async ({helper}) => { - await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.nft.mintCollection(alice)); - }); - - itSub.ifWithPallets('Prevents from setting access rights to properties of a ReFungible collection if not an onwer/admin', [Pallets.ReFungible], async ({helper}) => { - await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.rft.mintCollection(alice)); - }); - - async function testPreventFromAddingTooManyPossibleProperties(collection: UniqueNFTCollection | UniqueRFTCollection) { - const constitution = []; - for(let i = 0; i < 65; i++) { - constitution.push({ - key: 'property_' + i, - permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {}, - }); - } - - await expect(collection.setTokenPropertyPermissions(alice, constitution)) - .to.be.rejectedWith(/common\.PropertyLimitReached/); - - const propertyRights = await collection.getPropertyPermissions(); - expect(propertyRights).to.be.empty; - } - - itSub('Prevents from adding too many possible properties (NFT)', async ({helper}) => { - await testPreventFromAddingTooManyPossibleProperties(await helper.nft.mintCollection(alice)); - }); - - itSub.ifWithPallets('Prevents from adding too many possible properties (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - await testPreventFromAddingTooManyPossibleProperties(await helper.rft.mintCollection(alice)); - }); - - async function testPreventAccessRightsModifiedIfConstant(collection: UniqueNFTCollection | UniqueRFTCollection) { - await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}])) - .to.be.fulfilled; - - await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {collectionAdmin: true}}])) - .to.be.rejectedWith(/common\.NoPermission/); - - const propertyRights = await collection.getPropertyPermissions(['skullduggery']); - expect(propertyRights).to.deep.equal([ - {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}}, - ]); - } - - itSub('Prevents access rights to be modified if constant (NFT)', async ({helper}) => { - await testPreventAccessRightsModifiedIfConstant(await helper.nft.mintCollection(alice)); - }); - - itSub.ifWithPallets('Prevents access rights to be modified if constant (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - await testPreventAccessRightsModifiedIfConstant(await helper.rft.mintCollection(alice)); - }); - - async function testPreventsAddingPropertiesWithInvalidNames(collection: UniqueNFTCollection | UniqueRFTCollection) { - const invalidProperties = [ - [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}], - [{key: 'G#4', permission: {tokenOwner: true}}], - [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}], - ]; - - for(let i = 0; i < invalidProperties.length; i++) { - await expect( - collection.setTokenPropertyPermissions(alice, invalidProperties[i]), - `on setting the new badly-named property #${i}`, - ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/); - } - - await expect( - collection.setTokenPropertyPermissions(alice, [{key: '', permission: {}}]), - 'on rejecting an unnamed property', - ).to.be.rejectedWith(/common\.EmptyPropertyKey/); - - const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string - await expect( - collection.setTokenPropertyPermissions(alice, [ - {key: correctKey, permission: {collectionAdmin: true}}, - ]), - 'on setting the correctly-but-still-badly-named property', - ).to.be.fulfilled; - - const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat(''); - - const propertyRights = await collection.getPropertyPermissions(keys); - expect(propertyRights).to.be.deep.equal([ - {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}}, - ]); - } - - itSub('Prevents adding properties with invalid names (NFT)', async ({helper}) => { - await testPreventsAddingPropertiesWithInvalidNames(await helper.nft.mintCollection(alice)); - }); - - itSub.ifWithPallets('Prevents adding properties with invalid names (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - await testPreventsAddingPropertiesWithInvalidNames(await helper.rft.mintCollection(alice)); - }); -}); --- a/js-packages/tests/src/nesting/tokenProperties.seqtest.ts +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js'; - -describe('Integration Test: Token Properties with sudo', () => { - let superuser: IKeyringPair; - let alice: IKeyringPair; // collection owner - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - superuser = await privateKey('//Alice'); - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([100n], donor); - }); - }); - - [ - {mode: 'nft' as const, pieces: undefined, requiredPallets: []} as const, - {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]} as const, - ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => { - before(async function() { - // eslint-disable-next-line require-await - await usingPlaygrounds(async helper => { - requirePalletsOrSkip(this, helper, testSuite.requiredPallets); - }); - }); - - itSub('force_repair_item preserves valid consumed space', async({helper}) => { - const propKey = 'tok-prop'; - - const collection = await helper[testSuite.mode].mintCollection(alice, { - tokenPropertyPermissions: [ - { - key: propKey, - permission: {mutable: true, tokenOwner: true}, - }, - ], - }); - const token = await ( - testSuite.pieces - ? collection.mintToken(alice, testSuite.pieces as any) - : collection.mintToken(alice) - ); - - const prop = {key: propKey, value: 'a'.repeat(4096)}; - - await token.setProperties(alice, [prop]); - const originalSpace = await token.getTokenPropertiesConsumedSpace(); - expect(originalSpace).to.be.equal(sizeOfProperty(prop)); - - await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true); - const recomputedSpace = await token.getTokenPropertiesConsumedSpace(); - expect(recomputedSpace).to.be.equal(originalSpace); - }); - })); -}); --- a/js-packages/tests/src/nesting/tokenProperties.test.ts +++ /dev/null @@ -1,816 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect, sizeOfProperty} from '../util/index.js'; -import {UniqueHelper, UniqueNFToken, UniqueRFToken} from '@unique/playgrounds/src/unique.js'; - -describe('Integration Test: Token Properties', () => { - let alice: IKeyringPair; // collection owner - let bob: IKeyringPair; // collection admin - let charlie: IKeyringPair; // token owner - - let permissions: {permission: any, signers: IKeyringPair[]}[]; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 100n, 100n], donor); - }); - - permissions = [ - {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]}, - {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]}, - {permission: {mutable: true, tokenOwner: true}, signers: [charlie]}, - {permission: {mutable: false, tokenOwner: true}, signers: [charlie]}, - {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]}, - {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]}, - ]; - }); - - async function mintCollectionWithAllPermissionsAndToken(helper: UniqueHelper, mode: 'NFT' | 'RFT'): Promise<[UniqueNFToken | UniqueRFToken, bigint]> { - const collection = await (mode == 'NFT' ? helper.nft : helper.rft).mintCollection(alice, { - tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) => - signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))), - }); - return mode == 'NFT' ? [await collection.mintToken(alice), 1n] : [await collection.mintToken(alice, 100n as any), 100n]; - } - - async function testReadsYetEmptyProperties(token: UniqueNFToken | UniqueRFToken) { - const properties = await token.getProperties(); - expect(properties).to.be.empty; - - const tokenData = await token.getData(); - expect(tokenData!.properties).to.be.empty; - } - - itSub('Reads yet empty properties of a token (NFT)', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice); - const token = await collection.mintToken(alice); - await testReadsYetEmptyProperties(token); - }); - - itSub.ifWithPallets('Reads yet empty properties of a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice); - const token = await collection.mintToken(alice); - await testReadsYetEmptyProperties(token); - }); - - async function testAssignPropertiesAccordingToPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { - await token.collection.addAdmin(alice, {Substrate: bob.address}); - await token.transfer(alice, {Substrate: charlie.address}, pieces); - - const propertyKeys: string[] = []; - let i = 0; - for(const permission of permissions) { - i++; - let j = 0; - for(const signer of permission.signers) { - j++; - const key = i + '_' + signer.address; - propertyKeys.push(key); - - await expect( - token.setProperties(signer, [{key: key, value: 'Serotonin increase'}]), - `on adding property #${i} by signer #${j}`, - ).to.be.fulfilled; - } - } - - const properties = await token.getProperties(propertyKeys); - const tokenData = await token.getData(); - for(let i = 0; i < properties.length; i++) { - expect(properties[i].value).to.be.equal('Serotonin increase'); - expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase'); - } - } - - itSub('Assigns properties to a token according to permissions (NFT)', async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); - await testAssignPropertiesAccordingToPermissions(token, amount); - }); - - itSub.ifWithPallets('Assigns properties to a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); - await testAssignPropertiesAccordingToPermissions(token, amount); - }); - - async function testChangesPropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { - await token.collection.addAdmin(alice, {Substrate: bob.address}); - await token.transfer(alice, {Substrate: charlie.address}, pieces); - - const propertyKeys: string[] = []; - let i = 0; - for(const permission of permissions) { - i++; - if(!permission.permission.mutable) continue; - - let j = 0; - for(const signer of permission.signers) { - j++; - const key = i + '_' + signer.address; - propertyKeys.push(key); - - await expect( - token.setProperties(signer, [{key, value: 'Serotonin increase'}]), - `on adding property #${i} by signer #${j}`, - ).to.be.fulfilled; - - await expect( - token.setProperties(signer, [{key, value: 'Serotonin stable'}]), - `on changing property #${i} by signer #${j}`, - ).to.be.fulfilled; - } - } - - const properties = await token.getProperties(propertyKeys); - const tokenData = await token.getData(); - for(let i = 0; i < properties.length; i++) { - expect(properties[i].value).to.be.equal('Serotonin stable'); - expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable'); - } - } - - itSub('Changes properties of a token according to permissions (NFT)', async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); - await testChangesPropertiesAccordingPermission(token, amount); - }); - - itSub.ifWithPallets('Changes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); - await testChangesPropertiesAccordingPermission(token, amount); - }); - - async function testDeletePropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { - await token.collection.addAdmin(alice, {Substrate: bob.address}); - await token.transfer(alice, {Substrate: charlie.address}, pieces); - - const propertyKeys: string[] = []; - let i = 0; - - for(const permission of permissions) { - i++; - if(!permission.permission.mutable) continue; - - let j = 0; - for(const signer of permission.signers) { - j++; - const key = i + '_' + signer.address; - propertyKeys.push(key); - - await expect( - token.setProperties(signer, [{key, value: 'Serotonin increase'}]), - `on adding property #${i} by signer #${j}`, - ).to.be.fulfilled; - - await expect( - token.deleteProperties(signer, [key]), - `on deleting property #${i} by signer #${j}`, - ).to.be.fulfilled; - } - } - - expect(await token.getProperties(propertyKeys)).to.be.empty; - expect((await token.getData())!.properties).to.be.empty; - } - - itSub('Deletes properties of a token according to permissions (NFT)', async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); - await testDeletePropertiesAccordingPermission(token, amount); - }); - - itSub.ifWithPallets('Deletes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); - await testDeletePropertiesAccordingPermission(token, amount); - }); - - itSub('Assigns properties to a nested token according to permissions', async ({helper}) => { - const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const collectionB = await helper.nft.mintCollection(alice, { - tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) => - signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))), - }); - const targetToken = await collectionA.mintToken(alice); - const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount()); - - await collectionB.addAdmin(alice, {Substrate: bob.address}); - await targetToken.transfer(alice, {Substrate: charlie.address}); - - const propertyKeys: string[] = []; - let i = 0; - for(const permission of permissions) { - i++; - let j = 0; - for(const signer of permission.signers) { - j++; - const key = i + '_' + signer.address; - propertyKeys.push(key); - - await expect( - nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), - `on adding property #${i} by signer #${j}`, - ).to.be.fulfilled; - } - } - - const properties = await nestedToken.getProperties(propertyKeys); - const tokenData = await nestedToken.getData(); - for(let i = 0; i < properties.length; i++) { - expect(properties[i].value).to.be.equal('Serotonin increase'); - expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase'); - } - expect(await targetToken.getProperties()).to.be.empty; - }); - - itSub('Changes properties of a nested token according to permissions', async ({helper}) => { - const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const collectionB = await helper.nft.mintCollection(alice, { - tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) => - signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))), - }); - const targetToken = await collectionA.mintToken(alice); - const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount()); - - await collectionB.addAdmin(alice, {Substrate: bob.address}); - await targetToken.transfer(alice, {Substrate: charlie.address}); - - const propertyKeys: string[] = []; - let i = 0; - for(const permission of permissions) { - i++; - if(!permission.permission.mutable) continue; - - let j = 0; - for(const signer of permission.signers) { - j++; - const key = i + '_' + signer.address; - propertyKeys.push(key); - - await expect( - nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), - `on adding property #${i} by signer #${j}`, - ).to.be.fulfilled; - - await expect( - nestedToken.setProperties(signer, [{key, value: 'Serotonin stable'}]), - `on changing property #${i} by signer #${j}`, - ).to.be.fulfilled; - } - } - - const properties = await nestedToken.getProperties(propertyKeys); - const tokenData = await nestedToken.getData(); - for(let i = 0; i < properties.length; i++) { - expect(properties[i].value).to.be.equal('Serotonin stable'); - expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable'); - } - expect(await targetToken.getProperties()).to.be.empty; - }); - - itSub('Deletes properties of a nested token according to permissions', async ({helper}) => { - const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const collectionB = await helper.nft.mintCollection(alice, { - tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) => - signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))), - }); - const targetToken = await collectionA.mintToken(alice); - const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount()); - - await collectionB.addAdmin(alice, {Substrate: bob.address}); - await targetToken.transfer(alice, {Substrate: charlie.address}); - - const propertyKeys: string[] = []; - let i = 0; - for(const permission of permissions) { - i++; - if(!permission.permission.mutable) continue; - - let j = 0; - for(const signer of permission.signers) { - j++; - const key = i + '_' + signer.address; - propertyKeys.push(key); - - await expect( - nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), - `on adding property #${i} by signer #${j}`, - ).to.be.fulfilled; - - await expect( - nestedToken.deleteProperties(signer, [key]), - `on deleting property #${i} by signer #${j}`, - ).to.be.fulfilled; - } - } - - expect(await nestedToken.getProperties(propertyKeys)).to.be.empty; - expect((await nestedToken.getData())!.properties).to.be.empty; - expect(await targetToken.getProperties()).to.be.empty; - }); - - [ - {mode: 'nft' as const, storage: 'nonfungible' as const, pieces: undefined, requiredPallets: []} as const, - {mode: 'rft' as const, storage: 'refungible' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]} as const, - ].map(testCase => - itSub.ifWithPallets(`Allows modifying a token property multiple times with the same size (${testCase.mode})`, testCase.requiredPallets, async({helper}) => { - const propKey = 'tok-prop'; - - const collection = await helper[testCase.mode].mintCollection(alice, { - tokenPropertyPermissions: [ - { - key: propKey, - permission: {mutable: true, tokenOwner: true}, - }, - ], - }); - - const maxTokenPropertiesSize = 32768; - - const propDataSize = 4096; - - let propDataChar = 'a'; - const makeNewPropData = () => { - propDataChar = String.fromCharCode(propDataChar.charCodeAt(0) + 1); - return `${propDataChar}`.repeat(propDataSize); - }; - - const token = await ( - testCase.pieces - ? collection.mintToken(alice, testCase.pieces as any) - : collection.mintToken(alice) - ); - - const property = {key: propKey, value: makeNewPropData()}; - await token.setProperties(alice, [property]); - const originalSpace = await token.getTokenPropertiesConsumedSpace(); - expect(originalSpace).to.be.equal(sizeOfProperty(property)); - - const sameSizePropertiesPossibleNum = maxTokenPropertiesSize / propDataSize; - - // It is possible to modify a property as many times as needed. - // It will not consume any additional space. - for(let i = 0; i < sameSizePropertiesPossibleNum + 1; i++) { - await token.setProperties(alice, [{key: propKey, value: makeNewPropData()}]); - const consumedSpace = await token.getTokenPropertiesConsumedSpace(); - expect(consumedSpace).to.be.equal(originalSpace); - } - })); - - [ - {mode: 'nft' as const, pieces: undefined, requiredPallets: []}, - {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itSub.ifWithPallets(`Adding then removing a token property doesn't change the consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => { - const propKey = 'tok-prop'; - - const collection = await helper[testCase.mode].mintCollection(alice, { - tokenPropertyPermissions: [ - { - key: propKey, - permission: {mutable: true, tokenOwner: true}, - }, - ], - }); - const token = await ( - testCase.pieces - ? collection.mintToken(alice, testCase.pieces as any) - : collection.mintToken(alice) - ); - const originalSpace = await token.getTokenPropertiesConsumedSpace(); - - const propDataSize = 4096; - const propData = 'a'.repeat(propDataSize); - - const property = {key: propKey, value: propData}; - await token.setProperties(alice, [property]); - let consumedSpace = await token.getTokenPropertiesConsumedSpace(); - expect(consumedSpace).to.be.equal(sizeOfProperty(property)); - - await token.deleteProperties(alice, [propKey]); - consumedSpace = await token.getTokenPropertiesConsumedSpace(); - expect(consumedSpace).to.be.equal(originalSpace); - })); - - [ - {mode: 'nft' as const, pieces: undefined, requiredPallets: []}, - {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itSub.ifWithPallets(`Modifying a token property with different sizes correctly changes the consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => { - const propKey = 'tok-prop'; - - const collection = await helper[testCase.mode].mintCollection(alice, { - tokenPropertyPermissions: [ - { - key: propKey, - permission: {mutable: true, tokenOwner: true}, - }, - ], - }); - const token = await ( - testCase.pieces - ? collection.mintToken(alice, testCase.pieces as any) - : collection.mintToken(alice) - ); - const originalSpace = await token.getTokenPropertiesConsumedSpace(); - - const initProp = {key: propKey, value: 'a'.repeat(4096)}; - const biggerProp = {key: propKey, value: 'b'.repeat(5000)}; - const smallerProp = {key: propKey, value: 'c'.repeat(4000)}; - - let consumedSpace; - let expectedConsumedSpaceDiff; - - await token.setProperties(alice, [initProp]); - consumedSpace = await token.getTokenPropertiesConsumedSpace(); - expectedConsumedSpaceDiff = sizeOfProperty(initProp) - originalSpace; - expect(consumedSpace).to.be.equal(originalSpace + expectedConsumedSpaceDiff); - - await token.setProperties(alice, [biggerProp]); - consumedSpace = await token.getTokenPropertiesConsumedSpace(); - expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(initProp); - expect(consumedSpace).to.be.equal(sizeOfProperty(initProp) + expectedConsumedSpaceDiff); - - await token.setProperties(alice, [smallerProp]); - consumedSpace = await token.getTokenPropertiesConsumedSpace(); - expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(smallerProp); - expect(consumedSpace).to.be.equal(sizeOfProperty(biggerProp) - expectedConsumedSpaceDiff); - })); - - itSub('Set sponsored properties', async({helper}) => { - const collection = await helper.nft.mintCollection(alice, {tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: true}}]}); - - await collection.setSponsor(alice, alice.address); - await collection.confirmSponsorship(alice); - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await collection.addToAllowList(alice, {Substrate: bob.address}); - await collection.setLimits(alice, {sponsoredDataRateLimit: {blocks: 30}}); - - const token = await collection.mintToken(alice, {Substrate: bob.address}); - - const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); - const bobBalanceBefore = await helper.balance.getSubstrate(bob.address); - - await token.setProperties(bob, [{key: 'k', value: 'val'}]); - - const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); - const bobBalanceAfter = await helper.balance.getSubstrate(bob.address); - - expect(bobBalanceAfter).to.be.equal(bobBalanceBefore); - expect(aliceBalanceBefore > aliceBalanceAfter).to.be.true; - }); -}); - -describe('Negative Integration Test: Token Properties', () => { - let alice: IKeyringPair; // collection owner - let bob: IKeyringPair; // collection admin - let charlie: IKeyringPair; // token owner - - let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[]; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - let dave: IKeyringPair; - [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor); - - // todo:playgrounds probably separate these tests later - constitution = [ - {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie}, - {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie}, - {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice}, - {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice}, - {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave}, - {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave}, - ]; - }); - }); - - [ - {mode: 'nft' as const, requiredPallets: [Pallets.NFT]}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itSub.ifWithPallets(`Forbids adding/deleting properties of a token if token doesn't exist (${testCase.mode.toLocaleUpperCase})`, testCase.requiredPallets, async({helper}) => { - const collection = await helper[testCase.mode].mintCollection(alice, { - tokenPropertyPermissions: constitution.slice(0, 1).map(({permission}) => ({key: '1', permission})), - }); - const nonExistentToken = collection.getTokenObject(1); - - await expect( - nonExistentToken.setProperties(alice, [{key: '1', value: 'Serotonin increase'}]), - 'on expecting failure whilst adding a property by alice', - ).to.be.rejectedWith(/common\.TokenNotFound/); - - await expect( - nonExistentToken.deleteProperties(alice, ['1']), - 'on expecting failure whilst deleting a property by alice', - ).to.be.rejectedWith(/common\.TokenNotFound/); - })); - - async function mintCollectionWithAllPermissionsAndToken(helper: UniqueHelper, mode: 'NFT' | 'RFT'): Promise<[UniqueNFToken | UniqueRFToken, bigint]> { - const collection = await (mode == 'NFT' ? helper.nft : helper.rft).mintCollection(alice, { - tokenPropertyPermissions: constitution.map(({permission}, i) => ({key: `${i+1}`, permission})), - }); - return mode == 'NFT' ? [await collection.mintToken(alice), 1n] : [await collection.mintToken(alice, 100n as any), 100n]; - } - - async function getConsumedSpace(api: any, collectionId: number, tokenId: number, mode: 'NFT' | 'RFT'): Promise { - return (await (mode == 'NFT' ? api.query.nonfungible : api.query.refungible).tokenProperties(collectionId, tokenId)).toJSON().consumedSpace; - } - - async function prepare(token: UniqueNFToken | UniqueRFToken, pieces: bigint): Promise { - await token.collection.addAdmin(alice, {Substrate: bob.address}); - await token.transfer(alice, {Substrate: charlie.address}, pieces); - - let i = 0; - for(const passage of constitution) { - i++; - const signer = passage.signers[0]; - await expect( - token.setProperties(signer, [{key: `${i}`, value: 'Serotonin increase'}]), - `on adding property ${i} by ${signer.address}`, - ).to.be.fulfilled; - } - - const originalSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); - return originalSpace; - } - - async function testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { - const originalSpace = await prepare(token, pieces); - - let i = 0; - for(const forbiddance of constitution) { - i++; - if(!forbiddance.permission.mutable) continue; - - await expect( - token.setProperties(forbiddance.sinner, [{key: `${i}`, value: 'Serotonin down'}]), - `on failing to change property ${i} by the malefactor`, - ).to.be.rejectedWith(/common\.NoPermission/); - - await expect( - token.deleteProperties(forbiddance.sinner, [`${i}`]), - `on failing to delete property ${i} by the malefactor`, - ).to.be.rejectedWith(/common\.NoPermission/); - } - - const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); - expect(consumedSpace).to.be.equal(originalSpace); - } - - itSub('Forbids changing/deleting properties of a token if the user is outside of permissions (NFT)', async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); - await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, amount); - }); - - itSub.ifWithPallets('Forbids changing/deleting properties of a token if the user is outside of permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); - await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, amount); - }); - - async function testForbidsChangingDeletingPropertiesIfPropertyImmutable(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { - const originalSpace = await prepare(token, pieces); - - let i = 0; - for(const permission of constitution) { - i++; - if(permission.permission.mutable) continue; - - await expect( - token.setProperties(permission.signers[0], [{key: `${i}`, value: 'Serotonin down'}]), - `on failing to change property ${i} by signer #0`, - ).to.be.rejectedWith(/common\.NoPermission/); - - await expect( - token.deleteProperties(permission.signers[0], [i.toString()]), - `on failing to delete property ${i} by signer #0`, - ).to.be.rejectedWith(/common\.NoPermission/); - } - - const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); - expect(consumedSpace).to.be.equal(originalSpace); - } - - itSub('Forbids changing/deleting properties of a token if the property is permanent (immutable) (NFT)', async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); - await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, amount); - }); - - itSub.ifWithPallets('Forbids changing/deleting properties of a token if the property is permanent (immutable) (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); - await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, amount); - }); - - async function testForbidsAddingPropertiesIfPropertyNotDeclared(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { - const originalSpace = await prepare(token, pieces); - - await expect( - token.setProperties(alice, [{key: 'non-existent', value: 'I exist!'}]), - 'on failing to add a previously non-existent property', - ).to.be.rejectedWith(/common\.NoPermission/); - - await expect( - token.collection.setTokenPropertyPermissions(alice, [{key: 'now-existent', permission: {}}]), - 'on setting a new non-permitted property', - ).to.be.fulfilled; - - await expect( - token.setProperties(alice, [{key: 'now-existent', value: 'I exist!'}]), - 'on failing to add a property forbidden by the \'None\' permission', - ).to.be.rejectedWith(/common\.NoPermission/); - - expect(await token.getProperties(['non-existent', 'now-existent'])).to.be.empty; - - const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); - expect(consumedSpace).to.be.equal(originalSpace); - } - - itSub('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (NFT)', async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); - await testForbidsAddingPropertiesIfPropertyNotDeclared(token, amount); - }); - - itSub.ifWithPallets('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); - await testForbidsAddingPropertiesIfPropertyNotDeclared(token, amount); - }); - - async function testForbidsAddingTooLargeProperties(token: UniqueNFToken | UniqueRFToken, pieces: bigint) { - const originalSpace = await prepare(token, pieces); - - await expect( - token.collection.setTokenPropertyPermissions(alice, [ - {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, - {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}}, - ]), - 'on setting new permissions for properties', - ).to.be.fulfilled; - - // Mute the general tx parsing error - { - console.error = () => {}; - await expect(token.setProperties(alice, [{key: 'a_holy_book', value: 'word '.repeat(6554)}])) - .to.be.rejected; - } - - await expect(token.setProperties(alice, [ - {key: 'a_holy_book', value: 'word '.repeat(3277)}, - {key: 'young_years', value: 'neverending'.repeat(1490)}, - ])).to.be.rejectedWith(/common\.NoSpaceForProperty/); - - expect(await token.getProperties(['a_holy_book', 'young_years'])).to.be.empty; - const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); - expect(consumedSpace).to.be.equal(originalSpace); - } - - itSub('Forbids adding too large properties to a token (NFT)', async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT'); - await testForbidsAddingTooLargeProperties(token, amount); - }); - - itSub.ifWithPallets('Forbids adding too large properties to a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => { - const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT'); - await testForbidsAddingTooLargeProperties(token, amount); - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itSub.ifWithPallets(`Forbids adding too many propeties to a token (${testCase.mode})`, testCase.requiredPallets, async({helper}) => { - const collection = await helper[testCase.mode].mintCollection(alice); - const maxPropertiesPerItem = 64; - - for(let i = 0; i < maxPropertiesPerItem; i++) { - await collection.setTokenPropertyPermissions(alice, [{ - key: `${i+1}`, - permission: {mutable: true, tokenOwner: true, collectionAdmin: true}, - }]); - } - - await expect(collection.setTokenPropertyPermissions(alice, [{ - key: `${maxPropertiesPerItem}-th`, - permission: {mutable: true, tokenOwner: true, collectionAdmin: true}, - }])).to.be.rejectedWith(/common\.PropertyLimitReached/); - })); - - [ - {mode: 'nft' as const, pieces: undefined, requiredPallets: []}, - {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => - itSub.ifWithPallets(`Forbids force_repair_item from non-sudo (${testCase.mode})`, testCase.requiredPallets, async({helper}) => { - const propKey = 'tok-prop'; - - const collection = await helper[testCase.mode].mintCollection(alice, { - tokenPropertyPermissions: [ - { - key: propKey, - permission: {mutable: true, tokenOwner: true}, - }, - ], - }); - const token = await ( - testCase.pieces - ? collection.mintToken(alice, testCase.pieces as any) - : collection.mintToken(alice) - ); - - const propDataSize = 4096; - const propData = 'a'.repeat(propDataSize); - await token.setProperties(alice, [{key: propKey, value: propData}]); - - await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true)) - .to.be.rejectedWith(/BadOrigin/); - })); -}); - -describe('ReFungible token properties permissions tests', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - async function prepare(helper: UniqueHelper): Promise { - const collection = await helper.rft.mintCollection(alice); - const token = await collection.mintToken(alice, 100n); - - await collection.addAdmin(alice, {Substrate: bob.address}); - await collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable: true, tokenOwner: true}}]); - - return token; - } - - itSub('Forbids adding token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) => { - const token = await prepare(helper); - - await token.transfer(alice, {Substrate: charlie.address}, 33n); - - await expect(token.setProperties(alice, [ - {key: 'fractals', value: 'multiverse'}, - ])).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Forbids mutating token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) => { - const token = await prepare(helper); - - await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, tokenOwner: true}}])) - .to.be.fulfilled; - - await expect(token.setProperties(alice, [ - {key: 'fractals', value: 'multiverse'}, - ])).to.be.fulfilled; - - await token.transfer(alice, {Substrate: charlie.address}, 33n); - - await expect(token.setProperties(alice, [ - {key: 'fractals', value: 'want to rule the world'}, - ])).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Forbids deleting token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) => { - const token = await prepare(helper); - - await expect(token.setProperties(alice, [ - {key: 'fractals', value: 'one headline - why believe it'}, - ])).to.be.fulfilled; - - await token.transfer(alice, {Substrate: charlie.address}, 33n); - - await expect(token.deleteProperties(alice, ['fractals'])). - to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Allows token property mutation with collectionOwner==true when admin doesn\'t have all pieces', async ({helper}) => { - const token = await prepare(helper); - - await token.transfer(alice, {Substrate: charlie.address}, 33n); - - await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, collectionAdmin: true}}])) - .to.be.fulfilled; - - await expect(token.setProperties(alice, [ - {key: 'fractals', value: 'multiverse'}, - ])).to.be.fulfilled; - }); -}); --- a/js-packages/tests/src/nesting/unnest.test.ts +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, Pallets, usingPlaygrounds} from '../util/index.js'; -import {CrossAccountId, UniqueFTCollection, UniqueNFToken, UniqueRFToken} from '@unique/playgrounds/src/unique.js'; - -describe('Integration Test: Unnesting', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 50n, 50n], donor); - }); - }); - - itSub('NFT: allows the owner to successfully unnest a token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const targetToken = await collection.mintToken(alice); - - // Create a nested token - const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount()); - - // Unnest - await expect(nestedToken.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}), 'while unnesting').to.be.fulfilled; - expect(await nestedToken.getOwner()).to.be.deep.equal({Substrate: alice.address}); - - // Nest and burn - await nestedToken.nest(alice, targetToken); - await expect(nestedToken.burnFrom(alice, targetToken.nestingAccount()), 'while burning').to.be.fulfilled; - await expect(nestedToken.getOwner()).to.be.rejected; - }); - - itSub('NativeFungible: allows the owner to successfully unnest a token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const targetToken = await collection.mintToken(alice); - - const collectionFT = helper.ft.getCollectionObject(0); - - // Nest - await collectionFT.transfer(alice, targetToken.nestingAccount(), 10n); - // Unnest - await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled; - - expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(1n); - }); - - itSub('Fungible: allows the owner to successfully unnest a token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const targetToken = await collection.mintToken(alice); - - const collectionFT = await helper.ft.mintCollection(alice); - - // Nest and unnest - await collectionFT.mint(alice, 10n, targetToken.nestingAccount()); - await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled; - expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(9n); - expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(1n); - - // Nest and burn - await collectionFT.transfer(alice, targetToken.nestingAccount(), 5n); - await expect(collectionFT.burnTokensFrom(alice, targetToken.nestingAccount(), 6n), 'while burning').to.be.fulfilled; - expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(4n); - expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(0n); - expect(await targetToken.getChildren()).to.be.length(0); - }); - - itSub.ifWithPallets('ReFungible: allows the owner to successfully unnest a token', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const targetToken = await collection.mintToken(alice); - - const collectionRFT = await helper.rft.mintCollection(alice); - - // Nest and unnest - const token = await collectionRFT.mintToken(alice, 10n, targetToken.nestingAccount()); - await expect(token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(9n); - expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(1n); - - // Nest and burn - await token.transfer(alice, targetToken.nestingAccount(), 5n); - await expect(token.burnFrom(alice, targetToken.nestingAccount(), 6n), 'while burning').to.be.fulfilled; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(4n); - expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n); - expect(await targetToken.getChildren()).to.be.length(0); - }); - - async function checkNestedAmountState({ - expectedBalance, - childrenShouldPresent, - nested, - targetNft, - }: { - expectedBalance: bigint, - childrenShouldPresent: boolean, - nested: UniqueFTCollection | UniqueRFToken, - targetNft: UniqueNFToken, - }) { - const balance = await nested.getBalance(targetNft.nestingAccount()); - expect(balance).to.be.equal(expectedBalance); - - const children = await targetNft.getChildren(); - - if(childrenShouldPresent) { - expect(children[0]).to.be.deep.equal({ - collectionId: nested.collectionId, - tokenId: (nested instanceof UniqueFTCollection) ? 0 : nested.tokenId, - }); - } else { - expect(children.length).to.be.equal(0); - } - } - - function ownerOrAdminUnnestCases(modes: ('ft' | 'nft' | 'rft')[]): { - mode: 'ft' | 'nft' | 'rft', - sender: string, - op: 'transfer' | 'burn', - requiredPallets: Pallets[], - }[] { - const senders = ['owner', 'admin']; - const ops = ['transfer', 'burn']; - - const cases = []; - for(const mode of modes) { - const requiredPallets = (mode === 'rft') - ? [Pallets.ReFungible] - : []; - - for(const sender of senders) { - for(const op of ops) { - cases.push({ - mode: mode as 'ft' | 'nft' | 'rft', - sender, - op: op as 'transfer' | 'burn', - requiredPallets, - }); - } - } - } - - return cases; - } - - ownerOrAdminUnnestCases(['ft', 'rft']).map(testCase => - itSub.ifWithPallets(`[${testCase.mode}]: allows a collection ${testCase.sender} to ${testCase.op} nested token`, testCase.requiredPallets, async({helper}) => { - const owner = alice; - const admin = bob; - - const unnester = (testCase.sender === 'owner') - ? owner - : admin; - - const collectionNFT = await helper.nft.mintCollection(owner); - await collectionNFT.setPermissions(owner, {nesting: {tokenOwner: true}}); - - const collectionNested = await helper[testCase.mode as 'ft' | 'rft'].mintCollection(owner, { - limits: { - ownerCanTransfer: true, - }, - }); - await collectionNested.addAdmin(owner, {Substrate: admin.address}); - - const targetNft = await collectionNFT.mintToken(owner, {Substrate: charlie.address}); - - let nested: UniqueFTCollection | UniqueRFToken; - const totalAmount = 5n; - const firstUnnestAmount = 2n; - const restUnnestAmount = totalAmount - firstUnnestAmount; - - if(collectionNested instanceof UniqueFTCollection) { - await collectionNested.mint(owner, totalAmount, {Substrate: charlie.address}); - nested = collectionNested; - } else { - nested = await collectionNested.mintToken(owner, totalAmount, {Substrate: charlie.address}); - } - - // transfer/burn `amount` of nested assets by `unnester`. - const doOperationAndCheck = async ({ - amount, - shouldBeNestedAfterOp, - }: { - amount: bigint, - shouldBeNestedAfterOp: boolean, - }) => { - const nestedBalanceBeforeOp = await nested.getBalance(targetNft.nestingAccount()); - - if(testCase.op === 'transfer') { - const bobBalanceBeforeOp = await nested.getBalance({Substrate: bob.address}); - - await nested.transferFrom(unnester, targetNft.nestingAccount(), {Substrate: bob.address}, amount); - expect(await nested.getBalance({Substrate: bob.address})).to.be.equal(bobBalanceBeforeOp + amount); - } else { - if(nested instanceof UniqueFTCollection) { - await nested.burnTokensFrom(unnester, targetNft.nestingAccount(), amount); - } else { - await nested.burnFrom(unnester, targetNft.nestingAccount(), amount); - } - } - - await checkNestedAmountState({ - expectedBalance: nestedBalanceBeforeOp - amount, - childrenShouldPresent: shouldBeNestedAfterOp, - nested, - targetNft, - }); - }; - - // Initial setup: nest (fungibles/rft parts). - // Check NFT's balance of nested assets and NFT's children. - await nested.transfer(charlie, targetNft.nestingAccount(), totalAmount); - await checkNestedAmountState({ - expectedBalance: totalAmount, - childrenShouldPresent: true, - nested, - targetNft, - }); - - // Transfer/burn only a part of nested assets. - // Check that NFT's balance of the nested assets correctly decreased and NFT's children are not changed. - await doOperationAndCheck({ - amount: firstUnnestAmount, - shouldBeNestedAfterOp: true, - }); - - // Transfer/burn all remaining nested assets. - // Check that NFT's balance of the nested assets is 0 and NFT has no more children. - await doOperationAndCheck({ - amount: restUnnestAmount, - shouldBeNestedAfterOp: false, - }); - })); - - ownerOrAdminUnnestCases(['nft']).map(testCase => - itSub(`[nft]: allows a collection ${testCase.sender} to ${testCase.op} nested token`, async ({helper}) => { - const owner = alice; - const admin = bob; - - const unnester = (testCase.sender === 'owner') - ? owner - : admin; - - const collectionNFT = await helper.nft.mintCollection(owner); - await collectionNFT.setPermissions(owner, {nesting: {tokenOwner: true}}); - - const collectionNested = await helper.nft.mintCollection(owner, { - limits: { - ownerCanTransfer: true, - }, - }); - await collectionNested.addAdmin(owner, {Substrate: admin.address}); - - const targetNft = await collectionNFT.mintToken(owner, {Substrate: charlie.address}); - const nested = await collectionNested.mintToken(owner, {Substrate: charlie.address}); - - await nested.transfer(charlie, targetNft.nestingAccount()); - expect(await targetNft.getChildren()).to.be.deep.equal([{ - collectionId: nested.collectionId, - tokenId: nested.tokenId, - }]); - - if(testCase.op === 'transfer') { - await nested.transferFrom(unnester, targetNft.nestingAccount(), {Substrate: bob.address}); - } else { - await nested.burnFrom(unnester, targetNft.nestingAccount()); - } - - expect((await targetNft.getChildren()).length).to.be.equal(0); - })); -}); - -describe('Negative Test: Unnesting', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); - }); - }); - - itSub('Disallows a non-owner to unnest/burn a token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const targetToken = await collection.mintToken(alice); - - // Create a nested token - const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount()); - - // Try to unnest - await expect(nestedToken.unnest(bob, targetToken, {Substrate: alice.address})).to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); - - // Try to burn - await expect(nestedToken.burnFrom(bob, targetToken.nestingAccount())).to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); - }); - - // todo another test for creating excessive depth matryoshka with Ethereum? - - // Recursive nesting - itSub('Prevents Ouroboros creation', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const targetToken = await collection.mintToken(alice); - - // Fail to create a nested token ouroboros - const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount()); - await expect(targetToken.nest(alice, nestedToken)).to.be.rejectedWith(/^structure\.OuroborosDetected$/); - }); -}); --- a/js-packages/tests/src/nextSponsoring.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, Pallets, usingPlaygrounds} from './util/index.js'; - -const SPONSORING_TIMEOUT = 5; - -describe('Integration Test getNextSponsored(collection_id, owner, item_id):', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); - }); - }); - - itSub('NFT', async ({helper}) => { - // Non-existing collection - expect(await helper.collection.getTokenNextSponsored(0, 0, {Substrate: alice.address})).to.be.null; - - const collection = await helper.nft.mintCollection(alice, {}); - const token = await collection.mintToken(alice); - - // Check with Disabled sponsoring state - expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null; - - // Check with Unconfirmed sponsoring state - await collection.setSponsor(alice, bob.address); - expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null; - - // Check with Confirmed sponsoring state - await collection.confirmSponsorship(bob); - expect(await token.getNextSponsored({Substrate: alice.address})).to.be.equal(0); - - // Check after transfer - await token.transfer(alice, {Substrate: bob.address}); - expect(await token.getNextSponsored({Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT); - - // Non-existing token - expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null; - }); - - itSub('Fungible', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {}); - await collection.mint(alice, 10n); - - // Check with Disabled sponsoring state - expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null; - - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - - // Check with Confirmed sponsoring state - expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.equal(0); - - // Check after transfer - await collection.transfer(alice, {Substrate: bob.address}); - expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT); - }); - - itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {}); - const token = await collection.mintToken(alice, 10n); - - // Check with Disabled sponsoring state - expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null; - - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - - // Check with Confirmed sponsoring state - expect(await token.getNextSponsored({Substrate: alice.address})).to.be.equal(0); - - // Check after transfer - await token.transfer(alice, {Substrate: bob.address}); - expect(await token.getNextSponsored({Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT); - - // Non-existing token - expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null; - }); -}); --- a/js-packages/tests/src/pallet-presence.test.ts +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {itSub, usingPlaygrounds, expect} from './util/index.js'; - -// Pallets that must always be present -const requiredPallets = [ - 'balances', - 'balancesadapter', - 'common', - 'timestamp', - 'transactionpayment', - 'treasury', - 'statetriemigration', - 'structure', - 'system', - 'utility', - 'vesting', - 'parachainsystem', - 'parachaininfo', - 'evm', - 'evmcodersubstrate', - 'evmcontracthelpers', - 'evmmigration', - 'evmtransactionpayment', - 'ethereum', - 'fungible', - 'xcmpqueue', - 'polkadotxcm', - 'cumulusxcm', - 'dmpqueue', - 'inflation', - 'unique', - 'nonfungible', - 'charging', - 'configuration', - 'tokens', - 'xtokens', - 'maintenance', -]; - -// Pallets that depend on consensus and governance configuration -const consensusPallets = [ - 'sudo', - 'aura', - 'auraext', -]; - -describe('Pallet presence', () => { - before(async () => { - await usingPlaygrounds(async helper => { - const runtimeVersion = await helper.callRpc('api.rpc.state.getRuntimeVersion', []); - const chain = runtimeVersion.specName; - - const refungible = 'refungible'; - const foreignAssets = 'foreignassets'; - const appPromotion = 'apppromotion'; - const collatorSelection = ['authorship', 'session', 'collatorselection']; - const preimage = ['preimage']; - const governance = [ - 'council', - 'councilmembership', - 'democracy', - 'fellowshipcollective', - 'fellowshipreferenda', - 'origins', - 'scheduler', - 'technicalcommittee', - 'technicalcommitteemembership', - 'identity', - ]; - const testUtils = 'testutils'; - - if(chain.eq('opal')) { - requiredPallets.push( - refungible, - foreignAssets, - appPromotion, - testUtils, - ...collatorSelection, - ...preimage, - ...governance, - ); - } else if(chain.eq('quartz') || chain.eq('sapphire')) { - requiredPallets.push( - refungible, - appPromotion, - foreignAssets, - ...collatorSelection, - ...preimage, - ...governance, - ); - } else if(chain.eq('unique')) { - // Insert Unique additional pallets here - requiredPallets.push( - refungible, - foreignAssets, - appPromotion, - ...preimage, - ...governance, - ); - } - }); - }); - - itSub('Required pallets are present', ({helper}) => { - expect(helper.fetchAllPalletNames()).to.contain.members([...requiredPallets].sort()); - }); - - itSub('Governance and consensus pallets are present', ({helper}) => { - expect(helper.fetchAllPalletNames()).to.contain.members([...consensusPallets].sort()); - }); - - itSub('No extra pallets are included', ({helper}) => { - expect(helper.fetchAllPalletNames().sort()).to.be.deep.equal([...requiredPallets, ...consensusPallets].sort()); - }); -}); --- a/js-packages/tests/src/performance.seq.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2019-2023 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {ApiPromise} from '@polkadot/api'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, usingPlaygrounds} from './util/index.js'; -import type {ICrossAccountId, IProperty} from '@unique/playgrounds/src/types.js'; -import {UniqueHelper} from '@unique/playgrounds/src/unique.js'; - -describe('Performace tests', () => { - let alice: IKeyringPair; - const MAX_TOKENS_TO_MINT = 120; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([100_000n], donor); - }); - }); - - itSub('NFT tokens minting', async ({helper}) => { - const propertyKey = 'prop-a'; - const collection = await helper.nft.mintCollection(alice, { - name: 'test properties', - description: 'test properties collection', - tokenPrefix: 'TPC', - tokenPropertyPermissions: [ - {key: propertyKey, permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}, - ], - }); - - const step = 1_000; - const sizeOfKey = sizeOfEncodedStr(propertyKey); - let currentSize = step; - let startCount = 0; - let minterFunc = tryMintUnsafeRPC; - try { - startCount = await tryMintUnsafeRPC(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address}); - } - catch (e) { - startCount = await tryMintExplicit(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address}); - minterFunc = tryMintExplicit; - } - - expect(startCount).to.be.equal(MAX_TOKENS_TO_MINT); - - while(currentSize <= 32_000) { - const property = {key: propertyKey, value: 'A'.repeat(currentSize - sizeOfKey - sizeOfInt(currentSize))}; - const tokens = await minterFunc(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address}, property); - expect(tokens).to.be.equal(MAX_TOKENS_TO_MINT); - - currentSize += step; - await helper.wait.newBlocks(2); - } - }); -}); - - -const dryRun = async (api: ApiPromise, signer: IKeyringPair, tx: any) => { - const signed = await tx.signAsync(signer); - const dryRun = await api.rpc.system.dryRun(signed.toHex()); - return dryRun.isOk && dryRun.asOk.isOk; -}; - -const getTokens = (tokensCount: number, owner: ICrossAccountId, property?: IProperty) => (new Array(tokensCount)).fill(0).map(() => { - const token = {owner} as {owner: ICrossAccountId, properties?: IProperty[]}; - if(property) token.properties = [property]; - return token; -}); - -const tryMintUnsafeRPC = async (helper: UniqueHelper, signer: IKeyringPair, tokensCount: number, collectionId: number, owner: ICrossAccountId, property?: IProperty): Promise => { - if(tokensCount < 10) console.log('try mint', tokensCount, 'tokens'); - const tokens = getTokens(tokensCount, owner, property); - const tx = helper.constructApiCall('api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]); - if(!(await dryRun(helper.getApi(), signer, tx))) { - if(tokensCount < 2) return 0; - return await tryMintUnsafeRPC(helper, signer, tokensCount - 1, collectionId, owner, property); - } - await helper.executeExtrinsic(signer, 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]); - return tokensCount; -}; - -const tryMintExplicit = async (helper: UniqueHelper, signer: IKeyringPair, tokensCount: number, collectionId: number, owner: ICrossAccountId, property?: IProperty): Promise => { - const tokens = getTokens(tokensCount, owner, property); - try { - await helper.executeExtrinsic(signer, 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]); - } - catch (e) { - if(tokensCount < 2) return 0; - return await tryMintExplicit(helper, signer, tokensCount - 1, collectionId, owner, property); - } - return tokensCount; -}; - -function sizeOfInt(i: number) { - if(i < 0 || i > 0xffffffff) throw new Error('out of range'); - if(i < 0b11_1111) { - return 1; - } else if(i < 0b11_1111_1111_1111) { - return 2; - } else if(i < 0b11_1111_1111_1111_1111_1111_1111_1111) { - return 4; - } else { - return 5; - } -} - -const UTF8_ENCODER = new TextEncoder(); -function sizeOfEncodedStr(v: string) { - const encoded = UTF8_ENCODER.encode(v); - return sizeOfInt(encoded.length) + encoded.length; -} --- a/js-packages/tests/src/refungible.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from './util/index.js'; - -const MAX_REFUNGIBLE_PIECES = 1_000_000_000_000_000_000_000n; - -describe('integration test: Refungible functionality:', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); - }); - }); - - itSub('Create refungible collection and token', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - const itemCountBefore = await collection.getLastTokenId(); - const token = await collection.mintToken(alice, 100n); - - const itemCountAfter = await collection.getLastTokenId(); - - // What to expect - expect(token?.tokenId).to.be.gte(itemCountBefore); - expect(itemCountAfter).to.be.equal(itemCountBefore + 1); - expect(itemCountAfter.toString()).to.be.equal(token?.tokenId.toString()); - }); - - itSub('Checking RPC methods when interacting with maximum allowed values (MAX_REFUNGIBLE_PIECES)', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - const token = await collection.mintToken(alice, MAX_REFUNGIBLE_PIECES); - - expect(await collection.getTokenBalance(token.tokenId, {Substrate: alice.address})).to.be.equal(MAX_REFUNGIBLE_PIECES); - - await collection.transferToken(alice, token.tokenId, {Substrate: bob.address}, MAX_REFUNGIBLE_PIECES); - expect(await collection.getTokenBalance(token.tokenId, {Substrate: bob.address})).to.be.equal(MAX_REFUNGIBLE_PIECES); - expect(await token.getTotalPieces()).to.be.equal(MAX_REFUNGIBLE_PIECES); - - await expect(collection.mintToken(alice, MAX_REFUNGIBLE_PIECES + 1n)) - .to.eventually.be.rejectedWith(/refungible\.WrongRefungiblePieces/); - }); - - itSub('RPC method tokenOwners for refungible collection and token', async ({helper}) => { - const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; - const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => ({Substrate: keyring.address})); - - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - const token = await collection.mintToken(alice, 10_000n); - - await token.transfer(alice, {Substrate: bob.address}, 1000n); - await token.transfer(alice, ethAcc, 900n); - - for(let i = 0; i < 7; i++) { - await token.transfer(alice, facelessCrowd[i], 50n * BigInt(i + 1)); - } - - const owners = await token.getTop10Owners(); - - // What to expect - expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]); - expect(owners.length).to.be.equal(10); - - const [eleven] = await helper.arrange.createAccounts([0n], donor); - expect(await token.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true; - expect((await token.getTop10Owners()).length).to.be.equal(10); - }); - - itSub('Create multiple tokens', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - // TODO: fix mintMultipleTokens - // await collection.mintMultipleTokens(alice, [ - // {owner: {Substrate: alice.address}, pieces: 1n}, - // {owner: {Substrate: alice.address}, pieces: 2n}, - // {owner: {Substrate: alice.address}, pieces: 100n}, - // ]); - await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, [ - {pieces: 1n}, - {pieces: 2n}, - {pieces: 100n}, - ]); - const lastTokenId = await collection.getLastTokenId(); - expect(lastTokenId).to.be.equal(3); - expect(await collection.getTokenBalance(lastTokenId, {Substrate: alice.address})).to.be.equal(100n); - }); - - itSub('Set allowance for token', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, 100n); - - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); - - expect(await token.approve(alice, {Substrate: bob.address}, 60n)).to.be.true; - expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n); - - expect(await token.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(80n); - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(20n); - expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n); - }); - - itSub('Create new collection with properties', async ({helper}) => { - const properties = [{key: 'key1', value: 'val1'}]; - const tokenPropertyPermissions = [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}]; - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test', properties, tokenPropertyPermissions}); - const info = await collection.getData(); - expect(info?.raw.properties).to.be.deep.equal(properties); - expect(info?.raw.tokenPropertyPermissions).to.be.deep.equal(tokenPropertyPermissions); - }); -}); --- a/js-packages/tests/src/removeCollectionAdmin.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from './util/index.js'; -import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js'; - -describe('Integration Test removeCollectionAdmin(collection_id, account_id):', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); - }); - }); - - itSub('Remove collection admin', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-1', tokenPrefix: 'RCA'}); - const collectionInfo = await collection.getData(); - expect(collectionInfo?.raw.owner.toString()).to.be.deep.eq(alice.address); - // first - add collection admin Bob - await collection.addAdmin(alice, {Substrate: bob.address}); - - const adminListAfterAddAdmin = await collection.getAdmins(); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); - - // then remove bob from admins of collection - await collection.removeAdmin(alice, {Substrate: bob.address}); - - const adminListAfterRemoveAdmin = await collection.getAdmins(); - expect(adminListAfterRemoveAdmin).not.to.be.deep.contains({Substrate: bob.address}); - }); - - itSub('Remove admin from collection that has no admins', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-2', tokenPrefix: 'RCA'}); - - const adminListBeforeAddAdmin = await collection.getAdmins(); - expect(adminListBeforeAddAdmin).to.have.lengthOf(0); - - await expect(collection.removeAdmin(alice, {Substrate: alice.address})).to.be.rejectedWith('common.UserIsNotCollectionAdmin'); - }); -}); - -describe('Negative Integration Test removeCollectionAdmin(collection_id, account_id):', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); - }); - }); - - itSub('Can\'t remove collection admin from not existing collection', async ({helper}) => { - const collectionId = NON_EXISTENT_COLLECTION_ID; - - await expect(helper.collection.removeAdmin(alice, collectionId, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('Can\'t remove collection admin from deleted collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-2', tokenPrefix: 'RCA'}); - - expect(await collection.burn(alice)).to.be.true; - - await expect(helper.collection.removeAdmin(alice, collection.collectionId, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('Regular user can\'t remove collection admin', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-3', tokenPrefix: 'RCA'}); - - await collection.addAdmin(alice, {Substrate: bob.address}); - - await expect(collection.removeAdmin(charlie, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('Admin can\'t remove collection admin.', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-4', tokenPrefix: 'RCA'}); - - await collection.addAdmin(alice, {Substrate: bob.address}); - await collection.addAdmin(alice, {Substrate: charlie.address}); - - const adminListAfterAddAdmin = await collection.getAdmins(); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: charlie.address}); - - await expect(collection.removeAdmin(charlie, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.NoPermission/); - - const adminListAfterRemoveAdmin = await collection.getAdmins(); - expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: bob.address}); - expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: charlie.address}); - }); -}); --- a/js-packages/tests/src/removeCollectionSponsor.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from './util/index.js'; -import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js'; - -describe('integration test: ext. removeCollectionSponsor():', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); - }); - }); - - itSub('Removing NFT collection sponsor stops sponsorship', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-1', tokenPrefix: 'RCS'}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - await collection.removeSponsor(alice); - - // Find unused address - const [zeroBalance] = await helper.arrange.createAccounts([0n], donor); - - // Mint token for unused address - const token = await collection.mintToken(alice, {Substrate: zeroBalance.address}); - - // Transfer this tokens from unused address to Alice - should fail - const sponsorBalanceBefore = await helper.balance.getSubstrate(bob.address); - await expect(token.transfer(zeroBalance, {Substrate: alice.address})) - .to.be.rejectedWith('Inability to pay some fees'); - const sponsorBalanceAfter = await helper.balance.getSubstrate(bob.address); - - expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore); - }); - - itSub('Remove a sponsor after it was already removed', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-2', tokenPrefix: 'RCS'}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - await expect(collection.removeSponsor(alice)).to.not.be.rejected; - await expect(collection.removeSponsor(alice)).to.not.be.rejected; - }); - - itSub('Remove sponsor in a collection that never had the sponsor set', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-3', tokenPrefix: 'RCS'}); - await expect(collection.removeSponsor(alice)).to.not.be.rejected; - }); - - itSub('Remove sponsor for a collection that had the sponsor set, but not confirmed', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-4', tokenPrefix: 'RCS'}); - await collection.setSponsor(alice, bob.address); - await expect(collection.removeSponsor(alice)).to.not.be.rejected; - }); - - itSub('Remove a sponsor from a collection with collection admin permissions', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-1', tokenPrefix: 'RCS'}); - await collection.setSponsor(alice, bob.address); - await collection.addAdmin(alice, {Substrate: charlie.address}); - await expect(collection.removeSponsor(charlie)).not.to.be.rejected; - }); -}); - -describe('(!negative test!) integration test: ext. removeCollectionSponsor():', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); - }); - }); - - itSub('(!negative test!) Remove sponsor for a collection that never existed', async ({helper}) => { - const collectionId = NON_EXISTENT_COLLECTION_ID; - await expect(helper.collection.removeSponsor(alice, collectionId)).to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('(!negative test!) Remove sponsor for a collection by regular user', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-2', tokenPrefix: 'RCS'}); - await collection.setSponsor(alice, bob.address); - await expect(collection.removeSponsor(charlie)).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('(!negative test!) Remove sponsor in a destroyed collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-3', tokenPrefix: 'RCS'}); - await collection.setSponsor(alice, bob.address); - await collection.burn(alice); - await expect(collection.removeSponsor(alice)).to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('Set - remove - confirm: fails', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-4', tokenPrefix: 'RCS'}); - await collection.setSponsor(alice, bob.address); - await collection.removeSponsor(alice); - await expect(collection.confirmSponsorship(bob)).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); - }); - - itSub('Set - confirm - remove - confirm: Sponsor cannot come back', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-5', tokenPrefix: 'RCS'}); - await collection.setSponsor(alice, bob.address); - await collection.confirmSponsorship(bob); - await collection.removeSponsor(alice); - await expect(collection.confirmSponsorship(bob)).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/); - }); -}); --- a/js-packages/tests/src/rpc.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, itSub, expect} from './util/index.js'; -import {ICrossAccountId} from '@unique/playgrounds/src/types.js'; - -describe('integration test: RPC methods', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); - }); - }); - - itSub('returns None for fungible collection', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'RPC-1', tokenPrefix: 'RPC'}); - const owner = (await helper.callRpc('api.rpc.unique.tokenOwner', [collection.collectionId, 0])).toJSON() as any; - expect(owner).to.be.null; - }); - - itSub('RPC method tokenOwners for fungible collection and token', async ({helper}) => { - // Set-up a few token owners of all stripes - const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; - const facelessCrowd = (await helper.arrange.createAccounts([0n, 0n, 0n, 0n, 0n, 0n, 0n], donor)) - .map(i => ({Substrate: i.address})); - - const collection = await helper.ft.mintCollection(alice, {name: 'RPC-2', tokenPrefix: 'RPC'}); - // mint some maximum (u128) amounts of tokens possible - await collection.mint(alice, (1n << 128n) - 1n); - - await collection.transfer(alice, {Substrate: bob.address}, 1000n); - await collection.transfer(alice, ethAcc, 900n); - - for(let i = 0; i < facelessCrowd.length; i++) { - await collection.transfer(alice, facelessCrowd[i], 1n); - } - // Set-up over - - const owners = await helper.callRpc('api.rpc.unique.tokenOwners', [collection.collectionId, 0]); - const ids = owners.toHuman() as ICrossAccountId[]; - - expect(ids).to.have.deep.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]); - expect(owners.length == 10).to.be.true; - - // Make sure only 10 results are returned with this RPC - const [eleven] = await helper.arrange.createAccounts([0n], donor); - expect(await collection.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true; - expect((await helper.callRpc('api.rpc.unique.tokenOwners', [collection.collectionId, 0])).length).to.be.equal(10); - }); -}); --- a/js-packages/tests/src/scheduler.seqtest.ts +++ /dev/null @@ -1,796 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {expect, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {DevUniqueHelper, Event} from '@unique/playgrounds/src/unique.dev.js'; - -describe('Scheduling token and balance transfers', () => { - let superuser: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.UniqueScheduler]); - - superuser = await privateKey('//Alice'); - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - - await helper.testUtils.enable(Pallets.TestUtils); - }); - }); - - beforeEach(async () => { - await usingPlaygrounds(async (helper) => { - await helper.wait.noScheduledTasks(); - }); - }); - - itSched('Can delay a transfer of an owned token', async (scheduleKind, {helper}) => { - const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); - const token = await collection.mintToken(alice); - const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; - const blocksBeforeExecution = 4; - 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}); - - await helper.wait.forParachainBlockNumber(executionBlock); - - expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); - }); - - itSched('Can transfer funds periodically', async (scheduleKind, {helper}) => { - const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; - const waitForBlocks = 1; - - const amount = 1n * helper.balance.getOneTokenNominal(); - const periodic = { - period: 2, - repetitions: 2, - }; - - const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address); - - await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) - .balance.transferToSubstrate(alice, bob.address, amount); - const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; - - await helper.wait.forParachainBlockNumber(executionBlock); - - const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address); - expect(bobsBalanceAfterFirst) - .to.be.equal( - bobsBalanceBefore + 1n * amount, - '#1 Balance of the recipient should be increased by 1 * amount', - ); - - await helper.wait.forParachainBlockNumber(executionBlock + periodic.period); - - const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address); - expect(bobsBalanceAfterSecond) - .to.be.equal( - bobsBalanceBefore + 2n * amount, - '#2 Balance of the recipient should be increased by 2 * amount', - ); - }); - - itSub('Can cancel a scheduled operation which has not yet taken effect', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); - const token = await collection.mintToken(alice); - - const scheduledId = helper.arrange.makeScheduledId(); - const waitForBlocks = 4; - - expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.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); - - await helper.wait.forParachainBlockNumber(executionBlock); - - expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address}); - }); - - itSub('Can cancel a periodic operation (transfer of funds)', async ({helper}) => { - const waitForBlocks = 1; - const periodic = { - period: 3, - repetitions: 2, - }; - - const scheduledId = helper.arrange.makeScheduledId(); - - const amount = 1n * helper.balance.getOneTokenNominal(); - - const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address); - - await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) - .balance.transferToSubstrate(alice, bob.address, amount); - const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; - - await helper.wait.forParachainBlockNumber(executionBlock); - - const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address); - - expect(bobsBalanceAfterFirst) - .to.be.equal( - bobsBalanceBefore + 1n * amount, - '#1 Balance of the recipient should be increased by 1 * amount', - ); - - await helper.scheduler.cancelScheduled(alice, scheduledId); - await helper.wait.forParachainBlockNumber(executionBlock + periodic.period); - - const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address); - expect(bobsBalanceAfterSecond) - .to.be.equal( - bobsBalanceAfterFirst, - '#2 Balance of the recipient should not be changed', - ); - }); - - itSched('scheduler will not insert more tasks than allowed', async (scheduleKind, {helper}) => { - const maxScheduledPerBlock = 50; - let fillScheduledIds = new Array(maxScheduledPerBlock); - let extraScheduledId = undefined; - - if(scheduleKind == 'named') { - const scheduledIds = helper.arrange.makeScheduledIds(maxScheduledPerBlock + 1); - fillScheduledIds = scheduledIds.slice(0, maxScheduledPerBlock); - extraScheduledId = scheduledIds[maxScheduledPerBlock]; - } - - // Since the dev node has Instant Seal, - // we need a larger gap between the current block and the target one. - // - // We will schedule `maxScheduledPerBlock` transaction into the target block, - // so we need at least `maxScheduledPerBlock`-wide gap. - // We add some additional blocks to this gap to mitigate possible PolkadotJS delays. - const waitForBlocks = await helper.arrange.isDevNode() ? maxScheduledPerBlock + 5 : 5; - - const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks; - - const amount = 1n * helper.balance.getOneTokenNominal(); - - const balanceBefore = await helper.balance.getSubstrate(bob.address); - - // Fill the target block - for(let i = 0; i < maxScheduledPerBlock; i++) { - await helper.scheduler.scheduleAt(executionBlock, {scheduledId: fillScheduledIds[i]}) - .balance.transferToSubstrate(superuser, bob.address, amount); - } - - // Try to schedule a task into a full block - await expect(helper.scheduler.scheduleAt(executionBlock, {scheduledId: extraScheduledId}) - .balance.transferToSubstrate(superuser, bob.address, amount)) - .to.be.rejectedWith(/scheduler\.AgendaIsExhausted/); - - await helper.wait.forParachainBlockNumber(executionBlock); - - const balanceAfter = await helper.balance.getSubstrate(bob.address); - - expect(balanceAfter > balanceBefore).to.be.true; - - const diff = balanceAfter - balanceBefore; - expect(diff).to.be.equal(amount * BigInt(maxScheduledPerBlock)); - }); - - itSched.ifWithPallets('Scheduled tasks are transactional', [Pallets.TestUtils], async (scheduleKind, {helper}) => { - const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; - const waitForBlocks = 4; - - const initTestVal = 42; - const changedTestVal = 111; - - await helper.testUtils.setTestValue(alice, initTestVal); - - await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId}) - .testUtils.setTestValueAndRollback(alice, changedTestVal); - const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; - - await helper.wait.forParachainBlockNumber(executionBlock); - - const testVal = await helper.testUtils.testValue(); - expect(testVal, 'The test value should NOT be commited') - .to.be.equal(initTestVal); - }); - - itSched.ifWithPallets('Scheduled tasks should take correct fees', [Pallets.TestUtils], async function(scheduleKind, {helper}) { - const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; - const waitForBlocks = 4; - const periodic = { - period: 2, - repetitions: 2, - }; - - const dummyTx = helper.constructApiCall('api.tx.testUtils.justTakeFee', []); - const scheduledLen = dummyTx.callIndex.length; - - const expectedScheduledFee = (await helper.getPaymentInfo(alice, dummyTx, scheduledLen)) - .partialFee.toBigInt(); - - await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) - .testUtils.justTakeFee(alice); - const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; - - const aliceInitBalance = await helper.balance.getSubstrate(alice.address); - let diff; - - await helper.wait.forParachainBlockNumber(executionBlock); - - const aliceBalanceAfterFirst = await helper.balance.getSubstrate(alice.address); - expect( - aliceBalanceAfterFirst < aliceInitBalance, - '[after execution #1] Scheduled task should take a fee', - ).to.be.true; - - diff = aliceInitBalance - aliceBalanceAfterFirst; - expect(diff).to.be.equal( - expectedScheduledFee, - 'Scheduled task should take the right amount of fees', - ); - - await helper.wait.forParachainBlockNumber(executionBlock + periodic.period); - - const aliceBalanceAfterSecond = await helper.balance.getSubstrate(alice.address); - expect( - aliceBalanceAfterSecond < aliceBalanceAfterFirst, - '[after execution #2] Scheduled task should take a fee', - ).to.be.true; - - diff = aliceBalanceAfterFirst - aliceBalanceAfterSecond; - expect(diff).to.be.equal( - expectedScheduledFee, - 'Scheduled task should take the right amount of fees', - ); - }); - - // Check if we can cancel a scheduled periodic operation - // in the same block in which it is running - itSub.ifWithPallets('Can cancel the periodic sheduled tx when the tx is running', [Pallets.TestUtils], async ({helper}) => { - const currentBlockNumber = await helper.chain.getLatestBlockNumber(); - const blocksBeforeExecution = 10; - const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution; - - const [ - scheduledId, - scheduledCancelId, - ] = helper.arrange.makeScheduledIds(2); - - const periodic = { - period: 5, - repetitions: 5, - }; - - const initTestVal = 0; - const incTestVal = initTestVal + 1; - const finalTestVal = initTestVal + 2; - - await helper.testUtils.setTestValue(alice, initTestVal); - - await helper.scheduler.scheduleAt(firstExecutionBlockNumber, {scheduledId, periodic}) - .testUtils.incTestValue(alice); - - // Cancel the inc tx after 2 executions - // *in the same block* in which the second execution is scheduled - await helper.scheduler.scheduleAt( - firstExecutionBlockNumber + periodic.period, - {scheduledId: scheduledCancelId}, - ).scheduler.cancelScheduled(alice, scheduledId); - - await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber); - - // execution #0 - expect(await helper.testUtils.testValue()) - .to.be.equal(incTestVal); - - await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period); - - // execution #1 - expect(await helper.testUtils.testValue()) - .to.be.equal(finalTestVal); - - for(let i = 1; i < periodic.repetitions; i++) { - await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period * (i + 1)); - expect(await helper.testUtils.testValue()) - .to.be.equal(finalTestVal); - } - }); - - itSub.ifWithPallets('A scheduled operation can cancel itself', [Pallets.TestUtils], async ({helper}) => { - const scheduledId = helper.arrange.makeScheduledId(); - const waitForBlocks = 4; - const periodic = { - period: 2, - repetitions: 5, - }; - - const initTestVal = 0; - const maxTestVal = 2; - - await helper.testUtils.setTestValue(alice, initTestVal); - - await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) - .testUtils.selfCancelingInc(alice, scheduledId, maxTestVal); - const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; - - await helper.wait.forParachainBlockNumber(executionBlock); - - // execution #0 - expect(await helper.testUtils.testValue()) - .to.be.equal(initTestVal + 1); - - await helper.wait.forParachainBlockNumber(executionBlock + periodic.period); - - // execution #1 - expect(await helper.testUtils.testValue()) - .to.be.equal(initTestVal + 2); - - await helper.wait.forParachainBlockNumber(executionBlock + 2 * periodic.period); - - // - expect(await helper.testUtils.testValue()) - .to.be.equal(initTestVal + 2); - }); - - itSub('Root can cancel any scheduled operation', async ({helper}) => { - const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'}); - const token = await collection.mintToken(bob); - - const scheduledId = helper.arrange.makeScheduledId(); - const waitForBlocks = 4; - - 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); - - await helper.wait.forParachainBlockNumber(executionBlock); - - expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); - }); - - itSched('Root can set prioritized scheduled operation', async (scheduleKind, {helper}) => { - const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; - const waitForBlocks = 4; - - const amount = 42n * helper.balance.getOneTokenNominal(); - - const balanceBefore = await helper.balance.getSubstrate(charlie.address); - - await helper.getSudo() - .scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42}) - .balance.forceTransferToSubstrate(superuser, bob.address, charlie.address, amount); - const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; - - await helper.wait.forParachainBlockNumber(executionBlock); - - const balanceAfter = await helper.balance.getSubstrate(charlie.address); - - expect(balanceAfter > balanceBefore).to.be.true; - - const diff = balanceAfter - balanceBefore; - expect(diff).to.be.equal(amount); - }); - - itSub("Root can change scheduled operation's priority", async ({helper}) => { - const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'}); - const token = await collection.mintToken(bob); - - const scheduledId = helper.arrange.makeScheduledId(); - const waitForBlocks = 6; - - 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; - await helper.getSudo().scheduler.changePriority(superuser, scheduledId, priority); - - const priorityChanged = await helper.wait.expectEvent(waitForBlocks, Event.UniqueScheduler.PriorityChanged); - - const [blockNumber, index] = priorityChanged.task(); - expect(blockNumber).to.be.equal(executionBlock); - expect(index).to.be.equal(0); - - expect(priorityChanged.priority().toString()).to.be.equal(priority.toString()); - }); - - itSub('Prioritized operations execute in valid order', async ({helper}) => { - const [ - scheduledFirstId, - scheduledSecondId, - ] = helper.arrange.makeScheduledIds(2); - - const currentBlockNumber = await helper.chain.getLatestBlockNumber(); - const blocksBeforeExecution = 6; - const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution; - - const prioHigh = 0; - const prioLow = 255; - - const periodic = { - period: 6, - repetitions: 2, - }; - - const amount = 1n * helper.balance.getOneTokenNominal(); - - // Scheduler a task with a lower priority first, then with a higher priority - await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, { - scheduledId: scheduledFirstId, - priority: prioLow, - periodic, - }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount); - - await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, { - scheduledId: scheduledSecondId, - priority: prioHigh, - periodic, - }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount); - - const capture = await helper.arrange.captureEvents('scheduler', 'Dispatched'); - - await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber); - - // Flip priorities - await helper.getSudo().scheduler.changePriority(superuser, scheduledFirstId, prioHigh); - await helper.getSudo().scheduler.changePriority(superuser, scheduledSecondId, prioLow); - - await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period); - - const dispatchEvents = capture.extractCapturedEvents(); - expect(dispatchEvents.length).to.be.equal(4); - - const dispatchedIds = dispatchEvents.map(r => r.event.data[1].toString()); - - const firstExecuctionIds = [dispatchedIds[0], dispatchedIds[1]]; - const secondExecuctionIds = [dispatchedIds[2], dispatchedIds[3]]; - - expect(firstExecuctionIds[0]).to.be.equal(scheduledSecondId); - expect(firstExecuctionIds[1]).to.be.equal(scheduledFirstId); - - expect(secondExecuctionIds[0]).to.be.equal(scheduledFirstId); - expect(secondExecuctionIds[1]).to.be.equal(scheduledSecondId); - }); - - itSched('Periodic operations always can be rescheduled', async (scheduleKind, {helper}) => { - const maxScheduledPerBlock = 50; - const numFilledBlocks = 3; - const ids = helper.arrange.makeScheduledIds(numFilledBlocks * maxScheduledPerBlock + 1); - const periodicId = scheduleKind == 'named' ? ids[0] : undefined; - const fillIds = ids.slice(1); - - const fillScheduleFn = scheduleKind == 'named' ? 'scheduleNamed' : 'schedule'; - - const initTestVal = 0; - const firstExecTestVal = 1; - const secondExecTestVal = 2; - await helper.testUtils.setTestValue(alice, initTestVal); - - const currentBlockNumber = await helper.chain.getLatestBlockNumber(); - const blocksBeforeExecution = 8; - const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution; - - const period = 5; - - const periodic = { - period, - repetitions: 2, - }; - - // Fill `numFilledBlocks` blocks beginning from the block in which the second execution should occur - const txs = []; - for(let offset = 0; offset < numFilledBlocks; offset ++) { - for(let i = 0; i < maxScheduledPerBlock; i++) { - - const scheduledTx = helper.constructApiCall('api.tx.balances.transfer', [bob.address, 1n]); - - const when = firstExecutionBlockNumber + period + offset; - const mandatoryArgs = [when, null, null, scheduledTx]; - const scheduleArgs = scheduleKind == 'named' - ? [fillIds[i + offset * maxScheduledPerBlock], ...mandatoryArgs] - : mandatoryArgs; - - const tx = helper.constructApiCall(`api.tx.scheduler.${fillScheduleFn}`, scheduleArgs); - - txs.push(tx); - } - } - await helper.executeExtrinsic(alice, 'api.tx.testUtils.batchAll', [txs], true); - - await helper.scheduler.scheduleAt(firstExecutionBlockNumber, { - scheduledId: periodicId, - periodic, - }).testUtils.incTestValue(alice); - - await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber); - expect(await helper.testUtils.testValue()).to.be.equal(firstExecTestVal); - - await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + period + numFilledBlocks); - - // The periodic operation should be postponed by `numFilledBlocks` - for(let i = 0; i < numFilledBlocks; i++) { - expect(await helper.testUtils.testValue(firstExecutionBlockNumber + period + i)).to.be.equal(firstExecTestVal); - } - - // After the `numFilledBlocks` the periodic operation will eventually be executed - expect(await helper.testUtils.testValue()).to.be.equal(secondExecTestVal); - }); - - itSched('scheduled operations does not change nonce', async (scheduleKind, {helper}) => { - const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; - const blocksBeforeExecution = 4; - - await helper.scheduler - .scheduleAfter(blocksBeforeExecution, {scheduledId}) - .balance.transferToSubstrate(alice, bob.address, 1n); - const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1; - - const initNonce = await helper.chain.getNonce(alice.address); - - await helper.wait.forParachainBlockNumber(executionBlock); - - const finalNonce = await helper.chain.getNonce(alice.address); - - expect(initNonce).to.be.equal(finalNonce); - }); -}); - -describe('Negative Test: Scheduling', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.UniqueScheduler]); - - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - - await helper.testUtils.enable(Pallets.TestUtils); - }); - }); - - itSub("Can't overwrite a scheduled ID", async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); - const token = await collection.mintToken(alice); - - const scheduledId = helper.arrange.makeScheduledId(); - const waitForBlocks = 4; - - 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}); - await expect(scheduled.balance.transferToSubstrate(alice, bob.address, 1n * helper.balance.getOneTokenNominal())) - .to.be.rejectedWith(/scheduler\.FailedToSchedule/); - - const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address); - - await helper.wait.forParachainBlockNumber(executionBlock); - - const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address); - - expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); - expect(bobsBalanceBefore).to.be.equal(bobsBalanceAfter); - }); - - itSub("Can't cancel an operation which is not scheduled", async ({helper}) => { - const scheduledId = helper.arrange.makeScheduledId(); - await expect(helper.scheduler.cancelScheduled(alice, scheduledId)) - .to.be.rejectedWith(/scheduler\.NotFound/); - }); - - itSub("Can't cancel a non-owned scheduled operation", async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); - const token = await collection.mintToken(alice); - - const scheduledId = helper.arrange.makeScheduledId(); - const waitForBlocks = 4; - - 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)) - .to.be.rejectedWith(/BadOrigin/); - - await helper.wait.forParachainBlockNumber(executionBlock); - - expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); - }); - - itSched("Regular user can't set prioritized scheduled operation", async (scheduleKind, {helper}) => { - const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; - const waitForBlocks = 4; - - const amount = 42n * helper.balance.getOneTokenNominal(); - - const balanceBefore = await helper.balance.getSubstrate(bob.address); - - const scheduled = helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42}); - - await expect(scheduled.balance.transferToSubstrate(alice, bob.address, amount)) - .to.be.rejectedWith(/BadOrigin/); - - const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; - - await helper.wait.forParachainBlockNumber(executionBlock); - - const balanceAfter = await helper.balance.getSubstrate(bob.address); - - expect(balanceAfter).to.be.equal(balanceBefore); - }); - - itSub("Regular user can't change scheduled operation's priority", async ({helper}) => { - const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'}); - const token = await collection.mintToken(bob); - - const scheduledId = helper.arrange.makeScheduledId(); - const waitForBlocks = 4; - - 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)) - .to.be.rejectedWith(/BadOrigin/); - - await helper.wait.expectEvent(waitForBlocks, Event.UniqueScheduler.PriorityChanged); - }); -}); - -// Implementation of the functionality tested here was postponed/shelved -describe.skip('Sponsoring scheduling', () => { - // let alice: IKeyringPair; - // let bob: IKeyringPair; - - // before(async() => { - // await usingApi(async (_, privateKey) => { - // alice = privateKey('//Alice'); - // bob = privateKey('//Bob'); - // }); - // }); - - it('Can sponsor scheduling a transaction', async () => { - // const collectionId = await createCollectionExpectSuccess(); - // await setCollectionSponsorExpectSuccess(collectionId, bob.address); - // await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); - - // await usingApi(async api => { - // const scheduledId = await makeScheduledId(); - // const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address); - - // const bobBalanceBefore = await getFreeBalance(bob); - // const waitForBlocks = 4; - // // no need to wait to check, fees must be deducted on scheduling, immediately - // await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 0, waitForBlocks, scheduledId); - // const bobBalanceAfter = await getFreeBalance(bob); - // // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true; - // expect(bobBalanceAfter < bobBalanceBefore).to.be.true; - // // wait for sequentiality matters - // await waitNewBlocks(waitForBlocks - 1); - // }); - }); - - it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => { - // await usingApi(async (api, privateKey) => { - // // Find an empty, unused account - // const zeroBalance = await findUnusedAddress(api, privateKey); - - // const collectionId = await createCollectionExpectSuccess(); - - // // Add zeroBalance address to allow list - // await enablePublicMintingExpectSuccess(alice, collectionId); - // await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address); - - // // Grace zeroBalance with money, enough to cover future transactions - // const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE); - // await submitTransactionAsync(alice, balanceTx); - - // // Mint a fresh NFT - // const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT'); - // const scheduledId = await makeScheduledId(); - - // // Schedule transfer of the NFT a few blocks ahead - // const waitForBlocks = 5; - // await scheduleTransferExpectSuccess(api, collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, scheduledId); - - // // Get rid of the account's funds before the scheduled transaction takes place - // const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n); - // const events = await submitTransactionAsync(zeroBalance, balanceTx2); - // expect(getGenericResult(events).success).to.be.true; - // /*const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved? - // const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any); - // const events = await submitTransactionAsync(alice, sudoTx); - // expect(getGenericResult(events).success).to.be.true;*/ - - // // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions - // await waitNewBlocks(waitForBlocks - 3); - - // expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address)); - // }); - }); - - it('Sponsor going bankrupt does not impact a scheduled transaction', async () => { - // const collectionId = await createCollectionExpectSuccess(); - - // await usingApi(async (api, privateKey) => { - // const zeroBalance = await findUnusedAddress(api, privateKey); - // const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE); - // await submitTransactionAsync(alice, balanceTx); - - // await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address); - // await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance); - - // const scheduledId = await makeScheduledId(); - // const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address); - - // const waitForBlocks = 5; - // await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, scheduledId); - - // const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); - // const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any); - // const events = await submitTransactionAsync(alice, sudoTx); - // expect(getGenericResult(events).success).to.be.true; - - // // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions - // await waitNewBlocks(waitForBlocks - 3); - - // expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address)); - // }); - }); - - it('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => { - // const collectionId = await createCollectionExpectSuccess(); - // await setCollectionSponsorExpectSuccess(collectionId, bob.address); - // await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); - - // await usingApi(async (api, privateKey) => { - // const zeroBalance = await findUnusedAddress(api, privateKey); - - // await enablePublicMintingExpectSuccess(alice, collectionId); - // await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address); - - // const bobBalanceBefore = await getFreeBalance(bob); - - // const createData = {nft: {const_data: [], variable_data: []}}; - // const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any); - // const scheduledId = await makeScheduledId(); - - // /*const badTransaction = async function () { - // await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice); - // }; - // await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/ - - // await expect(scheduleAfter(api, creationTx, zeroBalance, 3, scheduledId, 1, 3)).to.be.rejectedWith(/Inability to pay some fees/); - - // expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore); - // }); - }); -}); --- a/js-packages/tests/src/setCollectionLimits.test.ts +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -// https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from './util/index.js'; -import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js'; - -const accountTokenOwnershipLimit = 0; -const sponsoredDataSize = 0; -const sponsorTransferTimeout = 1; -const tokenLimit = 10; - -describe('setCollectionLimits positive', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); - }); - }); - - itSub('execute setCollectionLimits with predefined params', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-1', tokenPrefix: 'SCL'}); - - await collection.setLimits( - alice, - { - accountTokenOwnershipLimit, - sponsoredDataSize, - tokenLimit, - sponsorTransferTimeout, - ownerCanTransfer: true, - ownerCanDestroy: true, - }, - ); - - // get collection limits defined previously - const collectionInfo = await collection.getEffectiveLimits(); - - expect(collectionInfo.accountTokenOwnershipLimit).to.be.equal(accountTokenOwnershipLimit); - expect(collectionInfo.sponsoredDataSize).to.be.equal(sponsoredDataSize); - expect(collectionInfo.tokenLimit).to.be.equal(tokenLimit); - expect(collectionInfo.sponsorTransferTimeout).to.be.equal(sponsorTransferTimeout); - expect(collectionInfo.ownerCanTransfer).to.be.true; - expect(collectionInfo.ownerCanDestroy).to.be.true; - }); - - itSub('Set the same token limit twice', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-2', tokenPrefix: 'SCL'}); - - const collectionLimits = { - accountTokenOwnershipLimit, - sponsoredDataSize, - tokenLimit, - sponsorTransferTimeout, - ownerCanTransfer: true, - ownerCanDestroy: true, - }; - - await collection.setLimits(alice, collectionLimits); - - const collectionInfo1 = await collection.getEffectiveLimits(); - - expect(collectionInfo1.tokenLimit).to.be.equal(tokenLimit); - - await collection.setLimits(alice, collectionLimits); - const collectionInfo2 = await collection.getEffectiveLimits(); - expect(collectionInfo2.tokenLimit).to.be.equal(tokenLimit); - }); - - itSub('execute setCollectionLimits from admin collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-3', tokenPrefix: 'SCL'}); - await collection.addAdmin(alice, {Substrate: bob.address}); - - const collectionLimits = { - accountTokenOwnershipLimit, - sponsoredDataSize, - // sponsoredMintSize, - tokenLimit, - }; - - await expect(collection.setLimits(alice, collectionLimits)).to.not.be.rejected; - }); -}); - -describe('setCollectionLimits negative', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); - }); - }); - - itSub('execute setCollectionLimits for not exists collection', async ({helper}) => { - const nonExistentCollectionId = NON_EXISTENT_COLLECTION_ID; - await expect(helper.collection.setLimits( - alice, - nonExistentCollectionId, - { - accountTokenOwnershipLimit, - sponsoredDataSize, - // sponsoredMintSize, - tokenLimit, - }, - )).to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('execute setCollectionLimits from user who is not owner of this collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-1', tokenPrefix: 'SCL'}); - - await expect(collection.setLimits(bob, { - accountTokenOwnershipLimit, - sponsoredDataSize, - // sponsoredMintSize, - tokenLimit, - })).to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('fails when trying to enable OwnerCanTransfer after it was disabled', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-2', tokenPrefix: 'SCL'}); - - await collection.setLimits(alice, { - accountTokenOwnershipLimit, - sponsoredDataSize, - tokenLimit, - sponsorTransferTimeout, - ownerCanTransfer: false, - ownerCanDestroy: true, - }); - - await expect(collection.setLimits(alice, { - accountTokenOwnershipLimit, - sponsoredDataSize, - tokenLimit, - sponsorTransferTimeout, - ownerCanTransfer: true, - ownerCanDestroy: true, - })).to.be.rejectedWith(/common\.OwnerPermissionsCantBeReverted/); - }); - - itSub('fails when trying to enable OwnerCanDestroy after it was disabled', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-3', tokenPrefix: 'SCL'}); - - await collection.setLimits(alice, { - accountTokenOwnershipLimit, - sponsoredDataSize, - tokenLimit, - sponsorTransferTimeout, - ownerCanTransfer: true, - ownerCanDestroy: false, - }); - - await expect(collection.setLimits(alice, { - accountTokenOwnershipLimit, - sponsoredDataSize, - tokenLimit, - sponsorTransferTimeout, - ownerCanTransfer: true, - ownerCanDestroy: true, - })).to.be.rejectedWith(/common\.OwnerPermissionsCantBeReverted/); - }); - - itSub('Setting the higher token limit fails', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-4', tokenPrefix: 'SCL'}); - - const collectionLimits = { - accountTokenOwnershipLimit: accountTokenOwnershipLimit, - sponsoredMintSize: sponsoredDataSize, - tokenLimit: tokenLimit, - sponsorTransferTimeout, - ownerCanTransfer: true, - ownerCanDestroy: true, - }; - - // The first time - await collection.setLimits(alice, collectionLimits); - - // The second time - higher token limit - collectionLimits.tokenLimit += 1; - await expect(collection.setLimits(alice, collectionLimits)).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/); - }); -}); --- a/js-packages/tests/src/setCollectionSponsor.test.ts +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect, Pallets} from './util/index.js'; -import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js'; - -describe('integration test: ext. setCollectionSponsor():', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); - }); - }); - - itSub('Set NFT collection sponsor', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-1-NFT', tokenPrefix: 'SCS'}); - await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; - - expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ - Unconfirmed: bob.address, - }); - }); - - itSub('Set Fungible collection sponsor', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'SetCollectionSponsor-1-FT', tokenPrefix: 'SCS'}); - await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; - - expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ - Unconfirmed: bob.address, - }); - }); - - itSub.ifWithPallets('Set ReFungible collection sponsor', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'SetCollectionSponsor-1-RFT', tokenPrefix: 'SCS'}); - await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; - - expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ - Unconfirmed: bob.address, - }); - }); - - itSub('Set the same sponsor repeatedly', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-2', tokenPrefix: 'SCS'}); - await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; - await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; - - expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ - Unconfirmed: bob.address, - }); - }); - - itSub('Replace collection sponsor', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-3', tokenPrefix: 'SCS'}); - await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; - await expect(collection.setSponsor(alice, charlie.address)).to.be.not.rejected; - - expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ - Unconfirmed: charlie.address, - }); - }); - - itSub('Collection admin add sponsor', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-4', tokenPrefix: 'SCS'}); - await collection.addAdmin(alice, {Substrate: bob.address}); - await expect(collection.setSponsor(bob, charlie.address)).to.be.not.rejected; - - expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ - Unconfirmed: charlie.address, - }); - }); -}); - -describe('(!negative test!) integration test: ext. setCollectionSponsor():', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([10n, 5n], donor); - }); - }); - - itSub('(!negative test!) Add sponsor with a non-owner', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-Neg-1', tokenPrefix: 'SCS'}); - await expect(collection.setSponsor(bob, bob.address)) - .to.be.rejectedWith(/common\.NoPermission/); - }); - - itSub('(!negative test!) Add sponsor to a collection that never existed', async ({helper}) => { - const collectionId = NON_EXISTENT_COLLECTION_ID; - await expect(helper.collection.setSponsor(alice, collectionId, bob.address)) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('(!negative test!) Add sponsor to a collection that was destroyed', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-Neg-2', tokenPrefix: 'SCS'}); - await collection.burn(alice); - await expect(collection.setSponsor(alice, bob.address)) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); -}); --- a/js-packages/tests/src/setPermissions.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from './util/index.js'; -import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js'; - -describe('Integration Test: Set Permissions', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - }); - }); - - itSub('can all be enabled twice', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-1', tokenPrefix: 'SP'}); - expect((await collection.getData())?.raw.permissions.access).to.not.equal('AllowList'); - - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}}); - await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}}); - - const permissions = (await collection.getData())?.raw.permissions; - expect(permissions).to.be.deep.equal({ - access: 'AllowList', - mintMode: true, - nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}, - }); - }); - - itSub('can all be disabled twice', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-2', tokenPrefix: 'SP'}); - expect((await collection.getData())?.raw.permissions.access).to.equal('Normal'); - - await collection.setPermissions(alice, {access: 'AllowList', nesting: {collectionAdmin: false, tokenOwner: true, restricted: [1, 2]}}); - expect((await collection.getData())?.raw.permissions).to.be.deep.equal({ - access: 'AllowList', - mintMode: false, - nesting: {collectionAdmin: false, tokenOwner: true, restricted: [1, 2]}, - }); - - await collection.setPermissions(alice, {access: 'Normal', mintMode: false, nesting: {}}); - await collection.setPermissions(alice, {access: 'Normal', mintMode: false, nesting: {}}); - expect((await collection.getData())?.raw.permissions).to.be.deep.equal({ - access: 'Normal', - mintMode: false, - nesting: {collectionAdmin: false, tokenOwner: false, restricted: null}, - }); - }); - - itSub('collection admin can set permissions', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-2', tokenPrefix: 'SP'}); - await collection.addAdmin(alice, {Substrate: bob.address}); - await collection.setPermissions(bob, {access: 'AllowList', mintMode: true}); - - expect((await collection.getData())?.raw.permissions.access).to.equal('AllowList'); - expect((await collection.getData())?.raw.permissions.mintMode).to.equal(true); - }); -}); - -describe('Negative Integration Test: Set Permissions', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - }); - }); - - itSub('fails on not existing collection', async ({helper}) => { - const collectionId = NON_EXISTENT_COLLECTION_ID; - await expect(helper.collection.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('fails on removed collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-Neg-1', tokenPrefix: 'SP'}); - await collection.burn(alice); - - await expect(collection.setPermissions(alice, {access: 'AllowList', mintMode: true})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('fails when non-owner tries to set permissions', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-Neg-2', tokenPrefix: 'SP'}); - - await expect(collection.setPermissions(bob, {access: 'AllowList', mintMode: true})) - .to.be.rejectedWith(/common\.NoPermission/); - }); -}); --- a/js-packages/tests/src/sub/appPromotion/appPromotion.seqtest.ts +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, Pallets, requirePalletsOrSkip} from '../../util/index.js'; -import {expect} from '../../eth/util/index.js'; - -let superuser: IKeyringPair; -let donor: IKeyringPair; -let palletAdmin: IKeyringPair; - -describe('App promotion', () => { - before(async function () { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.AppPromotion]); - superuser = await privateKey('//Alice'); - donor = await privateKey({url: import.meta.url}); - palletAdmin = await privateKey('//PromotionAdmin'); - const api = helper.getApi(); - await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address}))); - }); - }); - - after(async function () { - await usingPlaygrounds(async (helper) => { - if(helper.fetchMissingPalletNames([Pallets.AppPromotion]).length != 0) return; - const api = helper.getApi(); - await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address}))); - }); - }); - - describe('admin adress', () => { - itSub('can be set by sudo only', async ({helper}) => { - const api = helper.getApi(); - const [nonAdmin] = await helper.arrange.createAccounts([10n], donor); - // nonAdmin can not set admin not from himself nor as a sudo - await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address}))).to.be.rejected; - await expect(helper.signTransaction(nonAdmin, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address})))).to.be.rejected; - }); - - itSub('can be any valid CrossAccountId', async ({helper}) => { - // We are not going to set an eth address as a sponsor, - // but we do want to check, it doesn't break anything; - const api = helper.getApi(); - const [account] = await helper.arrange.createAccounts([10n], donor); - const ethAccount = helper.address.substrateToEth(account.address); - // Alice sets Ethereum address as a sudo. Then Substrate address back... - await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Ethereum: ethAccount})))).to.be.fulfilled; - await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.fulfilled; - - // ...It doesn't break anything; - const collection = await helper.nft.mintCollection(account, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - await expect(helper.signTransaction(account, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; - }); - - itSub('can be reassigned', async ({helper}) => { - const api = helper.getApi(); - const [oldAdmin, newAdmin, collectionOwner] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - - await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: oldAdmin.address})))).to.be.fulfilled; - await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: newAdmin.address})))).to.be.fulfilled; - await expect(helper.signTransaction(oldAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; - - await expect(helper.signTransaction(newAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled; - }); - }); -}); - --- a/js-packages/tests/src/sub/appPromotion/appPromotion.test.ts +++ /dev/null @@ -1,1008 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import { - itSub, usingPlaygrounds, Pallets, requirePalletsOrSkip, LOCKING_PERIOD, UNLOCKING_PERIOD, -} from '../../util/index.js'; -import {DevUniqueHelper} from '@unique/playgrounds/src/unique.dev.js'; -import {itEth, expect, SponsoringMode} from '../../eth/util/index.js'; - -let donor: IKeyringPair; -let palletAdmin: IKeyringPair; -let nominal: bigint; -let palletAddress: string; -let accounts: IKeyringPair[]; -let usedAccounts: IKeyringPair[] = []; - -async function getAccounts(accountsNumber: number, balance?: bigint) { - let accs: IKeyringPair[] = []; - if(balance) { - await usingPlaygrounds(async (helper) => { - accs = await helper.arrange.createAccounts(new Array(accountsNumber).fill(balance), donor); - }); - } else { - accs = accounts.splice(0, accountsNumber); - } - usedAccounts.push(...accs); - return accs; -} -// App promotion periods: -// LOCKING_PERIOD = 12 blocks of relay -// UNLOCKING_PERIOD = 6 blocks of parachain - -describe('App promotion', () => { - before(async function () { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.AppPromotion]); - donor = await privateKey({url: import.meta.url}); - palletAddress = helper.arrange.calculatePalletAddress('appstake'); - palletAdmin = await privateKey('//PromotionAdmin'); - - nominal = helper.balance.getOneTokenNominal(); - - const accountBalances = new Array(200).fill(1000n); - accounts = await helper.arrange.createAccounts(accountBalances, donor); // create accounts-pool to speed up tests - }); - }); - - afterEach(async () => { - await usingPlaygrounds(async (helper) => { - let unstakeTxs = []; - for(const account of usedAccounts) { - if(unstakeTxs.length === 3) { - await Promise.all(unstakeTxs); - unstakeTxs = []; - } - unstakeTxs.push(helper.staking.unstakeAll(account)); - } - await Promise.all(unstakeTxs); - usedAccounts = []; - expect(await helper.staking.getTotalStaked()).to.eq(0n); // there are no active stakes after each test - // Make sure previousCalculatedRecord is None to avoid problem with payout stakers; - await helper.admin.payoutStakers(palletAdmin, 100); - expect((await helper.getApi().query.appPromotion.previousCalculatedRecord() as any).isNone).to.be.true; - }); - }); - - describe('stake extrinsic', () => { - itSub('should "freeze" staking balance, add it to "staked" map, and increase "totalStaked" amount', async ({helper}) => { - const [staker, recepient] = await getAccounts(2); - const totalStakedBefore = await helper.staking.getTotalStaked(); - - // Minimum stake amount is 100: - await expect(helper.staking.stake(staker, 100n * nominal - 1n)).to.be.rejected; - await helper.staking.stake(staker, 100n * nominal); - - // Staker balance is: frozen: 100, reserved: 0n... - // ...so he can not transfer 900 - expect(await helper.balance.getSubstrateFull(staker.address)).to.contain({frozen: 100n * nominal, reserved: 0n}); - expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: 100n * nominal}]); - await expect(helper.balance.transferToSubstrate(staker, recepient.address, 900n * nominal)).to.be.rejectedWith(/^Token: Frozen$/); - - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(100n * nominal); - expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); - // it is potentially flaky test. Promotion can credited some tokens. Maybe we need to use closeTo? - expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore + 100n * nominal); // total tokens amount staked in app-promotion increased - - await helper.staking.stake(staker, 200n * nominal); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(300n * nominal); - const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - expect(totalStakedPerBlock[0].amount).to.equal(100n * nominal); - expect(totalStakedPerBlock[1].amount).to.equal(200n * nominal); - }); - - [ - {unstake: 'unstakeAll' as const}, - {unstake: 'unstakePartial' as const}, - ].map(testCase => { - itSub(`[${testCase.unstake}] should allow to create maximum 10 stakes for account`, async ({helper}) => { - const [staker] = await getAccounts(1, 2000n); - const ONE_STAKE = 100n * nominal; - for(let i = 0; i < 10; i++) { - await helper.staking.stake(staker, ONE_STAKE); - } - - // can have 10 stakes - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(1000n * nominal); - expect(await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).to.have.length(10); - - await expect(helper.staking.stake(staker, ONE_STAKE)).to.be.rejectedWith('appPromotion.NoPermission'); - - // After unstake can stake again - - // CASE 1: unstakeAll - if(testCase.unstake === 'unstakeAll') { - await helper.staking.unstakeAll(staker); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0); - await helper.staking.stake(staker, 100n * nominal); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.equal(100n * nominal); - } - // CASE 2: unstakePartial - else { - await helper.staking.unstakePartial(staker, ONE_STAKE); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(9); - await helper.staking.stake(staker, 100n * nominal); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(10); - await expect(helper.staking.stake(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.NoPermission'); - await helper.staking.unstakePartial(staker, 150n * nominal); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(9); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.equal(850n * nominal); - } - }); - }); - // TODO: Now AppPromo makes freezes. Probably this test should be changed\removed. - itSub.skip('should allow to stake() if balance is locked with different id', async ({helper}) => { - const [staker] = await getAccounts(1); - - // staker has tokens locked with vesting id: - await helper.balance.vestedTransfer(donor, staker.address, {start: 0n, period: 1n, periodCount: 1n, perPeriod: 200n * nominal}); - expect(await helper.balance.getSubstrateFull(staker.address)) - .to.deep.contain({free: 1200n * nominal, frozen: 200n * nominal, reserved: 0n}); - - // Locked balance can be staked. staker can stake 1200 tokens (minus fee): - await helper.staking.stake(staker, 1000n * nominal); - await helper.staking.stake(staker, 199n * nominal); - // check balances - expect(await helper.balance.getLocked(staker.address)).to.deep.eq([{id: 'ormlvest', amount: 200n * nominal, reasons: 'All'}]); - expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: 1199n * nominal}]); - expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 1199n * nominal}); - expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(1199n); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(1199n * nominal); - - // staker can unstake - await helper.staking.unstakeAll(staker); - expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(1199n * nominal); - const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); - await helper.wait.forParachainBlockNumber(pendingUnstake.block); - - // check balances - expect(await helper.balance.getLocked(staker.address)).to.deep.eq([{id: 'ormlvest', amount: 200n * nominal, reasons: 'All'}]); - expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 200n * nominal}); - expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(1199n); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n); - - // staker can transfer balances now - await helper.balance.transferToSubstrate(staker, donor.address, 900n * nominal); - }); - - itSub('should not allow to stake(), if stake amount is more than total free balance minus locked by staking', async ({helper}) => { - const [staker] = await getAccounts(1); - - // Can't stake full balance because Alice needs to pay some fee - await expect(helper.staking.stake(staker, 1000n * nominal)).to.be.rejected; // With('Arithmetic') - await helper.staking.stake(staker, 500n * nominal); - - // Can't stake 500 tkn because Alice has Less than 500 transferable; - await expect(helper.staking.stake(staker, 500n * nominal)).to.be.rejected; // With('Arithmetic'); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(500n * nominal); - }); - - itSub('for different accounts in one block is possible', async ({helper}) => { - const crowd = await getAccounts(4); - - const crowdStartsToStake = crowd.map(user => helper.staking.stake(user, 100n * nominal)); - await expect(Promise.all(crowdStartsToStake)).to.be.fulfilled; - - const crowdStakes = await Promise.all(crowd.map(address => helper.staking.getTotalStaked({Substrate: address.address}))); - expect(crowdStakes).to.deep.equal([100n * nominal, 100n * nominal, 100n * nominal, 100n * nominal]); - }); - }); - - describe('Unstaking', () => { - [ - {method: 'unstakeAll' as const}, - {method: 'unstakePartial' as const}, - ].map(testCase => { - itSub(`[${testCase.method}] should move tokens to "pendingUnstake" and subtract it from totalStaked`, async ({helper}) => { - const [staker, recepient] = await getAccounts(2); - const totalStakedBefore = await helper.staking.getTotalStaked(); - const STAKE_AMOUNT = 900n * nominal; - - await helper.staking.stake(staker, STAKE_AMOUNT); - testCase.method === 'unstakeAll' - ? await helper.staking.unstakeAll(staker) - : await helper.staking.unstakePartial(staker, STAKE_AMOUNT); - - // Right after unstake tokens are still locked - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0); - expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: STAKE_AMOUNT}]); - expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: STAKE_AMOUNT}); - // Staker can not transfer - await expect(helper.balance.transferToSubstrate(staker, recepient.address, 100n * nominal)).to.be.rejectedWith(/^Token: Frozen$/); - expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(STAKE_AMOUNT); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); - expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore); - }); - }); - - [ - {method: 'unstakeAll' as const}, - {method: 'unstakePartial' as const}, - ].map(testCase => { - itSub(`[${testCase.method}] should unlock balance after unlocking period ends and remove it from "pendingUnstake"`, async ({helper}) => { - const [staker] = await getAccounts(1); - await helper.staking.stake(staker, 100n * nominal); - testCase.method === 'unstakeAll' - ? await helper.staking.unstakeAll(staker) - : await helper.staking.unstakePartial(staker, 100n * nominal); - const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); - - // Wait for unstaking period. Balance now free ~1000; reserved, frozen, miscFrozeb: 0n - await helper.wait.forParachainBlockNumber(pendingUnstake.block); - expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n}); - expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); - - // staker can transfer: - await helper.balance.transferToSubstrate(staker, donor.address, 998n * nominal); - expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(1n); - }); - }); - - [ - {method: 'unstakeAll' as const}, - {method: 'unstakePartial' as const}, - ].map(testCase => { - itSub(`[${testCase.method}] should successfully unstake multiple stakes`, async ({helper}) => { - const [staker] = await getAccounts(1); - await helper.staking.stake(staker, 100n * nominal); - await helper.staking.stake(staker, 200n * nominal); - await helper.staking.stake(staker, 300n * nominal); - - // staked: [100, 200, 300]; unstaked: 0 - let totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address}); - let pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); - let stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - expect(totalPendingUnstake).to.be.deep.equal(0n); - expect(pendingUnstake).to.be.deep.equal([]); - expect(stakes[0].amount).to.equal(100n * nominal); - expect(stakes[1].amount).to.equal(200n * nominal); - expect(stakes[2].amount).to.equal(300n * nominal); - - // Can unstake multiple stakes - testCase.method === 'unstakeAll' - ? await helper.staking.unstakeAll(staker) - : await helper.staking.unstakePartial(staker, 600n * nominal); - - pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); - totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address}); - stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - expect(totalPendingUnstake).to.be.equal(600n * nominal); - expect(stakes).to.be.deep.equal([]); - expect(pendingUnstake[0].amount).to.equal(600n * nominal); - - expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 600n * nominal}); - expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); - await helper.wait.forParachainBlockNumber(pendingUnstake[0].block); - expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n}); - expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); - }); - }); - - [ - {method: 'unstakeAll' as const}, - {method: 'unstakePartial' as const}, - ].map(testCase => { - itSub(`[${testCase.method}] should not have any effects if no active stakes`, async ({helper}) => { - const [staker] = await getAccounts(1); - - // unstake has no effect if no stakes at all - testCase.method === 'unstakeAll' - ? await helper.staking.unstakeAll(staker) - : await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance'); - - expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(0n); - expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); // TODO bigint closeTo helper - - // TODO stake() unstake() waitUnstaked() unstake(); - - // can't unstake if there are only pendingUnstakes - await helper.staking.stake(staker, 100n * nominal); - - if(testCase.method === 'unstakeAll') { - await helper.staking.unstakeAll(staker); - await helper.staking.unstakeAll(staker); - } else { - await helper.staking.unstakePartial(staker, 100n * nominal); - await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance'); - } - - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0); - expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); - }); - }); - - [ - {method: 'unstakeAll' as const}, - {method: 'unstakePartial' as const}, - ].map(testCase => { - itSub(`[${testCase.method}] should create different pending-unlock for each unlocking stake`, async ({helper}) => { - const [staker] = await getAccounts(1); - await helper.staking.stake(staker, 100n * nominal); - testCase.method === 'unstakeAll' - ? await helper.staking.unstakeAll(staker) - : await helper.staking.unstakePartial(staker, 100n * nominal); - await helper.staking.stake(staker, 120n * nominal); - testCase.method === 'unstakeAll' - ? await helper.staking.unstakeAll(staker) - : await helper.staking.unstakePartial(staker, 120n * nominal); - - const unstakingPerBlock = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); - expect(unstakingPerBlock).has.length(2); - expect(unstakingPerBlock[0].amount).to.equal(100n * nominal); - expect(unstakingPerBlock[1].amount).to.equal(120n * nominal); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.equal(0); - }); - }); - - [ - {method: 'unstakeAll' as const}, - {method: 'unstakePartial' as const}, - ].map(testCase => { - itSub(`[${testCase.method}] should be possible for 3 accounts in one block`, async ({helper}) => { - const stakers = await getAccounts(3); - - await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal))); - await Promise.all(stakers.map(staker => testCase.method === 'unstakeAll' - ? helper.staking.unstakeAll(staker) - : helper.staking.unstakePartial(staker, 100n * nominal))); - - await Promise.all(stakers.map(async (staker) => { - expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); - })); - }); - }); - - itSub('should not be possible for more than 3 accounts in one block', async ({helper}) => { - if(!await helper.arrange.isDevNode()) { - const stakers = await getAccounts(10); - - await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal))); - const unstakingResults = await Promise.allSettled(stakers.map((staker, i) => i % 2 === 0 - ? helper.staking.unstakeAll(staker) - : helper.staking.unstakePartial(staker, 100n * nominal))); - - const successfulUnstakes = unstakingResults.filter(result => result.status === 'fulfilled'); - expect(successfulUnstakes).to.have.length(3); - } - }); - - itSub('Cannot partially unstake more than staked', async ({helper}) => { - const [staker] = await getAccounts(1); - // Staker stakes 300: - await helper.staking.stake(staker, 100n * nominal); - await helper.staking.stake(staker, 200n * nominal); - - // cannot usntake 300.00000...1 - await expect(helper.staking.unstakePartial(staker, 300n * nominal + 1n)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance'); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(2); - - await helper.staking.unstakePartial(staker, 150n * nominal); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(1); - await expect(helper.staking.unstakePartial(staker, 150n * nominal + 1n)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance'); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(1); - - // nothing broken, can unstake full amount: - await helper.staking.unstakePartial(staker, 150n * nominal); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(0); - }); - - itSub('Can partially unstake arbitrary amount', async ({helper}) => { - const [staker] = await getAccounts(1); - await helper.staking.stake(staker, 100n * nominal); - await helper.staking.stake(staker, 200n * nominal); - - // 0. Staker cannot unstake negative amount - await expect(helper.staking.unstakePartial(staker, -1n)).to.be.rejected; - - // 1. Staker can unstake 0 wei - await helper.staking.unstakePartial(staker, 0n); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(300n * nominal); - expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(0n); - - // 2. Staker can unstake 1 wei - await helper.staking.unstakePartial(staker, 1n); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(300n * nominal - 1n); - expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(1n); - // 2.1 The oldest stake decreased: - let [stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - expect(stake1.amount).to.eq(100n * nominal - 1n); - expect(stake2.amount).to.eq(200n * nominal); - - // 3. Staker can unstake all but 1 wei - await helper.staking.unstakePartial(staker, 100n * nominal - 2n); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(200n * nominal + 1n); - expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(100n * nominal - 1n); - [stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - expect(stake1.amount).to.eq(1n); - expect(stake2.amount).to.eq(200n * nominal); - }); - - itSub('can mix different type of unstakes', async ({helper}) => { - const [staker] = await getAccounts(1); - await helper.staking.stake(staker, 100n * nominal); - await helper.staking.stake(staker, 200n * nominal); - - await helper.staking.unstakePartial(staker, 50n * nominal); - await helper.staking.unstakeAll(staker); - expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n); - expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(300n * nominal); - - const [_unstake1, unstake2] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); - await helper.wait.forParachainBlockNumber(unstake2.block); - - expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([]); - expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n}); - expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(999n); - expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n); - expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(0n); - expect(await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address})).to.deep.eq([]); - }); - }); - - describe('collection sponsoring', () => { - itSub('should actually sponsor transactions', async ({helper}) => { - const api = helper.getApi(); - const [collectionOwner, tokenSender, receiver] = await getAccounts(3); - const collection = await helper.nft.mintCollection(collectionOwner, {name: 'Name', description: 'Description', tokenPrefix: 'Prefix', limits: {sponsorTransferTimeout: 0}}); - const token = await collection.mintToken(collectionOwner, {Substrate: tokenSender.address}); - await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId)); - const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress); - - await token.transfer(tokenSender, {Substrate: receiver.address}); - expect (await token.getOwner()).to.be.deep.equal({Substrate: receiver.address}); - const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress); - - // senders balance the same, transaction has sponsored - expect (await helper.balance.getSubstrate(tokenSender.address)).to.be.equal(1000n * nominal); - expect (palletBalanceBefore > palletBalanceAfter).to.be.true; - }); - - itSub('can not be set by non admin', async ({helper}) => { - const api = helper.getApi(); - const [collectionOwner, nonAdmin] = await getAccounts(2); - - const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - - await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; - expect((await collection.getData())?.raw.sponsorship).to.equal('Disabled'); - }); - - itSub('should set pallet address as confirmed admin', async ({helper}) => { - const api = helper.getApi(); - const [collectionOwner, oldSponsor] = await getAccounts(2); - - // Can set sponsoring for collection without sponsor - const collectionWithoutSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'No-sponsor', description: 'New Collection', tokenPrefix: 'Promotion'}); - await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithoutSponsor.collectionId))).to.be.fulfilled; - expect((await collectionWithoutSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); - - // Can set sponsoring for collection with unconfirmed sponsor - const collectionWithUnconfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Unconfirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: oldSponsor.address}}); - expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: oldSponsor.address}); - await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithUnconfirmedSponsor.collectionId))).to.be.fulfilled; - expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); - - // Can set sponsoring for collection with confirmed sponsor - const collectionWithConfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Confirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: oldSponsor.address}}); - await collectionWithConfirmedSponsor.confirmSponsorship(oldSponsor); - await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithConfirmedSponsor.collectionId))).to.be.fulfilled; - expect((await collectionWithConfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); - }); - - itSub('can be overwritten by collection owner', async ({helper}) => { - const api = helper.getApi(); - const [collectionOwner, newSponsor] = await getAccounts(2); - const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - const collectionId = collection.collectionId; - - await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionId))).to.be.fulfilled; - - // Collection limits still can be changed by the owner - expect(await collection.setLimits(collectionOwner, {sponsorTransferTimeout: 0})).to.be.true; - expect((await collection.getData())?.raw.limits.sponsorTransferTimeout).to.be.equal(0); - expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); - - // Collection sponsor can be changed too - expect((await collection.setSponsor(collectionOwner, newSponsor.address))).to.be.true; - expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: newSponsor.address}); - }); - - itSub('should not overwrite collection limits set by the owner earlier', async ({helper}) => { - const [owner] = await getAccounts(1); - const api = helper.getApi(); - const limits = {ownerCanDestroy: true, ownerCanTransfer: true, sponsorTransferTimeout: 0}; - const collectionWithLimits = await helper.nft.mintCollection(owner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits}); - - await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithLimits.collectionId))).to.be.fulfilled; - expect((await collectionWithLimits.getData())?.raw.limits).to.be.deep.contain(limits); - }); - - itSub('should reject transaction if collection doesn\'t exist', async ({helper}) => { - const api = helper.getApi(); - const [collectionOwner] = await getAccounts(1); - - // collection has never existed - await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(999999999))).to.be.rejected; - // collection has been burned - const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - await collection.burn(collectionOwner); - - await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; - }); - }); - - describe('stopSponsoringCollection', () => { - itSub('can not be called by non-admin', async ({helper}) => { - const api = helper.getApi(); - const [collectionOwner, nonAdmin] = await getAccounts(2); - const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - - await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled; - - await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.rejected; - expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); - }); - - itSub('should set sponsoring as disabled', async ({helper}) => { - const api = helper.getApi(); - const [collectionOwner, recepient] = await getAccounts(2); - const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits: {sponsorTransferTimeout: 0}}); - const token = await collection.mintToken(collectionOwner, {Substrate: collectionOwner.address}); - - await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId)); - await helper.signTransaction(palletAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId)); - - expect((await collection.getData())?.raw.sponsorship).to.be.equal('Disabled'); - - // Transactions are not sponsored anymore: - const ownerBalanceBefore = await helper.balance.getSubstrate(collectionOwner.address); - await token.transfer(collectionOwner, {Substrate: recepient.address}); - const ownerBalanceAfter = await helper.balance.getSubstrate(collectionOwner.address); - expect(ownerBalanceAfter < ownerBalanceBefore).to.be.equal(true); - }); - - itSub('should not affect collection which is not sponsored by pallete', async ({helper}) => { - const api = helper.getApi(); - const [collectionOwner] = await getAccounts(1); - const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: collectionOwner.address}}); - await collection.confirmSponsorship(collectionOwner); - - await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.rejected; - - expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: collectionOwner.address}); - }); - - itSub('should reject transaction if collection does not exist', async ({helper}) => { - const [collectionOwner] = await getAccounts(1); - const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); - - await collection.burn(collectionOwner); - await expect(helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringCollection', [collection.collectionId], true)).to.be.rejectedWith('common.CollectionNotFound'); - await expect(helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringCollection', [999_999_999], true)).to.be.rejectedWith('common.CollectionNotFound'); - }); - }); - - describe('contract sponsoring', () => { - itEth('should set palletes address as a sponsor', async ({helper}) => { - const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); - const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); - const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); - - await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address]); - - expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true; - expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner); - expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({ - confirmed: { - substrate: palletAddress, - }, - }); - }); - - itEth('should overwrite sponsoring mode and existed sponsor', async ({helper}) => { - const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); - const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); - const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); - - await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled; - - // Contract is self sponsored - expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.be.deep.equal({ - confirmed: { - ethereum: flipper.options.address.toLowerCase(), - }, - }); - - // set promotion sponsoring - await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true); - - // new sponsor is pallet address - expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true; - expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner); - expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({ - confirmed: { - substrate: palletAddress, - }, - }); - }); - - itEth('can be overwritten by contract owner', async ({helper}) => { - const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); - const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); - const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); - - // contract sponsored by pallet - await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true); - - // owner sets self sponsoring - await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected; - - expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true; - expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner); - expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({ - confirmed: { - ethereum: flipper.options.address.toLowerCase(), - }, - }); - }); - - itEth('can not be set by non admin', async ({helper}) => { - const [nonAdmin] = await getAccounts(1); - const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); - const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); - const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); - - await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled; - - // nonAdmin calls sponsorContract - await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true)).to.be.rejectedWith('appPromotion.NoPermission'); - - // contract still self-sponsored - expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({ - confirmed: { - ethereum: flipper.options.address.toLowerCase(), - }, - }); - }); - - itEth('should actually sponsor transactions', async ({helper}) => { - // Contract caller - const caller = await helper.eth.createAccountWithBalance(donor, 1000n); - const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress); - - // Deploy flipper - const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); - const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); - const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); - - // Owner sets to sponsor every tx - await contractHelper.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: contractOwner}); - await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner}); - await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address, 1000n); // transferBalanceToEth(api, alice, flipper.options.address, 1000n); - - // Set promotion to the Flipper - await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true); - - // Caller calls Flipper - await flipper.methods.flip().send({from: caller}); - expect(await flipper.methods.getValue().call()).to.be.true; - - // The contracts and caller balances have not changed - const callerBalance = await helper.balance.getEthereum(caller); - const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address); - expect(callerBalance).to.be.equal(1000n * nominal); - expect(1000n * nominal === contractBalanceAfter).to.be.true; - - // The pallet balance has decreased - const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress); - expect(palletBalanceAfter < palletBalanceBefore).to.be.true; - }); - }); - - describe('stopSponsoringContract', () => { - itEth('should remove pallet address from contract sponsors', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor, 1000n); - const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); - const flipper = await helper.eth.deployFlipper(contractOwner); - await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address); - const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); - - await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner}); - await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true); - await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address], true); - - expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.false; - expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner); - expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({ - disabled: null, - }); - - await flipper.methods.flip().send({from: caller}); - expect(await flipper.methods.getValue().call()).to.be.true; - - const callerBalance = await helper.balance.getEthereum(caller); - const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address); - - // caller payed for call - expect(1000n * nominal > callerBalance).to.be.true; - expect(contractBalanceAfter).to.be.equal(100n * nominal); - }); - - itEth('can not be called by non-admin', async ({helper}) => { - const [nonAdmin] = await getAccounts(1); - const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); - const flipper = await helper.eth.deployFlipper(contractOwner); - - await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address]); - await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address])) - .to.be.rejectedWith(/appPromotion\.NoPermission/); - }); - - itEth('should not affect a contract which is not sponsored by pallete', async ({helper}) => { - const [nonAdmin] = await getAccounts(1); - const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); - const flipper = await helper.eth.deployFlipper(contractOwner); - const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); - await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled; - - await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address], true)).to.be.rejectedWith('appPromotion.NoPermission'); - }); - }); - - describe('payoutStakers', () => { - itSub('can not be called by non admin', async ({helper}) => { - const [nonAdmin] = await getAccounts(1); - await expect(helper.admin.payoutStakers(nonAdmin, 100)).to.be.rejectedWith('appPromotion.NoPermission'); - }); - - itSub('should increase total staked', async ({helper}) => { - const [staker] = await getAccounts(1); - const totalStakedBefore = await helper.staking.getTotalStaked(); - await helper.staking.stake(staker, 100n * nominal); - - // Wait for rewards and pay - const [stakedInBlock] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock.block)); - - const payout = await helper.admin.payoutStakers(palletAdmin, 100); - const totalPayout = payout.reduce((prev, payout) => prev + payout.payout, 0n); - const stakerReward = payout.find(p => p.staker === staker.address); - - expect(stakerReward?.payout).to.eq(calculateIncome(100n * nominal) - (100n * nominal)); - - const totalStakedAfter = await helper.staking.getTotalStaked(); - expect(totalStakedAfter).to.equal(totalStakedBefore + (100n * nominal) + totalPayout); - // staker can unstake - await helper.staking.unstakeAll(staker); - expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedAfter - calculateIncome(100n * nominal)); - }); - - itSub('should credit 0.05% for staking period', async ({helper}) => { - const [staker] = await getAccounts(1); - - await waitPromotionPeriodDoesntEnd(helper); - - await helper.staking.stake(staker, 100n * nominal); - await helper.staking.stake(staker, 200n * nominal); - - // wait rewards are available: - const [_stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block)); - - const payoutToStaker = (await helper.admin.payoutStakers(palletAdmin, 100)).find((payout) => payout.staker === staker.address)!.payout; - expect(payoutToStaker + 300n * nominal).to.equal(calculateIncome(300n * nominal)); - - const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - const income1 = calculateIncome(100n * nominal); - const income2 = calculateIncome(200n * nominal); - expect(totalStakedPerBlock[0].amount).to.equal(income1); - expect(totalStakedPerBlock[1].amount).to.equal(income2); - - const stakerBalance = await helper.balance.getSubstrateFull(staker.address); - expect(stakerBalance).to.contain({frozen: income1 + income2, reserved: 0n}); - expect(stakerBalance.free / nominal).to.eq(999n); - }); - - itSub('shoud be paid for more than one period if payments was missed', async ({helper}) => { - const [staker] = await getAccounts(1); - - await helper.staking.stake(staker, 100n * nominal); - // wait for two rewards are available: - let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD); - - await helper.admin.payoutStakers(palletAdmin, 100); - [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - const frozenBalanceShouldBe = calculateIncome(100n * nominal, 2); - expect(stake.amount).to.be.equal(frozenBalanceShouldBe); - - const stakerFullBalance = await helper.balance.getSubstrateFull(staker.address); - - expect(stakerFullBalance).to.contain({reserved: 0n, frozen: frozenBalanceShouldBe}); - }); - - itSub('should not be credited for pending-unstaked tokens', async ({helper}) => { - // staker unstakes before rewards been payed - const [staker] = await getAccounts(1); - await helper.staking.stake(staker, 100n * nominal); - const [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD); - await helper.staking.unstakeAll(staker); - - // so he did not receive any rewards - const totalBalanceBefore = await helper.balance.getSubstrate(staker.address); - await helper.admin.payoutStakers(palletAdmin, 100); - const totalBalanceAfter = await helper.balance.getSubstrate(staker.address); - - expect(totalBalanceBefore).to.be.equal(totalBalanceAfter); - }); - - itSub('should bring compound interest', async ({helper}) => { - const [staker] = await getAccounts(1); - - await helper.staking.stake(staker, 100n * nominal); - - let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block)); - - await helper.admin.payoutStakers(palletAdmin, 100); - [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - expect(stake.amount).to.equal(calculateIncome(100n * nominal)); - - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD); - await helper.admin.payoutStakers(palletAdmin, 100); - [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - expect(stake.amount).to.equal(calculateIncome(100n * nominal, 2)); - }); - - itSub('can calculate reward for tiny stake', async ({helper}) => { - const [staker] = await getAccounts(1); - await helper.staking.stake(staker, 100n * nominal); - await helper.staking.stake(staker, 100n * nominal); - await helper.staking.unstakePartial(staker, 100n * nominal - 1n); - - const [_stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block)); - - const stakerPayout = await payUntilRewardFor(staker.address, helper); - expect(stakerPayout.stake).to.eq(100n * nominal + 1n); - }); - - itSub('can eventually pay all rewards', async ({helper}) => { - const stakers = await getAccounts(30); - // Create 30 stakes: - await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal))); - - let unstakingTxs = []; - for(const staker of stakers) { - if(unstakingTxs.length == 3) { - await Promise.all(unstakingTxs); - unstakingTxs = []; - } - unstakingTxs.push(helper.staking.unstakePartial(staker, 100n * nominal - 1n)); - } - - const [staker] = await getAccounts(1); - await helper.staking.stake(staker, 100n * nominal); - const [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block)); - - let payouts; - do { - payouts = await helper.admin.payoutStakers(palletAdmin, 20); - } while(payouts.length !== 0); - }); - }); - - describe('events', () => { - [ - {method: 'unstakePartial' as const}, - {method: 'unstakeAll' as const}, - ].map(testCase => { - itSub(testCase.method, async ({helper}) => { - const unstakeParams: [] | [bigint] = testCase.method === 'unstakePartial' - ? [100n * nominal - 1n] - : []; - const [staker] = await getAccounts(1); - await helper.staking.stake(staker, 100n * nominal); - await helper.staking.stake(staker, 200n * nominal); - const {result} = await helper.executeExtrinsic(staker, `api.tx.appPromotion.${testCase.method}`, unstakeParams); - - const event = result.events.find(e => e.event.section === 'appPromotion' && e.event.method === 'Unstake'); - const unstakerEvents = event?.event.data[0].toString(); - const unstakedEvents = BigInt(event?.event.data[1].toString()); - expect(unstakerEvents).to.eq(staker.address); - expect(unstakedEvents).to.eq(testCase.method === 'unstakeAll' ? 300n * nominal : 100n * nominal - 1n); - }); - }); - - itSub('stake', async ({helper}) => { - const [staker] = await getAccounts(1); - const {result} = await helper.executeExtrinsic(staker, 'api.tx.appPromotion.stake', [100n * nominal]); - - const event = result.events.find(e => e.event.section === 'appPromotion' && e.event.method === 'Stake'); - const stakerEvents = event?.event.data[0].toString(); - const stakedEvents = BigInt(event?.event.data[1].toString()); - expect(stakerEvents).to.eq(staker.address); - expect(stakedEvents).to.eq(100n * nominal); - }); - - // Flaky - itSub.skip('payoutStakers', async ({helper}) => { - const [staker1, staker2] = await getAccounts(2); - const STAKE1 = 100n * nominal; - const STAKE2 = 200n * nominal; - await helper.staking.stake(staker1, STAKE1); - await helper.staking.stake(staker2, STAKE2); - - const [stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker2.address}); - await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block)); - - const results = await helper.admin.payoutStakers(palletAdmin, 100); - const stakersEvents = results.filter(ev => ev.staker === staker1.address || ev.staker === staker2.address); - expect(stakersEvents).has.length(2); - expect(stakersEvents).has.not.ordered.members([ - {staker: staker1.address, stake: STAKE1, payout: calculateIncome(STAKE1) - STAKE1}, - {staker: staker2.address, stake: STAKE2, payout: calculateIncome(STAKE2) - STAKE2}, - ]); - }); - }); -}); - - -// Sometimes is is required to make a cycle in order for the payment to be calculated for a specific account -async function payUntilRewardFor(account: string, helper: DevUniqueHelper) { - for(let i = 0; i < 3; i++) { - const payouts = await helper.admin.payoutStakers(palletAdmin, 100); - const accountPayout = payouts.find(p => p.staker === account); - if(accountPayout) return accountPayout; - } - throw Error(`Cannot find payout for ${account}`); -} - -function calculateIncome(base: bigint, iter = 0, calcPeriod: bigint = UNLOCKING_PERIOD): bigint { - const DAY = 7200n; - const ACCURACY = 1_000_000_000n; - // 5n / 10_000n = 0.05% p/day - const income = base + base * (ACCURACY * (calcPeriod * 5n) / (10_000n * DAY)) / ACCURACY ; - - if(iter > 1) { - return calculateIncome(income, iter - 1, calcPeriod); - } else return income; -} - -function rewardAvailableInBlock(stakedInBlock: bigint) { - if(stakedInBlock % LOCKING_PERIOD === 0n) return stakedInBlock + LOCKING_PERIOD; - return (stakedInBlock - stakedInBlock % LOCKING_PERIOD) + (LOCKING_PERIOD * 2n); -} - -// Wait while promotion period less than specified block, to avoid boundary cases -// 0 if this should be the beginning of the period. -async function waitPromotionPeriodDoesntEnd(helper: DevUniqueHelper, waitBlockLessThan = LOCKING_PERIOD / 3n) { - const relayBlockNumber = (await helper.callRpc('api.query.parachainSystem.validationData', [])).value.relayParentNumber.toNumber(); // await helper.chain.getLatestBlockNumber(); - const currentPeriodBlock = BigInt(relayBlockNumber) % LOCKING_PERIOD; - - if(currentPeriodBlock > waitBlockLessThan) { - await helper.wait.forRelayBlockNumber(BigInt(relayBlockNumber) + LOCKING_PERIOD - currentPeriodBlock); - } -} --- a/js-packages/tests/src/sub/nesting/admin.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, usingPlaygrounds} from '../../util/index.js'; -import {CrossAccountId} from '@unique/playgrounds/src/unique.js'; - -describe('Nesting by collection admin', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 10n, 10n], donor); - }); - }); - - [ - {restricted: true}, - {restricted: false}, - ].map(testCase => { - itSub(`can nest tokens if "collectionAdmin" permission set ${testCase.restricted ? ', in restricted mode' : ''}`, async ({helper}) => { - const collectionA = await helper.nft.mintCollection(alice); - await collectionA.addAdmin(alice, {Substrate: bob.address}); - const collectionB = await helper.nft.mintCollection(alice); - await collectionB.addAdmin(alice, {Substrate: bob.address}); - // Collection has permission for collectionAdmin to nest: - await collectionA.setPermissions(alice, {nesting: - {collectionAdmin: true, restricted: testCase.restricted ? [collectionA.collectionId, collectionB.collectionId] : null}, - }); - // Token for nesting in from collectionA: - const targetTokenA = await collectionA.mintToken(alice, {Substrate: charlie.address}); - - // 1. Create an immediately nested tokens: - // 1.1 From own collection: - const nestedTokenA = await collectionA.mintToken(bob, targetTokenA.nestingAccount()); - // 1.2 From different collection: - const nestedTokenB = await collectionB.mintToken(bob, targetTokenA.nestingAccount()); - expect(await nestedTokenA.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address}); - expect(await nestedTokenA.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount())); - expect(await nestedTokenB.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address}); - expect(await nestedTokenB.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount())); - - // 2. Create a token to be nested and nest: - const newNestedTokenA = await collectionA.mintToken(bob); - const newNestedTokenB = await collectionB.mintToken(bob); - // 2.1 From own collection: - await newNestedTokenA.nest(bob, targetTokenA); - // 2.2 From different collection: - await newNestedTokenB.nest(bob, targetTokenA); - expect(await newNestedTokenB.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address}); - expect(await newNestedTokenB.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount())); - }); - }); - - itSub('can operate together with token owner if "collectionAdmin" and "tokenOwner" permissions set', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true, tokenOwner: true}}}); - await collection.addAdmin(alice, {Substrate: bob.address}); - const targetToken = await collection.mintToken(alice, {Substrate: charlie.address}); - - // Admin can create an immediately nested token: - const nestedToken = await collection.mintToken(bob, targetToken.nestingAccount()); - expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address}); - expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); - - // Owner can create and and nest: - const newToken = await collection.mintToken(alice, {Substrate: charlie.address}); - await newToken.nest(charlie, targetToken); - expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address}); - expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); - }); -}); --- a/js-packages/tests/src/sub/nesting/common.test.ts +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js'; -import {CrossAccountId, UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/src/unique.js'; - -let alice: IKeyringPair; -let bob: IKeyringPair; - -describe('Common nesting tests', () => { - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - [ - {mode: 'nft' as const, restrictedMode: true, requiredPallets: []}, - {mode: 'nft' as const, restrictedMode: false, requiredPallets: []}, - {mode: 'rft' as const, restrictedMode: true, requiredPallets: [Pallets.ReFungible]}, - {mode: 'rft' as const, restrictedMode: false, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => { - itSub.ifWithPallets(`Token owner can nest ${testCase.mode.toUpperCase()} in NFT if "tokenOwner" permission set ${testCase.restrictedMode ? 'in restricted mode': ''}`, testCase.requiredPallets, async ({helper}) => { - // Only NFT can be target for nesting in - const targetNFTCollection = await helper.nft.mintCollection(alice); - const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address}); - - const collectionForNesting = await helper[testCase.mode].mintCollection(bob); - // permissions should be set: - await targetNFTCollection.setPermissions(alice, { - nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null}, - }); - - // 1. Bob can immediately create nested token: - const nestedToken1 = testCase.mode === 'nft' - ? await (collectionForNesting as UniqueNFTCollection).mintToken(bob, targetTokenBob.nestingAccount()) - : await (collectionForNesting as UniqueRFTCollection).mintToken(bob, 10n, targetTokenBob.nestingAccount()); - expect(await nestedToken1.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); - expect(await nestedToken1.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenBob.nestingAccount())); - - // 2. Bob can mint and nest token: - const nestedToken2 = await collectionForNesting.mintToken(bob); - await nestedToken2.nest(bob, targetTokenBob); - expect(await nestedToken2.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); - expect(await nestedToken2.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenBob.nestingAccount())); - }); - }); - - - [ - {restrictedMode: true}, - {restrictedMode: false}, - ].map(testCase => { - itSub(`Token owner can nest FT in NFT if "tokenOwner" permission set ${testCase.restrictedMode ? 'in restricted mode': ''}`, async ({helper}) => { - // Only NFT allows nesting, permissions should be set: - const targetNFTCollection = await helper.nft.mintCollection(alice); - const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address}); - - const collectionForNesting = await helper.ft.mintCollection(bob); - // permissions should be set: - await targetNFTCollection.setPermissions(alice, { - nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null}, - }); - - // 1. Alice can immediately create nested tokens: - await collectionForNesting.mint(bob, 100n, targetTokenBob.nestingAccount()); - expect(await collectionForNesting.getTop10Owners()).deep.eq([CrossAccountId.toLowerCase(targetTokenBob.nestingAccount())]); - expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(100n); - - // 2. Alice can mint and nest token: - await collectionForNesting.mint(bob, 100n); - await collectionForNesting.transfer(bob, targetTokenBob.nestingAccount(), 50n); - expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(150n); - expect(await targetTokenBob.getChildren()).to.be.deep.equal([{collectionId: collectionForNesting.collectionId, tokenId: 0}]); - }); - }); - - [ - {restrictedMode: true}, - {restrictedMode: false}, - ].map(testCase => { - itSub(`Token owner can nest Native FT in NFT if "tokenOwner" permission set ${testCase.restrictedMode ? 'in restricted mode': ''}`, async ({helper}) => { - // Only NFT allows nesting, permissions should be set: - const targetNFTCollection = await helper.nft.mintCollection(alice); - const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address}); - expect(await targetTokenBob.getChildren()).to.be.empty; - - const collectionForNesting = helper.ft.getCollectionObject(0); - // permissions should be set: - await targetNFTCollection.setPermissions(alice, { - nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null}, - }); - - // Bob can nest Native FT into their NFT: - await collectionForNesting.transfer(bob, targetTokenBob.nestingAccount(), 50n); - expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(50n); - // Native FT should't be visible in NFT children: - expect(await targetTokenBob.getChildren()).to.be.deep.equal([]); - }); - }); - - - itSub.ifWithPallets('Owner can unnest tokens using transferFrom', [Pallets.ReFungible], async ({helper}) => { - const collectionToNest = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const tokenA = await collectionToNest.mintToken(alice); - const tokenB = await collectionToNest.mintToken(alice); - - // Create a nested token - const nftCollectionToBeNested = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const rftCollectionToBeNested = await helper.rft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const ftCollectionToBeNested = await helper.ft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const nativeFtCollectionToBeNested = helper.ft.getCollectionObject(0); - - const nestedNFT = await nftCollectionToBeNested.mintToken(alice, tokenA.nestingAccount()); - const nestedRFT = await rftCollectionToBeNested.mintToken(alice, 100n, tokenA.nestingAccount()); - await ftCollectionToBeNested.mint(alice, 100n, tokenA.nestingAccount()); - await nativeFtCollectionToBeNested.transfer(alice, tokenA.nestingAccount(), 100n); - expect(await nestedNFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenA.nestingAccount())); - expect(await nestedRFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenA.nestingAccount())); - expect(await ftCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(100n); - expect(await nativeFtCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(100n); - - expect(await tokenA.getChildren()).to.be.length(3); - expect(await tokenB.getChildren()).to.be.length(0); - - // Transfer the nested token to another token - await nestedNFT.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount()); - await nestedRFT.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n); - await ftCollectionToBeNested.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n); - await nativeFtCollectionToBeNested.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n); - - expect(await nestedNFT.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); - expect(await nestedNFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenB.nestingAccount())); - - expect(await nestedRFT.getBalance(tokenB.nestingAccount())).to.equal(25n); - expect(await nestedRFT.getBalance(tokenA.nestingAccount())).to.equal(75n); - - expect(await ftCollectionToBeNested.getBalance(tokenB.nestingAccount())).to.equal(25n); - expect(await ftCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(75n); - - expect(await nativeFtCollectionToBeNested.getBalance(tokenB.nestingAccount())).to.equal(25n); - expect(await nativeFtCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(75n); - - // RFT, FT, and without native FT - expect(await tokenA.getChildren()).to.be.length(2); - // NFT, RFT, FT, and without native FT - expect(await tokenB.getChildren()).to.be.length(3); - }); -}); --- a/js-packages/tests/src/sub/nesting/e2e.test.ts +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, usingPlaygrounds} from '../../util/index.js'; -import {CrossAccountId} from '@unique/playgrounds/src/unique.js'; - -describe('Composite nesting tests', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); - }); - }); - - itSub('Checks token children e2e', async ({helper}) => { - const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const collectionB = await helper.ft.mintCollection(alice); - const collectionC = await helper.rft.mintCollection(alice); - const collectionNative = helper.ft.getCollectionObject(0); - - const targetToken = await collectionA.mintToken(alice); - expect((await targetToken.getChildren()).length).to.be.equal(0, 'Children length check at creation'); - - // Create a nested NFT token - const tokenA = await collectionA.mintToken(alice, targetToken.nestingAccount()); - expect(await targetToken.getChildren()).to.have.deep.members([ - {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, - ]).and.has.length(1); - - // Create then nest - const tokenB = await collectionA.mintToken(alice); - await tokenB.nest(alice, targetToken); - expect(await targetToken.getChildren()).to.have.deep.members([ - {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, - {tokenId: tokenB.tokenId, collectionId: collectionA.collectionId}, - ]).and.has.length(2); - - // Move token B to a different user outside the nesting tree - await tokenB.unnest(alice, targetToken, {Substrate: bob.address}); - expect(await targetToken.getChildren()).to.have.deep.members([ - {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, - ]).and.has.length(1); - - // Create a fungible token in another collection and then nest - await collectionB.mint(alice, 10n); - await collectionB.transfer(alice, targetToken.nestingAccount(), 2n); - expect(await targetToken.getChildren()).to.have.deep.members([ - {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, - {tokenId: 0, collectionId: collectionB.collectionId}, - ]).and.has.length(2); - - // Create a refungible token in another collection and then nest - const tokenC = await collectionC.mintToken(alice, 10n); - await tokenC.transfer(alice, targetToken.nestingAccount(), 2n); - expect(await targetToken.getChildren()).to.have.deep.members([ - {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, - {tokenId: 0, collectionId: collectionB.collectionId}, - {tokenId: tokenC.tokenId, collectionId: collectionC.collectionId}, - ]).and.has.length(3); - - // Nest native fungible token into another collection - await collectionNative.transfer(alice, targetToken.nestingAccount(), 2n); - expect(await targetToken.getChildren()).to.have.deep.members([ - {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, - {tokenId: 0, collectionId: collectionB.collectionId}, - {tokenId: tokenC.tokenId, collectionId: collectionC.collectionId}, - ]).and.has.length(3); - - // Burn all nested pieces - await tokenC.burnFrom(alice, targetToken.nestingAccount(), 2n); - expect(await targetToken.getChildren()).to.have.deep.members([ - {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, - {tokenId: 0, collectionId: collectionB.collectionId}, - ]) - .and.has.length(2); - - // Move part of the fungible token inside token A deeper in the nesting tree - await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n); - expect(await targetToken.getChildren()).to.be.have.deep.members([ - {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, - {tokenId: 0, collectionId: collectionB.collectionId}, - ]).and.has.length(2); - // Nested token also has children now: - expect(await tokenA.getChildren()).to.have.deep.members([ - {tokenId: 0, collectionId: collectionB.collectionId}, - ]).and.has.length(1); - - // Move the remaining part of the fungible token inside token A deeper in the nesting tree - await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n); - expect(await targetToken.getChildren()).to.have.deep.members([ - {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, - ]).and.has.length(1); - expect(await tokenA.getChildren()).to.have.deep.members([ - {tokenId: 0, collectionId: collectionB.collectionId}, - ]).and.has.length(1); - }); - - /// TODO review this test - itSub('Performs the full suite: bundles a token, transfers, and unnests', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const targetToken = await collection.mintToken(alice); - - // Create an immediately nested token - const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount()); - expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); - expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); - - // Create a token to be nested - const newToken = await collection.mintToken(alice); - - // Nest - await newToken.nest(alice, targetToken); - expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); - expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); - - // Move bundle to different user - await targetToken.transfer(alice, {Substrate: bob.address}); - expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); - expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); - expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); - expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); - - // Unnest - await newToken.unnest(bob, targetToken, {Substrate: bob.address}); - expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); - expect(await newToken.getOwner()).to.be.deep.equal({Substrate: bob.address}); - }); -}); --- a/js-packages/tests/src/sub/nesting/nesting.negative.test.ts +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js'; -import {UniqueFTCollection, UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection, UniqueRFToken} from '@unique/playgrounds/src/unique.js'; -import {itEth} from '../../eth/util/index.js'; - -let alice: IKeyringPair; -let bob: IKeyringPair; -let charlie: IKeyringPair; - -describe('Negative Test: Nesting', () => { - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => { - itSub.ifWithPallets(`Owner cannot nest ${testCase.mode.toUpperCase()} if nesting is disabled`, testCase.requiredPallets, async ({helper}) => { - // Create default collection, permissions are not set: - const aliceNFTCollection = await helper.nft.mintCollection(alice); - const targetToken = await aliceNFTCollection.mintToken(alice); - - const collectionForNesting = await helper[testCase.mode].mintCollection(alice); - - // 1. Alice cannot create immediately nested tokens: - const nestingTx = testCase.mode === 'nft' - ? (collectionForNesting as UniqueNFTCollection).mintToken(alice, targetToken.nestingAccount()) - : (collectionForNesting as UniqueRFTCollection).mintToken(alice, 10n, targetToken.nestingAccount()); - await expect(nestingTx).to.be.rejectedWith('common.UserIsNotAllowedToNest'); - - // 2. Alice cannot mint and nest token: - const nestedToken2 = await collectionForNesting.mintToken(alice); - await expect(nestedToken2.nest(alice, targetToken)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); - }); - }); - - [ - {mode: 'ft'}, - {mode: 'nativeFt'}, - ].map(testCase => { - itSub(`Owner cannot nest [${testCase.mode}] if nesting is disabled (except for native fungible collection)`, async ({helper}) => { - // Create default collection, permissions are not set: - const aliceNFTCollection = await helper.nft.mintCollection(alice); - const targetToken = await aliceNFTCollection.mintToken(alice); - - const collectionForNesting = testCase.mode === 'ft' ? await helper.ft.mintCollection(alice) : helper.ft.getCollectionObject(0); - - // Alice cannot create immediately nested tokens: - if(testCase.mode === 'ft') { - await expect(collectionForNesting.mint(alice, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); - } else { - await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 100n)).to.be.not.rejected; - } - - // Alice can't mint and nest tokens: - if(testCase.mode === 'ft') { - await collectionForNesting.mint(alice, 100n); - } - - if(testCase.mode === 'ft') { - await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); - } else { - await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n)).to.be.not.rejected; - } - }); - }); - - [ - {mode: 'nft' as const}, - {mode: 'rft' as const}, - {mode: 'ft' as const}, - {mode: 'native ft' as const}, - ].map(testCase => { - itSub(`Non-owner and non-admin cannot nest ${testCase.mode.toUpperCase()} in someone else's tokens (except for native fungible collection)`, async ({helper}) => { - const targetCollection = await helper.nft.mintCollection(alice, {permissions: - {nesting: {tokenOwner: true, collectionAdmin: true}}, - }); - const targetToken = await targetCollection.mintToken(alice); - - const nestedCollectionBob = await ( - testCase.mode === 'native ft' - ? helper.ft.getCollectionObject(0) - : helper[testCase.mode].mintCollection(bob) - ); - - let nestedTokenBob: UniqueNFToken | UniqueRFToken; - switch (testCase.mode) { - case 'nft': nestedTokenBob = await (nestedCollectionBob as UniqueNFTCollection).mintToken(bob); break; - case 'rft': nestedTokenBob = await (nestedCollectionBob as UniqueRFTCollection).mintToken(bob, 100n); break; - case 'ft': await (nestedCollectionBob as UniqueFTCollection).mint(bob, 100n); break; - case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n)).to.be.rejectedWith('common.UnsupportedOperation'); break; - } - - // Bob non-owner of targetToken and non admin of targetCollection, so - // 1. cannot mint nested token: - switch (testCase.mode) { - case 'nft': await expect((nestedCollectionBob as UniqueNFTCollection).mintToken(bob, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break; - case 'rft': await expect((nestedCollectionBob as UniqueRFTCollection).mintToken(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break; - case 'ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break; - case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UnsupportedOperation'); break; - } - - // 2. cannot nest existing token: - switch (testCase.mode) { - case 'nft': - case 'rft': await expect(nestedTokenBob!.transfer(bob, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break; - case 'ft': await expect((nestedCollectionBob as UniqueFTCollection).transfer(bob, targetToken.nestingAccount(), 100n)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break; - case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).transfer(bob, targetToken.nestingAccount(), 100n)).to.be.not.rejected; break; - } - }); - }); - - [ - {mode: 'nft' as const, nesting: {tokenOwner: true, collectionAdmin: false}}, - {mode: 'nft' as const, nesting: {tokenOwner: false, collectionAdmin: true}}, - ].map(testCase => { - itSub(`${testCase.nesting.tokenOwner ? 'Admin' : 'Token owner'} cannot nest when only ${testCase.nesting.tokenOwner ? 'tokenOwner' : 'collectionAdmin'} is allowed`, async ({helper}) => { - // Create collection with tokenOwner or create collection with collectionAdmin permission: - const targetCollection = await helper.nft.mintCollection(alice, {permissions: {nesting: testCase.nesting}}); - const targetTokenCharlie = await targetCollection.mintToken(alice, {Substrate: charlie.address}); - await targetCollection.addAdmin(alice, {Substrate: bob.address}); - - const nestedCollectionCharlie = await helper[testCase.mode].mintCollection(charlie); - const nestedCollectionBob = await helper[testCase.mode].mintCollection(bob); - // if nesting permissions restricted for token owner – minter is bob (admin), - // if collectionAdmin – charlie (owner) - testCase.nesting.tokenOwner - ? await expect(nestedCollectionBob.mintToken(bob, targetTokenCharlie.nestingAccount())).to.be.rejectedWith(/common\.UserIsNotAllowedToNest/) - : await expect(nestedCollectionCharlie.mintToken(charlie, targetTokenCharlie.nestingAccount())).to.be.rejectedWith(/common\.UserIsNotAllowedToNest/); - }); - }); - - itSub.ifWithPallets('Cannot nest in non existing token (except for native fungible collection)', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.nft.mintCollection(alice); - // To avoid UserIsNotAllowedToNest error - await helper.collection.setPermissions(alice, collection.collectionId, {nesting: {collectionAdmin: true}}); - - // The list of non-existing tokens: - const tokenFromNonExistingCollection = helper.nft.getTokenObject(9999999, 1); - const tokenBurnt = await collection.mintToken(alice); - await tokenBurnt.burn(alice); - const tokenNotMintedYet = helper.nft.getTokenObject(collection.collectionId, 2); - - // The list of collections to nest tokens from: - const nftCollectionForNesting = await helper.nft.mintCollection(alice); - const rftCollectionForNesting = await helper.rft.mintCollection(alice); - const ftCollectionForNesting = await helper.ft.mintCollection(alice); - const nativeFtCollectionForNesting = helper.ft.getCollectionObject(0); - - const testCases = [ - {token: tokenFromNonExistingCollection, error: 'CollectionNotFound'}, - {token: tokenBurnt, error: 'TokenNotFound'}, - {token: tokenNotMintedYet, error: 'TokenNotFound'}, - ]; - - for(const testCase of testCases) { - // 1. Alice cannot create nested token to non-existing token - await expect(nftCollectionForNesting.mintToken(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); - await expect(rftCollectionForNesting.mintToken(alice, 10n, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); - await expect(ftCollectionForNesting.mint(alice, 10n, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); - - // 2. Alice cannot mint and nest token: - const nft = await nftCollectionForNesting.mintToken(alice); - const rft = await rftCollectionForNesting.mintToken(alice, 100n); - await ftCollectionForNesting.mint(alice, 100n); - await expect(nft.transfer(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); - await expect(rft.transfer(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); - await expect(ftCollectionForNesting.transfer(alice, testCase.token.nestingAccount(), 50n)).to.be.rejectedWith(testCase.error); - await expect(nativeFtCollectionForNesting.transfer(alice, testCase.token.nestingAccount(), 50n)).to.be.not.rejected; - } - }); - - itEth.ifWithPallets('Cannot nest in collection address', [Pallets.ReFungible], async({helper}) => { - const existingCollection = await helper.nft.mintCollection(alice); - const existingCollectionAddress = helper.ethAddress.fromCollectionId(existingCollection.collectionId); - const futureCollectionAddress = helper.ethAddress.fromCollectionId(99999999); - - const nftCollectionForNesting = await helper.nft.mintCollection(alice); - - // 1. Alice cannot create nested token to collection address - await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); - await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); - - // 2. Alice cannot mint and nest token to collection address: - const nft = await nftCollectionForNesting.mintToken(alice); - await expect(nft.transfer(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); - await expect(nft.transfer(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); - }); - - itEth.ifWithPallets('Cannot nest in RFT or FT (except for native fungible collection)', [Pallets.ReFungible], async ({helper}) => { - // Create default collection, permissions are not set: - const rftCollection = await helper.rft.mintCollection(alice); - const ftCollection = await helper.ft.mintCollection(alice); - const nativeFtCollection = helper.ft.getCollectionObject(0); - - const rftToken = await rftCollection.mintToken(alice); - await ftCollection.mint(alice, 100n); - - const collectionForNesting = await helper.nft.mintCollection(alice); - - // 1. Alice cannot create immediately nested tokens: - await expect(collectionForNesting.mintToken(alice, rftToken.nestingAccount())).to.be.rejectedWith('refungible.RefungibleDisallowsNesting'); - await expect(collectionForNesting.mintToken(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting'); - await expect(collectionForNesting.mintToken(alice, {Ethereum: helper.ethAddress.fromTokenId(nativeFtCollection.collectionId, 0)})).to.be.rejectedWith('common.UnsupportedOperation'); - - // 2. Alice cannot mint and nest token: - const nestedToken2 = await collectionForNesting.mintToken(alice); - await expect(nestedToken2.nest(alice, rftToken)).to.be.rejectedWith('refungible.RefungibleDisallowsNesting'); - await expect(ftCollection.transfer(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting'); - await expect(nativeFtCollection.transfer(alice, {Ethereum: helper.ethAddress.fromTokenId(nativeFtCollection.collectionId, 0)})).to.be.not.rejected; - }); - - itSub('Cannot nest in restricted collection if collection is not in the list (except native fungible collection)', async ({helper}) => { - const {collectionId: allowedCollectionId} = await helper.nft.mintCollection(alice); - const notAllowedCollectionNFT = await helper.nft.mintCollection(alice); - const notAllowedCollectionRFT = await helper.rft.mintCollection(alice); - const notAllowedCollectionFT = await helper.ft.mintCollection(alice); - const allowedCollectionNativeFT = helper.ft.getCollectionObject(0); - - // Collection restricted to allowedCollectionId - const restrictedCollectionA = await helper.nft.mintCollection(alice, {permissions: - {nesting: {tokenOwner: true, restricted: [allowedCollectionId]}}, - }); - // Create collection with restricted nesting -- even self is not allowed - const restrictedCollectionB = await helper.nft.mintCollection(alice, {permissions: - {nesting: {tokenOwner: true, restricted: []}}, - }); - - const targetTokenA = await restrictedCollectionA.mintToken(alice); - const targetTokenB = await restrictedCollectionB.mintToken(alice); - - // 1. Cannot mint in own collection after allowlisting the accounts: - await expect(restrictedCollectionA.mintToken(alice, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); - await expect(restrictedCollectionB.mintToken(alice, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); - - // 2. Cannot mint from notAllowedCollection: - await expect(notAllowedCollectionNFT.mintToken(alice, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); - await expect(notAllowedCollectionNFT.mintToken(alice, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); - await expect(notAllowedCollectionRFT.mintToken(alice, 100n, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); - await expect(notAllowedCollectionRFT.mintToken(alice, 100n, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); - await expect(notAllowedCollectionFT.mint(alice, 100n, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); - await expect(notAllowedCollectionFT.mint(alice, 100n, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); - await expect(allowedCollectionNativeFT.transfer(alice, targetTokenA.nestingAccount(), 100n)).to.be.not.rejected; - await expect(allowedCollectionNativeFT.transfer(alice, targetTokenB.nestingAccount(), 100n)).to.be.not.rejected; - }); - - itSub('Cannot create nesting chains greater than 5', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - let token = await collection.mintToken(alice); - - const maxNestingLevel = 5; - - // Create a nested-token matryoshka - for(let i = 0; i < maxNestingLevel; i++) { - token = await collection.mintToken(alice, token.nestingAccount()); - } - - // The nesting depth is limited by `maxNestingLevel` - // 1. Cannot mint: - await expect(collection.mintToken(alice, token.nestingAccount())) - .to.be.rejectedWith(/structure\.DepthLimit/); - // 2. Cannot transfer: - const anotherToken = await collection.mintToken(alice); - await expect(anotherToken.transfer(alice, token.nestingAccount())) - .to.be.rejectedWith(/structure\.DepthLimit/); - // 3. Cannot nest FT pieces: - const ftCollection = await helper.ft.mintCollection(alice); - await expect(ftCollection.mint(alice, 100n, token.nestingAccount())) - .to.be.rejectedWith(/structure\.DepthLimit/); - - expect(await token.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); - expect(await token.getChildren()).to.has.length(0); - }); -}); --- a/js-packages/tests/src/sub/nesting/refungible.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js'; - -describe('ReFungible-specific nesting tests', () => { - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice] = await helper.arrange.createAccounts([200n], donor); - }); - }); - - itSub.ifWithPallets('ReFungible: getTopmostOwner works correctly with Nesting', [Pallets.ReFungible], async({helper}) => { - const collectionNFT = await helper.nft.mintCollection(alice, { - permissions: { - nesting: { - tokenOwner: true, - }, - }, - }); - const collectionRFT = await helper.rft.mintCollection(alice); - - const nft = await collectionNFT.mintToken(alice, {Substrate: alice.address}); - const rft = await collectionRFT.mintToken(alice, 100n, {Substrate: alice.address}); - - expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address}); - - await rft.transfer(alice, nft.nestingAccount(), 40n); - - expect(await rft.getTopmostOwner()).deep.equal(null); - - await rft.transfer(alice, nft.nestingAccount(), 60n); - - expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address}); - - await rft.transferFrom(alice, nft.nestingAccount(), {Substrate: alice.address}, 30n); - - expect(await rft.getTopmostOwner()).deep.equal(null); - - await rft.transferFrom(alice, nft.nestingAccount(), {Substrate: alice.address}, 70n); - - expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address}); - }); -}); --- a/js-packages/tests/src/sub/nesting/unnesting.negative.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, usingPlaygrounds} from '../../util/index.js'; -import {CrossAccountId} from '@unique/playgrounds/src/unique.js'; - -describe('Negative Test: Unnesting', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 50n], donor); - }); - }); - - // TODO: make this test a bit more generic - itSub('Admin (NFT): disallows an Admin to unnest someone else\'s token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {limits: {ownerCanTransfer: true}, permissions: {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true}}}); - //await collection.addAdmin(alice, {Substrate: bob.address}); - const targetToken = await collection.mintToken(alice, {Substrate: bob.address}); - await collection.addToAllowList(alice, {Substrate: bob.address}); - await collection.addToAllowList(alice, targetToken.nestingAccount()); - - // Try to nest somebody else's token - const newToken = await collection.mintToken(bob); - await expect(newToken.nest(alice, targetToken)) - .to.be.rejectedWith(/common\.NoPermission/); - - // Try to unnest a token belonging to someone else as collection admin - const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount()); - await expect(nestedToken.unnest(alice, targetToken, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.AddressNotInAllowlist/); - - expect(await targetToken.getChildren()).to.be.length(1); - expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); - expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); - }); - - [ - {mode: 'ft' as const}, - {mode: 'native ft' as const}, - ].map(md => [ - {mode: md.mode, restrictedMode: true}, - {mode: md.mode, restrictedMode: false}, - ].map(testCase => { - itSub(`Fungible: disallows a non-Owner to unnest someone else's token [${testCase.mode}${testCase.restrictedMode ? ' (Restricted nesting)' : ''}]`, async ({helper}) => { - const collectionNFT = await helper.nft.mintCollection(alice); - const collectionFT = await ( - testCase.mode === 'ft' - ? helper.ft.mintCollection(alice) - : helper.ft.getCollectionObject(0) - ); - const targetToken = await collectionNFT.mintToken(alice, {Substrate: bob.address}); - - await collectionNFT.setPermissions(alice, {nesting: { - collectionAdmin: true, tokenOwner: true, restricted: testCase.restrictedMode ? [collectionFT.collectionId] : null, - }}); - - // Nest some tokens as Alice into Bob's token - await ( - testCase.mode === 'ft' - ? collectionFT.mint(alice, 5n, targetToken.nestingAccount()) - : collectionFT.transfer(alice, targetToken.nestingAccount(), 5n) - ); - - // Try to pull it out as Alice still - await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: bob.address}, 1n)) - .to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(5n); - }); - })); -}); --- a/js-packages/tests/src/sub/refungible/burn.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js'; - -describe('Refungible: burn', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); - }); - }); - - itSub('can burn some pieces', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, 100n); - expect(await collection.doesTokenExist(token.tokenId)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); - await token.burn(alice, 99n); - expect(await collection.doesTokenExist(token.tokenId)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(1n); - }); - - itSub('can burn all pieces', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, 100n); - - expect(await collection.doesTokenExist(token.tokenId)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); - - await token.burn(alice, 100n); - expect(await collection.doesTokenExist(token.tokenId)).to.be.false; - }); - - itSub('burn pieces for multiple users', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, 100n); - - await token.transfer(alice, {Substrate: bob.address}, 60n); - - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n); - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n); - - await token.burn(alice, 40n); - - expect(await collection.doesTokenExist(token.tokenId)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n); - - await token.burn(bob, 59n); - - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(1n); - expect(await collection.doesTokenExist(token.tokenId)).to.be.true; - - await token.burn(bob, 1n); - - expect(await collection.doesTokenExist(token.tokenId)).to.be.false; - }); - - itSub('burn pieces by admin', async function({helper}) { - const collection = await helper.rft.mintCollection(alice); - await collection.setLimits(alice, {ownerCanTransfer: true}); - await collection.addAdmin(alice, {Substrate: bob.address}); - const token = await collection.mintToken(alice, 100n); - - await token.burnFrom(bob, {Substrate: alice.address}, 100n); - expect(await token.doesExist()).to.be.false; - }); -}); - -describe('Refungible: burn negative tests', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); - }); - }); - - itSub('cannot burn non-owned token pieces', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice); - const aliceToken = await collection.mintToken(alice, 10n, {Substrate: alice.address}); - const bobToken = await collection.mintToken(alice, 10n, {Substrate: bob.address}); - - // 1. Cannot burn non-owned token: - await expect(bobToken.burn(alice, 0n)).to.be.rejectedWith('common.TokenValueTooLow'); - await expect(bobToken.burn(alice, 5n)).to.be.rejectedWith('common.TokenValueTooLow'); - // 2. Cannot burn non-existing token: - await expect(helper.rft.burnToken(alice, 99999, 10)).to.be.rejectedWith('common.CollectionNotFound'); - await expect(helper.rft.burnToken(alice, collection.collectionId, 99999)).to.be.rejectedWith('common.TokenValueTooLow'); - // 3. Can burn zero amount of owned tokens (EIP-20) - await aliceToken.burn(alice, 0n); - - // 4. Storage is not corrupted: - expect(await aliceToken.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]); - expect(await bobToken.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]); - - // 4.1 Tokens can be transfered: - await aliceToken.transfer(alice, {Substrate: bob.address}, 10n); - await bobToken.transfer(bob, {Substrate: alice.address}, 10n); - expect(await aliceToken.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]); - expect(await bobToken.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]); - }); -}); --- a/js-packages/tests/src/sub/refungible/nesting.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../../util/index.js'; - -describe('Refungible nesting', () => { - let alice: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - const donor = await privateKey({url: import.meta.url}); - [alice, charlie] = await helper.arrange.createAccounts([50n, 10n], donor); - }); - }); - - [ - {restrictedMode: true}, - {restrictedMode: false}, - ].map(testCase => { - itSub(`Owner can nest their token${testCase.restrictedMode ? ': Restricted mode' : ''}`, async ({helper}) => { - const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}}}); - const collectionRFT = await helper.rft.mintCollection(alice); - const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address}); - - await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionRFT.collectionId] : null}}); - await collectionNFT.addToAllowList(alice, {Substrate: charlie.address}); - await collectionNFT.addToAllowList(alice, targetToken.nestingAccount()); - - await collectionRFT.setPermissions(alice, {access: 'AllowList', mintMode: true}); - await collectionRFT.addToAllowList(alice, {Substrate: charlie.address}); - await collectionRFT.addToAllowList(alice, targetToken.nestingAccount()); - - // Create an immediately nested token - const nestedToken = await collectionRFT.mintToken(charlie, 5n, targetToken.nestingAccount()); - expect(await nestedToken.getBalance(targetToken.nestingAccount())).to.be.equal(5n); - - // Create a token to be nested and nest - const newToken = await collectionRFT.mintToken(charlie, 5n); - await newToken.transfer(charlie, targetToken.nestingAccount(), 2n); - expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(2n); - expect(await newToken.getBalance({Substrate: charlie.address})).to.be.equal(3n); - }); - }); - - itSub('Owner can unnest nested token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const targetToken = await collection.mintToken(alice); - - // Owner mints nested RFT token: - const collectionRFT = await helper.rft.mintCollection(alice); - const token = await collectionRFT.mintToken(alice, 10n, targetToken.nestingAccount()); - - // 1.1 Owner can partially unnest token pieces with transferFrom: - await token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n); - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(9n); - expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(1n); - - // 1.2 Owner can unnest all pieces: - await token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 1n); - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(10n); - expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n); - }); - - itSub('Owner can burn nested token pieces', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const targetToken = await collection.mintToken(alice); - - // Owner mints nested RFT token: - const collectionRFT = await helper.rft.mintCollection(alice); - const token = await collectionRFT.mintToken(alice, 100n, {Substrate: alice.address}); - await token.transfer(alice, targetToken.nestingAccount(), 30n); - - // 1.1 Owner can partially burnFrom nested pieces: - await token.burnFrom(alice, targetToken.nestingAccount(), 10n); - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(70n); - expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(20n); - expect(await targetToken.getChildren()).to.has.length(1); - - // 1.1 Owner can burnFrom all nested pieces: - await token.burnFrom(alice, targetToken.nestingAccount(), 20n); - expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n); - expect(await targetToken.getChildren()).to.has.length(0); - expect(await token.doesExist()).to.be.true; - - // 2. Target token does not contain any pieces and can be burnt: - await targetToken.burn(alice); - expect(await targetToken.doesExist()).to.be.false; - }); -}); - -describe('Refungible nesting negative tests', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 50n], donor); - }); - }); - - [ - {restrictedMode: true}, - {restrictedMode: false}, - ].map(testCase => { - itSub(`non-Owner cannot nest someone else's token${testCase.restrictedMode ? ': Restricted mode' : ''}`, async ({helper}) => { - const collectionNFT = await helper.nft.mintCollection(alice); - const collectionRFT = await helper.rft.mintCollection(alice); - const targetToken = await collectionNFT.mintToken(alice); - - await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionRFT.collectionId] : null}}); - await collectionNFT.addToAllowList(alice, {Substrate: bob.address}); - await collectionNFT.addToAllowList(alice, targetToken.nestingAccount()); - - // Try to create a token to be nested and nest - const newToken = await collectionRFT.mintToken(alice); - await expect(newToken.transfer(bob, targetToken.nestingAccount())).to.be.rejectedWith(/common\.TokenValueTooLow/); - - expect(await targetToken.getChildren()).to.be.length(0); - expect(await newToken.getBalance({Substrate: alice.address})).to.be.equal(1n); - - // Nest some tokens as Alice into Bob's token - await newToken.transfer(alice, targetToken.nestingAccount()); - - // Try to pull it out - await expect(newToken.transferFrom(bob, targetToken.nestingAccount(), {Substrate: alice.address}, 1n)) - .to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(1n); - }); - }); -}); --- a/js-packages/tests/src/sub/refungible/repartition.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js'; - -describe('integration test: Refungible functionality:', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); - }); - }); - - itSub('Repartition', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, 100n); - - expect(await token.repartition(alice, 200n)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(200n); - expect(await token.getTotalPieces()).to.be.equal(200n); - - expect(await token.transfer(alice, {Substrate: bob.address}, 110n)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(90n); - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(110n); - - await expect(token.repartition(alice, 80n)) - .to.eventually.be.rejectedWith(/refungible\.RepartitionWhileNotOwningAllPieces/); - - expect(await token.transfer(alice, {Substrate: bob.address}, 90n)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n); - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(200n); - - expect(await token.repartition(bob, 150n)).to.be.true; - await expect(token.transfer(bob, {Substrate: alice.address}, 160n)) - .to.eventually.be.rejectedWith(/common\.TokenValueTooLow/); - }); - - itSub('Repartition with increased amount', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, 100n); - await token.repartition(alice, 200n); - const chainEvents = helper.chainLog.slice(-1)[0].events; - const event = chainEvents.find((event: any) => event.section === 'common' && event.method === 'ItemCreated'); - expect(event).to.deep.include({ - section: 'common', - method: 'ItemCreated', - index: [66, 2], - data: [ - collection.collectionId, - token.tokenId, - {substrate: alice.address}, - 100n, - ], - }); - }); - - itSub('Repartition with decreased amount', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, 100n); - await token.repartition(alice, 50n); - const chainEvents = helper.chainLog.slice(-1)[0].events; - const event = chainEvents.find((event: any) => event.section === 'common' && event.method === 'ItemDestroyed'); - expect(event).to.deep.include({ - section: 'common', - method: 'ItemDestroyed', - index: [66, 3], - data: [ - collection.collectionId, - token.tokenId, - {substrate: alice.address}, - 50n, - ], - }); - }); -}); - --- a/js-packages/tests/src/sub/refungible/transfer.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js'; - -describe('Refungible transfer tests', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async function() { - await usingPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - - donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); - }); - }); - - itSub('Can transfer token pieces', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, 100n); - - expect(await token.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true; - // 1. Can transfer less or equal than have: - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n); - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n); - }); - - itSub('Cannot transfer incorrect amount of token pieces', async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const tokenAlice = await collection.mintToken(alice, 10n, {Substrate: alice.address}); - const tokenBob = await collection.mintToken(alice, 10n, {Substrate: bob.address}); - - // 1. Alice cannot transfer Bob's token: - await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.TokenValueTooLow'); - await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow'); - await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 10n)).to.be.rejectedWith('common.TokenValueTooLow'); - await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 100n)).to.be.rejectedWith('common.TokenValueTooLow'); - - // 2. Alice cannot transfer non-existing token: - await expect(collection.transferToken(alice, 100, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.TokenValueTooLow'); - await expect(collection.transferToken(alice, 100, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow'); - - // 3. Cannot transfer more than have: - await expect(tokenAlice.transfer(alice, {Substrate: bob.address}, 11n)) - .to.eventually.be.rejectedWith(/common\.TokenValueTooLow/); - - // 4. Zero transfer allowed (EIP-20): - await tokenAlice.transfer(alice, {Substrate: charlie.address}, 0n); - - expect(await tokenAlice.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]); - expect(await tokenBob.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]); - expect(await tokenAlice.getBalance({Substrate: alice.address})).to.eq(10n); - expect(await tokenBob.getBalance({Substrate: bob.address})).to.eq(10n); - expect(await tokenBob.getBalance({Substrate: charlie.address})).to.eq(0n); - }); -}); --- a/js-packages/tests/src/transfer.test.ts +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itEth, usingEthPlaygrounds} from './eth/util/index.js'; -import {itSub, Pallets, usingPlaygrounds, expect} from './util/index.js'; -import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js'; - -describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => { - let donor: IKeyringPair; - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); - }); - }); - - itSub('Balance transfers and check balance', async ({helper}) => { - const alicesBalanceBefore = await helper.balance.getSubstrate(alice.address); - const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address); - - expect(await helper.balance.transferToSubstrate(alice, bob.address, 1n)).to.be.true; - - const alicesBalanceAfter = await helper.balance.getSubstrate(alice.address); - const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address); - - expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true; - expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true; - }); - - itSub('Inability to pay fees error message is correct', async ({helper}) => { - const [zero] = await helper.arrange.createAccounts([0n], donor); - - // console.error = () => {}; - // The following operation throws an error into the console and the logs. Pay it no heed as long as the test succeeds. - await expect(helper.balance.transferToSubstrate(zero, donor.address, 1n)) - .to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low'); - }); - - itSub('[nft] User can transfer owned token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-1-NFT', description: '', tokenPrefix: 'T'}); - const nft = await collection.mintToken(alice); - - await nft.transfer(alice, {Substrate: bob.address}); - expect(await nft.getOwner()).to.be.deep.equal({Substrate: bob.address}); - }); - - itSub('[fungible] User can transfer owned token', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-1-FT', description: '', tokenPrefix: 'T'}); - await collection.mint(alice, 10n); - - await collection.transfer(alice, {Substrate: bob.address}, 9n); - expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(9n); - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n); - }); - - itSub.ifWithPallets('[refungible] User can transfer owned token', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-1-RFT', description: '', tokenPrefix: 'T'}); - const rft = await collection.mintToken(alice, 10n); - - await rft.transfer(alice, {Substrate: bob.address}, 9n); - expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(9n); - expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(1n); - }); - - itSub('[nft] Collection admin can transfer owned token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-2-NFT', description: '', tokenPrefix: 'T'}); - await collection.addAdmin(alice, {Substrate: bob.address}); - - const nft = await collection.mintToken(bob, {Substrate: bob.address}); - await nft.transfer(bob, {Substrate: alice.address}); - - expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); - }); - - itSub('[fungible] Collection admin can transfer owned token', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-2-FT', description: '', tokenPrefix: 'T'}); - await collection.addAdmin(alice, {Substrate: bob.address}); - - await collection.mint(bob, 10n, {Substrate: bob.address}); - await collection.transfer(bob, {Substrate: alice.address}, 1n); - - expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(9n); - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n); - }); - - itSub.ifWithPallets('[refungible] Collection admin can transfer owned token', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-2-RFT', description: '', tokenPrefix: 'T'}); - await collection.addAdmin(alice, {Substrate: bob.address}); - - const rft = await collection.mintToken(bob, 10n, {Substrate: bob.address}); - await rft.transfer(bob, {Substrate: alice.address}, 1n); - - expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(9n); - expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(1n); - }); -}); - -describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); - }); - }); - - - itSub('[nft] Transfer with not existed collection_id', async ({helper}) => { - await expect(helper.nft.transferToken(alice, NON_EXISTENT_COLLECTION_ID, 1, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('[fungible] Transfer with not existed collection_id', async ({helper}) => { - await expect(helper.ft.transfer(alice, NON_EXISTENT_COLLECTION_ID, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub.ifWithPallets('[refungible] Transfer with not existed collection_id', [Pallets.ReFungible], async ({helper}) => { - await expect(helper.rft.transferToken(alice, NON_EXISTENT_COLLECTION_ID, 1, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('[nft] Transfer with deleted collection_id', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-1-NFT', description: '', tokenPrefix: 'T'}); - const nft = await collection.mintToken(alice); - - await nft.burn(alice); - await collection.burn(alice); - - await expect(nft.transfer(alice, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('[fungible] Transfer with deleted collection_id', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-1-FT', description: '', tokenPrefix: 'T'}); - await collection.mint(alice, 10n); - - await collection.burnTokens(alice, 10n); - await collection.burn(alice); - - await expect(collection.transfer(alice, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub.ifWithPallets('[refungible] Transfer with deleted collection_id', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-1-RFT', description: '', tokenPrefix: 'T'}); - const rft = await collection.mintToken(alice, 10n); - - await rft.burn(alice, 10n); - await collection.burn(alice); - - await expect(rft.transfer(alice, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - itSub('[nft] Transfer with not existed item_id', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-2-NFT', description: '', tokenPrefix: 'T'}); - await expect(collection.transferToken(alice, 1, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.TokenNotFound/); - }); - - itSub('[fungible] Transfer with not existed item_id', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-2-FT', description: '', tokenPrefix: 'T'}); - await expect(collection.transfer(alice, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.TokenValueTooLow/); - }); - - itSub.ifWithPallets('[refungible] Transfer with not existed item_id', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-2-RFT', description: '', tokenPrefix: 'T'}); - await expect(collection.transferToken(alice, 1, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.TokenValueTooLow/); - }); - - itSub('Zero transfer NFT', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-3-NFT', description: '', tokenPrefix: 'T'}); - const tokenAlice = await collection.mintToken(alice, {Substrate: alice.address}); - const tokenBob = await collection.mintToken(alice, {Substrate: bob.address}); - // 1. Zero transfer of own tokens allowed: - await helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: bob.address}, collection.collectionId, tokenAlice.tokenId, 0]); - // 2. Zero transfer of non-owned tokens not allowed: - await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: alice.address}, collection.collectionId, tokenBob.tokenId, 0])).to.be.rejectedWith('common.NoPermission'); - // 3. Zero transfer of non-existing tokens not allowed: - await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: alice.address}, collection.collectionId, 10, 0])).to.be.rejectedWith('common.TokenNotFound'); - expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: alice.address}); - expect(await tokenBob.getOwner()).to.deep.eq({Substrate: bob.address}); - // 4. Storage is not corrupted: - await tokenAlice.transfer(alice, {Substrate: bob.address}); - await tokenBob.transfer(bob, {Substrate: alice.address}); - expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: bob.address}); - expect(await tokenBob.getOwner()).to.deep.eq({Substrate: alice.address}); - }); - - itSub('[nft] Transfer with deleted item_id', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-3-NFT', description: '', tokenPrefix: 'T'}); - const nft = await collection.mintToken(alice); - - await nft.burn(alice); - - await expect(nft.transfer(alice, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.TokenNotFound/); - }); - - itSub('[fungible] Transfer with deleted item_id', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-3-FT', description: '', tokenPrefix: 'T'}); - await collection.mint(alice, 10n); - - await collection.burnTokens(alice, 10n); - - await expect(collection.transfer(alice, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.TokenValueTooLow/); - }); - - itSub.ifWithPallets('[refungible] Transfer with deleted item_id', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-3-RFT', description: '', tokenPrefix: 'T'}); - const rft = await collection.mintToken(alice, 10n); - - await rft.burn(alice, 10n); - - await expect(rft.transfer(alice, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.TokenValueTooLow/); - }); - - itSub('[nft] Transfer with recipient that is not owner', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-4-NFT', description: '', tokenPrefix: 'T'}); - const nft = await collection.mintToken(alice); - - await expect(nft.transfer(bob, {Substrate: bob.address})) - .to.be.rejectedWith(/common\.NoPermission/); - expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); - }); - - itSub('[fungible] Transfer with recipient that is not owner', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-4-FT', description: '', tokenPrefix: 'T'}); - await collection.mint(alice, 10n); - - await expect(collection.transfer(bob, {Substrate: bob.address}, 9n)) - .to.be.rejectedWith(/common\.TokenValueTooLow/); - expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(0n); - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(10n); - }); - - itSub.ifWithPallets('[refungible] Transfer with recipient that is not owner', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-1-RFT', description: '', tokenPrefix: 'T'}); - const rft = await collection.mintToken(alice, 10n); - - await expect(rft.transfer(bob, {Substrate: bob.address}, 9n)) - .to.be.rejectedWith(/common\.TokenValueTooLow/); - expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(0n); - expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(10n); - }); -}); - -describe('Transfers to self (potentially over substrate-evm boundary)', () => { - let donor: IKeyringPair; - - before(async function() { - await usingEthPlaygrounds(async (_, privateKey) => { - donor = await privateKey({url: import.meta.url}); - }); - }); - - itEth('Transfers to self. In case of same frontend', async ({helper}) => { - const [owner] = await helper.arrange.createAccounts([10n], donor); - const collection = await helper.ft.mintCollection(owner, {}); - await collection.mint(owner, 100n); - - const ownerProxy = helper.address.substrateToEth(owner.address); - - // transfer to own proxy - await collection.transfer(owner, {Ethereum: ownerProxy}, 10n); - expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n); - expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n); - - // transfer-from own proxy to own proxy again - await collection.transferFrom(owner, {Ethereum: ownerProxy}, {Ethereum: ownerProxy}, 5n); - expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n); - expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n); - }); - - itEth('Transfers to self. In case of substrate-evm boundary', async ({helper}) => { - const [owner] = await helper.arrange.createAccounts([10n], donor); - const collection = await helper.ft.mintCollection(owner, {}); - await collection.mint(owner, 100n); - - const ownerProxy = helper.address.substrateToEth(owner.address); - - // transfer to own proxy - await collection.transfer(owner, {Ethereum: ownerProxy}, 10n); - expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n); - expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n); - - // transfer-from own proxy to self - await collection.transferFrom(owner, {Ethereum: ownerProxy}, {Substrate: owner.address}, 5n); - expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(95n); - expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(5n); - }); - - itEth('Transfers to self. In case of inside substrate-evm', async ({helper}) => { - const [owner] = await helper.arrange.createAccounts([10n], donor); - const collection = await helper.ft.mintCollection(owner, {}); - await collection.mint(owner, 100n); - - // transfer to self again - await collection.transfer(owner, {Substrate: owner.address}, 10n); - expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(100n); - - // transfer-from self to self again - await collection.transferFrom(owner, {Substrate: owner.address}, {Substrate: owner.address}, 5n); - expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(100n); - }); - - itEth('Transfers to self. In case of inside substrate-evm when not enought "Fungibles"', async ({helper}) => { - const [owner] = await helper.arrange.createAccounts([10n], donor); - const collection = await helper.ft.mintCollection(owner, {}); - await collection.mint(owner, 10n); - - // transfer to self again - await expect(collection.transfer(owner, {Substrate: owner.address}, 11n)) - .to.be.rejectedWith(/common\.TokenValueTooLow/); - - // transfer-from self to self again - await expect(collection.transferFrom(owner, {Substrate: owner.address}, {Substrate: owner.address}, 12n)) - .to.be.rejectedWith(/common\.TokenValueTooLow/); - expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(10n); - }); -}); --- a/js-packages/tests/src/transferFrom.test.ts +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, Pallets, usingPlaygrounds, expect} from './util/index.js'; -import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js'; - -describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); - }); - }); - - itSub('[nft] Execute the extrinsic and check nftItemList - owner of token', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-1', description: '', tokenPrefix: 'TF'}); - const nft = await collection.mintToken(alice); - await nft.approve(alice, {Substrate: bob.address}); - expect(await nft.isApproved({Substrate: bob.address})).to.be.true; - - await nft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}); - expect(await nft.getOwner()).to.be.deep.equal({Substrate: charlie.address}); - }); - - itSub('[fungible] Execute the extrinsic and check nftItemList - owner of token', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-2', description: '', tokenPrefix: 'TF'}); - await collection.mint(alice, 10n); - await collection.approveTokens(alice, {Substrate: bob.address}, 7n); - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(7n); - - await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 6n); - expect(await collection.getBalance({Substrate: charlie.address})).to.be.equal(6n); - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(4n); - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(1n); - }); - - itSub.ifWithPallets('[refungible] Execute the extrinsic and check nftItemList - owner of token', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-3', description: '', tokenPrefix: 'TF'}); - const rft = await collection.mintToken(alice, 10n); - await rft.approve(alice, {Substrate: bob.address}, 7n); - expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(7n); - - await rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 6n); - expect(await rft.getBalance({Substrate: charlie.address})).to.be.equal(6n); - expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(4n); - expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(1n); - }); - - itSub('Should reduce allowance if value is big', async ({helper}) => { - // fungible - const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-4', description: '', tokenPrefix: 'TF'}); - await collection.mint(alice, 500000n); - - await collection.approveTokens(alice, {Substrate: bob.address}, 500000n); - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(500000n); - await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 500000n); - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(0n); - }); - - itSub('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-5', description: '', tokenPrefix: 'TF'}); - await collection.setLimits(alice, {ownerCanTransfer: true}); - - const nft = await collection.mintToken(alice, {Substrate: bob.address}); - await nft.transferFrom(alice, {Substrate: bob.address}, {Substrate: charlie.address}); - expect(await nft.getOwner()).to.be.deep.equal({Substrate: charlie.address}); - }); -}); - -describe('Negative Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - let charlie: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({url: import.meta.url}); - [alice, bob, charlie] = await helper.arrange.createAccounts([50n, 10n, 10n], donor); - }); - }); - - itSub('transferFrom for a collection that does not exist', async ({helper}) => { - await expect(helper.collection.approveToken(alice, NON_EXISTENT_COLLECTION_ID, 0, {Substrate: bob.address}, 1n)) - .to.be.rejectedWith(/common\.CollectionNotFound/); - await expect(helper.collection.transferTokenFrom(bob, NON_EXISTENT_COLLECTION_ID, 0, {Substrate: alice.address}, {Substrate: bob.address}, 1n)) - .to.be.rejectedWith(/common\.CollectionNotFound/); - }); - - /* itSub('transferFrom for a collection that was destroyed', async ({helper}) => { - this test copies approve negative test - }); */ - - /* itSub('transferFrom a token that does not exist', async ({helper}) => { - this test copies approve negative test - }); */ - - /* itSub('transferFrom a token that was deleted', async ({helper}) => { - this test copies approve negative test - }); */ - - itSub('[nft] transferFrom for not approved address', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-1', description: '', tokenPrefix: 'TF'}); - const nft = await collection.mintToken(alice); - - await expect(nft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address})) - .to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); - }); - - itSub('[fungible] transferFrom for not approved address', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-1', description: '', tokenPrefix: 'TF'}); - await collection.mint(alice, 10n); - - await expect(collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 5n)) - .to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); - expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); - expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); - }); - - itSub.ifWithPallets('[refungible] transferFrom for not approved address', [Pallets.ReFungible], async({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-3', description: '', tokenPrefix: 'TF'}); - const rft = await collection.mintToken(alice, 10n); - - await expect(rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address})) - .to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); - expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); - expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); - }); - - itSub('[nft] transferFrom incorrect token count', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-4', description: '', tokenPrefix: 'TF'}); - const nft = await collection.mintToken(alice); - - await nft.approve(alice, {Substrate: bob.address}); - expect(await nft.isApproved({Substrate: bob.address})).to.be.true; - - await expect(helper.collection.transferTokenFrom( - bob, - collection.collectionId, - nft.tokenId, - {Substrate: alice.address}, - {Substrate: charlie.address}, - 2n, - )).to.be.rejectedWith(/nonfungible\.NonfungibleItemsHaveNoAmount/); - expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); - }); - - itSub('[fungible] transferFrom incorrect token count', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-5', description: '', tokenPrefix: 'TF'}); - await collection.mint(alice, 10n); - - await collection.approveTokens(alice, {Substrate: bob.address}, 2n); - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(2n); - - await expect(collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 5n)) - .to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); - expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); - expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); - }); - - itSub.ifWithPallets('[refungible] transferFrom incorrect token count', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-6', description: '', tokenPrefix: 'TF'}); - const rft = await collection.mintToken(alice, 10n); - - await rft.approve(alice, {Substrate: bob.address}, 5n); - expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(5n); - - await expect(rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 7n)) - .to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); - expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); - expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); - }); - - itSub('[nft] execute transferFrom from account that is not owner of collection', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-7', description: '', tokenPrefix: 'TF'}); - const nft = await collection.mintToken(alice); - - await expect(nft.approve(charlie, {Substrate: bob.address})).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); - expect(await nft.isApproved({Substrate: bob.address})).to.be.false; - - await expect(nft.transferFrom( - charlie, - {Substrate: alice.address}, - {Substrate: charlie.address}, - )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); - }); - - itSub('[fungible] execute transferFrom from account that is not owner of collection', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-8', description: '', tokenPrefix: 'TF'}); - await collection.mint(alice, 10000n); - - await expect(collection.approveTokens(charlie, {Substrate: bob.address}, 1n)).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(0n); - expect(await collection.getApprovedTokens({Substrate: charlie.address}, {Substrate: bob.address})).to.be.eq(0n); - - await expect(collection.transferFrom( - charlie, - {Substrate: alice.address}, - {Substrate: charlie.address}, - )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10000n); - expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); - expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); - }); - - itSub.ifWithPallets('[refungible] execute transferFrom from account that is not owner of collection', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-9', description: '', tokenPrefix: 'TF'}); - const rft = await collection.mintToken(alice, 10000n); - - await expect(rft.approve(charlie, {Substrate: bob.address}, 1n)).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); - expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(0n); - expect(await rft.getApprovedPieces({Substrate: charlie.address}, {Substrate: bob.address})).to.be.eq(0n); - - await expect(rft.transferFrom( - charlie, - {Substrate: alice.address}, - {Substrate: charlie.address}, - )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); - expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10000n); - expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); - expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); - }); - - itSub('transferFrom burnt token before approve NFT', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-10', description: '', tokenPrefix: 'TF'}); - await collection.setLimits(alice, {ownerCanTransfer: true}); - const nft = await collection.mintToken(alice); - - await nft.burn(alice); - await expect(nft.approve(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.TokenNotFound/); - - await expect(nft.transferFrom( - bob, - {Substrate: alice.address}, - {Substrate: charlie.address}, - )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); - }); - - itSub('transferFrom burnt token before approve Fungible', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-11', description: '', tokenPrefix: 'TF'}); - await collection.setLimits(alice, {ownerCanTransfer: true}); - await collection.mint(alice, 10n); - - await collection.burnTokens(alice, 10n); - await expect(collection.approveTokens(alice, {Substrate: bob.address})).to.be.not.rejected; - - await expect(collection.transferFrom( - alice, - {Substrate: alice.address}, - {Substrate: charlie.address}, - )).to.be.rejectedWith(/common\.TokenValueTooLow/); - }); - - itSub.ifWithPallets('transferFrom burnt token before approve ReFungible', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-12', description: '', tokenPrefix: 'TF'}); - await collection.setLimits(alice, {ownerCanTransfer: true}); - const rft = await collection.mintToken(alice, 10n); - - await rft.burn(alice, 10n); - await expect(rft.approve(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); - - await expect(rft.transferFrom( - alice, - {Substrate: alice.address}, - {Substrate: charlie.address}, - )).to.be.rejectedWith(/common\.TokenValueTooLow/); - }); - - itSub('transferFrom burnt token after approve NFT', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-13', description: '', tokenPrefix: 'TF'}); - const nft = await collection.mintToken(alice); - - await nft.approve(alice, {Substrate: bob.address}); - expect(await nft.isApproved({Substrate: bob.address})).to.be.true; - - await nft.burn(alice); - - await expect(nft.transferFrom( - bob, - {Substrate: alice.address}, - {Substrate: charlie.address}, - )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); - }); - - itSub('transferFrom burnt token after approve Fungible', async ({helper}) => { - const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-14', description: '', tokenPrefix: 'TF'}); - await collection.mint(alice, 10n); - - await collection.approveTokens(alice, {Substrate: bob.address}); - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(1n); - - await collection.burnTokens(alice, 10n); - - await expect(collection.transferFrom( - bob, - {Substrate: alice.address}, - {Substrate: charlie.address}, - )).to.be.rejectedWith(/common\.TokenValueTooLow/); - }); - - itSub.ifWithPallets('transferFrom burnt token after approve ReFungible', [Pallets.ReFungible], async ({helper}) => { - const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-15', description: '', tokenPrefix: 'TF'}); - const rft = await collection.mintToken(alice, 10n); - - await rft.approve(alice, {Substrate: bob.address}, 10n); - expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(10n); - - await rft.burn(alice, 10n); - - await expect(rft.transferFrom( - bob, - {Substrate: alice.address}, - {Substrate: charlie.address}, - )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); - }); - - itSub('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-16', description: '', tokenPrefix: 'TF'}); - const nft = await collection.mintToken(alice, {Substrate: bob.address}); - - await collection.setLimits(alice, {ownerCanTransfer: false}); - - await expect(nft.transferFrom( - alice, - {Substrate: bob.address}, - {Substrate: charlie.address}, - )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); - }); - - itSub('zero transfer NFT', async ({helper}) => { - const collection = await helper.nft.mintCollection(alice, {name: 'Zero', description: 'Zero transfer', tokenPrefix: 'TF'}); - const notApprovedNft = await collection.mintToken(alice, {Substrate: bob.address}); - const approvedNft = await collection.mintToken(alice, {Substrate: bob.address}); - await approvedNft.approve(bob, {Substrate: alice.address}); - - // 1. Cannot zero transferFrom (non-existing token) - await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, 9999, 0])).to.be.rejectedWith('common.ApprovedValueTooLow'); - // 2. Cannot zero transferFrom (not approved token) - await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, notApprovedNft.tokenId, 0])).to.be.rejectedWith('common.ApprovedValueTooLow'); - // 3. Can zero transferFrom (approved token): - await helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, approvedNft.tokenId, 0]); - - // 4.1 approvedNft still approved: - expect(await approvedNft.isApproved({Substrate: alice.address})).to.be.true; - // 4.2 bob is still the owner: - expect(await approvedNft.getOwner()).to.deep.eq({Substrate: bob.address}); - expect(await notApprovedNft.getOwner()).to.deep.eq({Substrate: bob.address}); - // 4.3 Alice can transfer approved nft: - await approvedNft.transferFrom(alice, {Substrate: bob.address}, {Substrate: alice.address}); - expect(await approvedNft.getOwner()).to.deep.eq({Substrate: alice.address}); - }); -}); --- a/js-packages/tests/src/tx-version-presence.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import {Metadata} from '@polkadot/types'; -import {itSub, usingPlaygrounds, expect} from './util/index.js'; - -let metadata: Metadata; - -describe('TxVersion is present', () => { - before(async () => { - await usingPlaygrounds(async helper => { - metadata = await helper.callRpc('api.rpc.state.getMetadata', []); - }); - }); - - itSub('Signed extension CheckTxVersion is present', () => { - expect(metadata.asLatest.extrinsic.signedExtensions.map(se => se.identifier.toString())).to.include('CheckTxVersion'); - }); -}); --- a/js-packages/tests/src/util/authorizeEnactUpgrade.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {readFile} from 'fs/promises'; -import {u8aToHex} from '@polkadot/util'; -import {usingPlaygrounds} from './index.js'; -import {blake2AsHex} from '@polkadot/util-crypto'; - - -const codePath = process.argv[2]; -if(!codePath) throw new Error('missing code path argument'); - -const code = await readFile(codePath); - -await usingPlaygrounds(async (helper, privateKey) => { - const alice = await privateKey('//Alice'); - const hex = blake2AsHex(code); - await helper.getSudo().executeExtrinsic(alice, 'api.tx.parachainSystem.authorizeUpgrade', [hex, true]); - await helper.getSudo().executeExtrinsicUncheckedWeight(alice, 'api.tx.parachainSystem.enactAuthorizedUpgrade', [u8aToHex(code)]); -}); -// We miss disconnect/unref somewhere. -process.exit(0); --- a/js-packages/tests/src/util/createHrmp.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {usingPlaygrounds} from './index.js'; -import config from '../config.js'; - -const profile = process.argv[2]; -if(!profile) throw new Error('missing profile/relay argument'); - -await usingPlaygrounds(async (helper, privateKey) => { - const bidirOpen = async (a: number, b: number) => { - console.log(`Opening ${a} <=> ${b}`); - await helper.getSudo().executeExtrinsic(alice, 'api.tx.hrmp.forceOpenHrmpChannel', [a, b, 8, 512]); - await helper.getSudo().executeExtrinsic(alice, 'api.tx.hrmp.forceOpenHrmpChannel', [b, a, 8, 512]); - }; - const alice = await privateKey('//Alice'); - switch (profile) { - case 'opal': - await bidirOpen(1001, 1002); - break; - case 'quartz': - await bidirOpen(1001, 1002); - await bidirOpen(1001, 1003); - await bidirOpen(1001, 1004); - await bidirOpen(1001, 1005); - break; - case 'unique': - await bidirOpen(1001, 1002); - await bidirOpen(1001, 1003); - await bidirOpen(1001, 1004); - await bidirOpen(1001, 1005); - await bidirOpen(1001, 1006); - break; - default: - throw new Error(`unknown hrmp config profile: ${profile}`); - } -}, config.relayUrl); -// We miss disconnect/unref somewhere. -process.exit(0); --- a/js-packages/tests/src/util/frankensteinMigrate.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {migration as locksToFreezesMigration} from '../migrations/942057-appPromotion/index.js'; -export interface Migration { - before: () => Promise, - after: () => Promise, -} - -export const migrations: {[key: string]: Migration} = { - 'v942057': locksToFreezesMigration, -}; --- a/js-packages/tests/src/util/globalSetup.ts +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -import { - usingPlaygrounds, Pallets, DONOR_FUNDING, MINIMUM_DONOR_FUND, LOCKING_PERIOD, UNLOCKING_PERIOD, makeNames, -} from './index.js'; -import * as path from 'path'; -import {promises as fs} from 'fs'; - -const {dirname} = makeNames(import.meta.url); - -// This function should be called before running test suites. -const globalSetup = async (): Promise => { - await usingPlaygrounds(async (helper, privateKey) => { - try { - // 1. Wait node producing blocks - await helper.wait.newBlocks(1, 600_000); - - // 2. Create donors for test files - await fundFilenamesWithRetries(3) - .then((result) => { - if(!result) throw Error('Some problems with fundFilenamesWithRetries'); - }); - - // 3. Configure App Promotion - const missingPallets = helper.fetchMissingPalletNames([Pallets.AppPromotion]); - if(missingPallets.length === 0) { - const superuser = await privateKey('//Alice'); - const palletAddress = helper.arrange.calculatePalletAddress('appstake'); - const palletAdmin = await privateKey('//PromotionAdmin'); - const api = helper.getApi(); - await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address}))); - const nominal = helper.balance.getOneTokenNominal(); - await helper.balance.transferToSubstrate(superuser, palletAdmin.address, 10000n * nominal); - await helper.balance.transferToSubstrate(superuser, palletAddress, 10000n * nominal); - await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [api.tx.configuration - .setAppPromotionConfigurationOverride({ - recalculationInterval: LOCKING_PERIOD, - pendingInterval: UNLOCKING_PERIOD})], true); - } - } catch (error) { - console.error(error); - throw Error('Error during globalSetup'); - } - }); -}; - -async function getFiles(rootPath: string): Promise { - const files = await fs.readdir(rootPath, {withFileTypes: true}); - const filenames: string[] = []; - for(const entry of files) { - const res = path.resolve(rootPath, entry.name); - if(entry.isDirectory()) { - filenames.push(...await getFiles(res)); - } else { - filenames.push(res); - } - } - return filenames; -} - -const fundFilenames = async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const oneToken = helper.balance.getOneTokenNominal(); - const alice = await privateKey('//Alice'); - const nonce = await helper.chain.getNonce(alice.address); - const filenames = await getFiles(path.resolve(dirname, '..')); - - // batching is actually undesireable, it takes away the time while all the transactions actually succeed - const batchSize = 300; - let balanceGrantedCounter = 0; - for(let b = 0; b < filenames.length; b += batchSize) { - const tx: Promise[] = []; - let batchBalanceGrantedCounter = 0; - for(let i = 0; batchBalanceGrantedCounter < batchSize && b + i < filenames.length; i++) { - const f = filenames[b + i]; - if(!f.endsWith('.test.ts') && !f.endsWith('seqtest.ts') || f.includes('.outdated')) continue; - const account = await privateKey({filename: f, ignoreFundsPresence: true}); - const aliceBalance = await helper.balance.getSubstrate(account.address); - - if(aliceBalance < MINIMUM_DONOR_FUND * oneToken) { - tx.push(helper.executeExtrinsic( - alice, - 'api.tx.balances.transfer', - [account.address, DONOR_FUNDING * oneToken], - true, - {nonce: nonce + balanceGrantedCounter++}, - ).then(() => true).catch(() => {console.error(`Transaction to ${path.basename(f)} registered as failed. Strange.`); return false;})); - batchBalanceGrantedCounter++; - } - } - - if(tx.length > 0) { - console.log(`Granting funds to ${batchBalanceGrantedCounter} filename accounts.`); - const result = await Promise.all(tx); - if(result && result.lastIndexOf(false) > -1) throw new Error('The transactions actually probably succeeded, should check the balances.'); - } - } - - if(balanceGrantedCounter == 0) console.log('No account needs additional funding.'); - }); -}; - -const fundFilenamesWithRetries = (retriesLeft: number): Promise => { - if(retriesLeft <= 0) return Promise.resolve(false); - return fundFilenames() - .then(() => Promise.resolve(true)) - .catch(e => { - console.error(e); - console.error(`Some transactions might have failed. ${retriesLeft > 1 ? 'Retrying...' : 'Something is wrong.'}\n`); - return fundFilenamesWithRetries(--retriesLeft); - }); -}; - -globalSetup().catch(e => { - console.error('Setup error'); - console.error(e); - process.exit(1); -}); --- a/js-packages/tests/src/util/identitySetter.ts +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// SPDX-License-Identifier: Apache-2.0 -// -// Pulls identities and sub-identities from a chain and then makes a preimage to later force upload them into another. -// Only changed or previously non-existent data are inserted. -// -// Usage: `yarn setIdentities [relay WS URL] [parachain WS URL] [user key] -// Example: `yarn setIdentities wss://polkadot-rpc.dwellir.com ws://localhost:9944 escape pattern miracle train sudden cart adapt embark wedding alien lamp mesh` - -import {encodeAddress} from '@polkadot/keyring'; -import type {IKeyringPair} from '@polkadot/types/types'; -import {usingPlaygrounds, Pallets} from './index.js'; -import {ChainHelperBase} from '@unique/playgrounds/src/unique.js'; - -const relayUrl = process.argv[2] ?? 'ws://localhost:9844'; -const paraUrl = process.argv[3] ?? 'ws://localhost:9944'; -const key = process.argv.length > 4 ? process.argv.slice(4).join(' ') : '//Alice'; - -export function extractAccountId(key: any): string { - return (key as any).toHuman()[0]; -} - -export function extractIdentityInfo(value: any): object { - const heart = (value as any).unwrap(); - const identity = heart.toJSON(); - identity.judgements = heart.judgements.toHuman(); - return identity; -} - -export function extractIdentity(key: any, value: any): [string, any] { - return [extractAccountId(key), extractIdentityInfo(value)]; -} - -export async function getIdentities(helper: ChainHelperBase, noneCasePredicate?: (key: any, value: any) => void) { - const identities: [string, any][] = []; - for(const [key, v] of await helper.getApi().query.identity.identityOf.entries()) { - const value = v as any; - if(value.isNone) { - if(noneCasePredicate) noneCasePredicate(key, value); - continue; - } - identities.push(extractIdentity(key, value)); - } - return identities; -} - -// whether the existing chain data is more important than the coming -function isCurrentChainDataPriority(helper: ChainHelperBase, currentChainId: number | undefined, relayChainId: number) { - if(!currentChainId) return false; - // information has come from local chain, and is automatically superior - if(currentChainId == helper.chain.getChainProperties().ss58Format) return true; - // the lower the id, the more important it is (Polkadot has ss58 prefix = 0, Kusama has ss58 prefix = 2) - return currentChainId < relayChainId; -} - -// construct an object with all data necessary for insertion from storage query results -export function constructSubInfo(identityAccount: string, subQuery: any, supers: any[], ss58?: number): [string, any] { - const deposit = subQuery.toJSON()[0]; - const subIdentities = subQuery.toHuman()[1]; - subIdentities.map((sub: string) => supers.find((sup: any) => sup[0] === sub)); - - return [ - encodeAddress(identityAccount, ss58), [ - deposit, - subIdentities.map((sub: string): [string, object] | null => { - const sup = supers.find((sup: any) => sup[0] === sub); - if(!sup) { - console.error(`Error: Could not find info on super for \nsub-identity account\t${sub} of \nsuper account \t\t${identityAccount}, skipping.`); - return null; - } - return [encodeAddress(sub, ss58), sup[1].toJSON()[1]]; - }).filter((x: any) => x), - ], - ]; -} - -export async function getSubs(helper: ChainHelperBase) { - return (await helper.getApi().query.identity.subsOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]); -} - -export async function getSupers(helper: ChainHelperBase) { - return (await helper.getApi().query.identity.superOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]); -} - -async function uploadPreimage(helper: ChainHelperBase, preimageMaker: IKeyringPair, preimage: string) { - try { - await helper.executeExtrinsic(preimageMaker, 'api.tx.preimage.notePreimage', [preimage]); - } catch (e: any) { - if(e.message.includes('AlreadyNoted')) { - console.warn('Warning: The same preimage already exists on the chain. Nothing was uploaded.'); - } else { - console.error(e); - } - } -} - -// The utility for pulling identity and sub-identity data -const forceInsertIdentities = async (): Promise => { - let relaySS58Prefix = 0; - const identitiesOnRelay: any[] = []; - const subsOnRelay: any[] = []; - const identitiesToRemove: string[] = []; - await usingPlaygrounds(async helper => { - try { - relaySS58Prefix = helper.chain.getChainProperties().ss58Format; - // iterate over every identity - for(const [key, value] of await getIdentities(helper, (key, _value) => identitiesToRemove.push((key as any).toHuman()[0]))) { - // if any of the judgements resulted in a good confirmed outcome, keep this identity - let knownGood = false, reasonable = false; - for(const [_id, judgement] of value.judgements) { - if(judgement == 'KnownGood') knownGood = true; - if(judgement == 'Reasonable') reasonable = true; - } - if(!(reasonable || knownGood)) continue; - // replace the registrator id with the relay chain's ss58 format - value.judgements = [[helper.chain.getChainProperties().ss58Format, knownGood ? 'KnownGood' : 'Reasonable']]; - identitiesOnRelay.push([key, value]); - } - - const sublessIdentities = [...identitiesOnRelay]; - const supersOfSubs = await getSupers(helper); - - // iterate over every sub-identity - for(const [key, value] of await getSubs(helper)) { - // only get subs of the identities interesting to us - const identityIndex = sublessIdentities.findIndex((x: any) => x[0] == key); - if(identityIndex == -1) continue; - sublessIdentities.splice(identityIndex, 1); - subsOnRelay.push(constructSubInfo(key, value, supersOfSubs)); - } - - // mark the rest of sub-identities for deletion with empty arrays - /*for(const [account, _identity] of sublessIdentities) { - subsOnRelay.push([account, ['0', []]]); - }*/ - } catch (error) { - console.error(error); - throw Error('Error during fetching identities'); - } - }, relayUrl); - - await usingPlaygrounds(async (helper, privateKey) => { - if(helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) console.error('pallet-identity is not included in parachain.'); - if(helper.fetchMissingPalletNames([Pallets.Preimage]).length != 0) console.error('pallet-preimage is not included in parachain.'); - - try { - const preimageMaker = await privateKey(key); - const ss58Format = helper.chain.getChainProperties().ss58Format; - const paraIdentities = await getIdentities(helper); - const identitiesToAdd: any[] = []; - const paraAccountsRegistrators: {[name: string]: number} = {}; - - // cross-reference every account for changes - for(const [key, value] of identitiesOnRelay) { - const encodedKey = encodeAddress(key, ss58Format); - - // only update if the identity info does not exist or is changed - const identity = paraIdentities.find(i => i[0] === encodedKey); - if(identity) { - const registratorId = identity[1].judgements[0][0]; - paraAccountsRegistrators[encodedKey] = registratorId; - if(isCurrentChainDataPriority(helper, registratorId, value.judgements[0][0]) - || JSON.stringify(value.info) === JSON.stringify(identity[1].info)) { - continue; - } - } - - identitiesToAdd.push([key, value]); - // exercise caution - in case we have an identity and the realy doesn't, it might mean one of two things: - // 1) it was deleted on the relay; - // 2) it is our own identity, we don't want to delete it. - // identitiesToRemove.push((key as any).toHuman()[0]); - } - - if(identitiesToRemove.length != 0) - await uploadPreimage( - helper, - preimageMaker, - helper.constructApiCall('api.tx.identity.forceRemoveIdentities', [identitiesToRemove]).method.toHex(), - ); - if(identitiesToAdd.length != 0) - await uploadPreimage( - helper, - preimageMaker, - helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]).method.toHex(), - ); - - console.log(`Tried to push ${identitiesToAdd.length} identities` - + ` and found ${identitiesToRemove.length} identities for potential removal.` - + ` Currently there are ${(await helper.getApi().query.identity.identityOf.keys()).length} identities on the chain.`); - - // fill sub-identities - const paraSubs = await getSubs(helper); - const supersOfSubs = await getSupers(helper); - const subsToUpdate: any[] = []; - - for(const [key, value] of subsOnRelay) { - const encodedKey = encodeAddress(key, ss58Format); - const sub = paraSubs.find(i => i[0] === encodedKey); - if(sub) { - // only update if the sub-identity info does not exist or is changed - if(isCurrentChainDataPriority(helper, paraAccountsRegistrators[encodedKey], relaySS58Prefix) - || JSON.stringify(value) === JSON.stringify(constructSubInfo(sub[0], sub[1], supersOfSubs)[1])) { - continue; - } - } else if(value[1].length == 0) - continue; - - subsToUpdate.push([key, value]); - } - - if(subsToUpdate.length != 0) - await uploadPreimage( - helper, - preimageMaker, - helper.constructApiCall('api.tx.identity.forceSetSubs', [subsToUpdate]).method.toHex(), - ); - - console.log(`Also tried to push ${subsToUpdate.length} identities with their sub-identities.` - + ` Currently there are ${(await helper.getApi().query.identity.subsOf.keys()).length} identities with subs.`); - } catch (error) { - console.error(error); - throw Error('Error during setting identities'); - } - }, paraUrl); -}; - -if(process.argv[1] === module.filename) - forceInsertIdentities().catch(() => process.exit(1)); --- a/js-packages/tests/src/util/index.ts +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -import * as path from 'path'; -import * as crypto from 'crypto'; -import type {IKeyringPair} from '@polkadot/types/types/interfaces'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import chaiSubset from 'chai-subset'; -import {Context} from 'mocha'; -import config from '../config.js'; -import {ChainHelperBase} from '@unique/playgrounds/src/unique.js'; -import type {ILogger} from '@unique/playgrounds/src/types.js'; -import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper, DevPolkadexHelper} from '@unique/playgrounds/src/unique.dev.js'; -import {dirname} from 'path'; -import {fileURLToPath} from 'url'; - -chai.config.truncateThreshold = 0; -chai.use(chaiAsPromised); -chai.use(chaiSubset); -export const expect = chai.expect; - -const getTestHash = (filename: string) => crypto.createHash('md5').update(filename).digest('hex'); - -export const getTestSeed = (filename: string) => `//Alice+${getTestHash(filename)}`; - -async function usingPlaygroundsGeneral( - helperType: new (logger: ILogger) => T, - url: string, - code: (helper: T, privateKey: (seed: string | { filename?: string, url?: string, ignoreFundsPresence?: boolean }) => Promise) => Promise, -): Promise { - const silentConsole = new SilentConsole(); - silentConsole.enable(); - - const helper = new helperType(new SilentLogger()); - let result; - try { - await helper.connect(url); - const ss58Format = helper.chain.getChainProperties().ss58Format; - const privateKey = async (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => { - if(typeof seed === 'string') { - return helper.util.fromSeed(seed, ss58Format); - } - if(seed.url) { - const {filename} = makeNames(seed.url); - seed.filename = filename; - } else if(seed.filename) { - // Pass - } else { - throw new Error('no url nor filename set'); - } - const actualSeed = getTestSeed(seed.filename); - let account = helper.util.fromSeed(actualSeed, ss58Format); - // here's to hoping that no - if(!seed.ignoreFundsPresence && ((helper as any)['balance'] == undefined || await (helper as any).balance.getSubstrate(account.address) < MINIMUM_DONOR_FUND)) { - console.warn(`${path.basename(seed.filename)}: Not enough funds present on the filename account. Using the default one as the donor instead.`); - account = helper.util.fromSeed('//Alice', ss58Format); - } - return account; - }; - result = await code(helper, privateKey); - } - finally { - await helper.disconnect(); - silentConsole.disable(); - } - return result as any as R; -} - -export const usingPlaygrounds = (code: (helper: DevUniqueHelper, privateKey: (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => Promise) => Promise, url: string = config.substrateUrl) => usingPlaygroundsGeneral(DevUniqueHelper, url, code); - -export const usingWestmintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevWestmintHelper, url, code); - -export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevWestmintHelper, url, code); - -export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevWestmintHelper, url, code); - -export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevRelayHelper, url, code); - -export const usingAcalaPlaygrounds = (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevAcalaHelper, url, code); - -export const usingKaruraPlaygrounds = (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevAcalaHelper, url, code); - -export const usingMoonbeamPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevMoonbeamHelper, url, code); - -export const usingMoonriverPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevMoonriverHelper, url, code); - -export const usingAstarPlaygrounds = (url: string, code: (helper: DevAstarHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevAstarHelper, url, code); - -export const usingShidenPlaygrounds = (url: string, code: (helper: DevShidenHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevShidenHelper, url, code); - -export const usingPolkadexPlaygrounds = (url: string, code: (helper: DevPolkadexHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevPolkadexHelper, url, code); - -export const MINIMUM_DONOR_FUND = 4_000_000n; -export const DONOR_FUNDING = 4_000_000n; - -// App-promotion periods: -export const LOCKING_PERIOD = 12n; // 12 blocks of relay -export const UNLOCKING_PERIOD = 6n; // 6 blocks of parachain - -// Native contracts -export const COLLECTION_HELPER = '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f'; -export const CONTRACT_HELPER = '0x842899ECF380553E8a4de75bF534cdf6fBF64049'; - -export enum Pallets { - Inflation = 'inflation', - ReFungible = 'refungible', - Fungible = 'fungible', - NFT = 'nonfungible', - Scheduler = 'scheduler', - UniqueScheduler = 'uniqueScheduler', - AppPromotion = 'apppromotion', - CollatorSelection = 'collatorselection', - Session = 'session', - Identity = 'identity', - Democracy = 'democracy', - Council = 'council', - //CouncilMembership = 'councilmembership', - TechnicalCommittee = 'technicalcommittee', - Fellowship = 'fellowshipcollective', - Preimage = 'preimage', - Maintenance = 'maintenance', - TestUtils = 'testutils', -} - -export function requirePalletsOrSkip(test: Context, helper: DevUniqueHelper, requiredPallets: readonly string[]) { - const missingPallets = helper.fetchMissingPalletNames(requiredPallets); - - if(missingPallets.length > 0) { - const skipMsg = `\tSkipping test '${test.test?.title}'.\n\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`; - console.warn('\x1b[38:5:208m%s\x1b[0m', skipMsg); - test.skip(); - } -} - -export function itSub(name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) { - (opts.only ? it.only : - opts.skip ? it.skip : it)(name, async function () { - await usingPlaygrounds(async (helper, privateKey) => { - if(opts.requiredPallets) { - requirePalletsOrSkip(this, helper, opts.requiredPallets); - } - - await cb({helper, privateKey}); - }); - }); -} -export function itSubIfWithPallet(name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) { - return itSub(name, cb, {requiredPallets: required, ...opts}); -} -itSub.only = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSub(name, cb, {only: true}); -itSub.skip = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSub(name, cb, {skip: true}); - -itSubIfWithPallet.only = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSubIfWithPallet(name, required, cb, {only: true}); -itSubIfWithPallet.skip = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSubIfWithPallet(name, required, cb, {skip: true}); -itSub.ifWithPallets = itSubIfWithPallet; - -export type SchedKind = 'anon' | 'named'; - -export function itSched( - name: string, - cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any, - opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}, -) { - itSub(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts); - itSub(name + ' (named scheduling)', (apis) => cb('named', apis), opts); -} -itSched.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSched(name, cb, {only: true}); -itSched.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSched(name, cb, {skip: true}); -itSched.ifWithPallets = itSchedIfWithPallets; - -function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) { - return itSched(name, cb, {requiredPallets: required, ...opts}); -} - -export function describeXCM(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) { - (process.env.RUN_XCM_TESTS && !opts.skip - ? describe - : describe.skip)(title, fn); -} - -describeXCM.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeXCM(name, fn, {skip: true}); - -export function describeGov(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) { - (process.env.RUN_GOV_TESTS && !opts.skip - ? describe - : describe.skip)(title, fn); -} - -describeGov.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeGov(name, fn, {skip: true}); - -export function sizeOfInt(i: number) { - if(i < 0 || i > 0xffffffff) throw new Error('out of range'); - if(i < 0b11_1111) { - return 1; - } else if(i < 0b11_1111_1111_1111) { - return 2; - } else if(i < 0b11_1111_1111_1111_1111_1111_1111_1111) { - return 4; - } else { - return 5; - } -} - -const UTF8_ENCODER = new TextEncoder(); -export function sizeOfEncodedStr(v: string) { - const encoded = UTF8_ENCODER.encode(v); - return sizeOfInt(encoded.length) + encoded.length; -} - -export function sizeOfProperty(prop: {key: string, value: string}) { - return sizeOfEncodedStr(prop.key) + sizeOfEncodedStr(prop.value); -} - -export function makeNames(url: string) { - const filename = fileURLToPath(url); - return { - filename, - dirname: dirname(filename), - }; -} --- a/js-packages/tests/src/util/relayIdentitiesChecker.ts +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// SPDX-License-Identifier: Apache-2.0 -// -// Checks and reports the differences between identities and sub-identities on two chains. -// -// Usage: `yarn checkRelayIdentities [relay-1 WS URL] [relay-2 WS URL]` -// Example: `yarn checkRelayIdentities wss://polkadot-rpc.dwellir.com wss://kusama-rpc.dwellir.com` - -import {encodeAddress} from '@polkadot/keyring'; -import {usingPlaygrounds} from './index.js'; -import {getIdentities, getSubs, getSupers, constructSubInfo} from './identitySetter.js'; - -const relay1Url = process.argv[2] ?? 'ws://localhost:9844'; -const relay2Url = process.argv[3] ?? 'ws://localhost:9844'; - -async function pullIdentities(relayUrl: string): Promise<[any[], any[]]> { - const identities: any[] = []; - const subs: any[] = []; - - await usingPlaygrounds(async helper => { - try { - // iterate over every identity - for(const [key, value] of await getIdentities(helper)) { - // if any of the judgements resulted in a good confirmed outcome, keep this identity - if(value.toHuman().judgements.filter((x: any) => x[1] == 'Reasonable' || x[1] == 'KnownGood').length == 0) continue; - identities.push([key, value]); - } - - const supersOfSubs = await getSupers(helper); - - // iterate over every sub-identity - for(const [key, value] of await getSubs(helper)) { - // only get subs of the identities interesting to us - if(identities.find((x: any) => x[0] == key) == -1) continue; - subs.push(constructSubInfo(key, value, supersOfSubs)); - } - } catch (error) { - console.error(error); - throw Error(`Error during fetching identities on ${relayUrl}`); - } - }, relayUrl); - - return [identities, subs]; -} - -// The utility for pulling identity and sub-identity data -const checkRelayIdentities = async (): Promise => { - const [identitiesOnRelay1, subIdentitiesOnRelay1] = await pullIdentities(relay1Url); - const [identitiesOnRelay2, subIdentitiesOnRelay2] = await pullIdentities(relay2Url); - - console.log('identities counts:\t', identitiesOnRelay1.length, identitiesOnRelay2.length); - console.log('sub-identities counts:\t', subIdentitiesOnRelay1.length, subIdentitiesOnRelay2.length); - console.log(); - - try { - const matchingAddresses: string[] = []; - const inequalIdentities: {[name: string]: [any, any]} = {}; - - for(const [key1, value1] of identitiesOnRelay1) { - const address = encodeAddress(key1); - const identity2 = identitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2)); - if(!identity2) continue; - matchingAddresses.push(address); - - //const [[key2, value2]] = identitiesOnRelay2.splice(index2, 1); - const [_key2, value2] = identity2; - if(JSON.stringify(value1.info) === JSON.stringify(value2.info)) continue; - inequalIdentities[address] = [value1, value2]; - } - - /*for (const [v1, v2] of Object.values(inequalIdentities)) { - console.log(v1.toHuman().info); - console.log(); - console.log(v2.toHuman().info); - await new Promise(resolve => setTimeout(resolve, 4000)); - }*/ - - console.log(`Accounts with identities on both relays:\t${matchingAddresses.length}`); - console.log(`Sub-identities with conflicting information:\t${Object.entries(inequalIdentities).length}`); - console.log(); - - const inequalSubIdentities = []; - let matchesFound = 0; - for(const address of matchingAddresses) { - const sub1 = subIdentitiesOnRelay1.find(([key1, _value1]) => address === encodeAddress(key1)); - if(!sub1) continue; - const sub2 = subIdentitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2)); - if(!sub2) continue; - - const [value1, value2] = [sub1[1], sub2[1]]; - matchesFound++; - - if(JSON.stringify(value1[1]) === JSON.stringify(value2[1])) { - continue; - } - inequalSubIdentities.push([value1, value2]); - } - - /*for (const [v1, v2] of inequalSubIdentities) { - console.log(v1[1]); - console.log(); - console.log(v2[1]); - await new Promise(resolve => setTimeout(resolve, 300)); - }*/ - console.log(`Accounts with sub-identities on both relays:\t${matchesFound}`); - console.log(`Of them, those with conflicting sub-identities:\t${inequalSubIdentities.length}`); - } catch (error) { - console.error(error); - throw Error('Error during comparison'); - } -}; - -if(process.argv[1] === module.filename) - checkRelayIdentities().catch(() => process.exit(1)); --- a/js-packages/tests/src/util/setCode.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {readFile} from 'fs/promises'; -import {u8aToHex} from '@polkadot/util'; -import {usingPlaygrounds} from './index.js'; - -const codePath = process.argv[2]; -if(!codePath) throw new Error('missing code path argument'); - -const code = await readFile(codePath); - -await usingPlaygrounds(async (helper, privateKey) => { - const alice = await privateKey('//Alice'); - await helper.getSudo().executeExtrinsicUncheckedWeight(alice, 'api.tx.system.setCode', [u8aToHex(code)]); -}); -// We miss disconnect/unref somewhere. -process.exit(0); --- a/js-packages/tests/src/vesting.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, usingPlaygrounds, expect} from './util/index.js'; - -describe('Vesting', () => { - let donor: IKeyringPair; - let nominal: bigint; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - donor = await privateKey({url: import.meta.url}); - nominal = helper.balance.getOneTokenNominal(); - }); - }); - - itSub('can perform vestedTransfer and claim tokens', async ({helper}) => { - // arrange - const [sender, recepient] = await helper.arrange.createAccounts([1000n, 1n], donor); - const currentRelayBlock = await helper.chain.getRelayBlockNumber(); - const SCHEDULE_1_PERIOD = 6n; // 6 blocks one period - const SCHEDULE_1_START = currentRelayBlock + 6n; // Block when 1 schedule starts - const SCHEDULE_2_PERIOD = 12n; // 12 blocks one period - const SCHEDULE_2_START = currentRelayBlock + 12n; // Block when 2 schedule starts - const schedule1 = {start: SCHEDULE_1_START, period: SCHEDULE_1_PERIOD, periodCount: 2n, perPeriod: 50n * nominal}; - const schedule2 = {start: SCHEDULE_2_START, period: SCHEDULE_2_PERIOD, periodCount: 2n, perPeriod: 100n * nominal}; - - // act - await helper.balance.vestedTransfer(sender, recepient.address, schedule1); - await helper.balance.vestedTransfer(sender, recepient.address, schedule2); - let schedule = await helper.balance.getVestingSchedules(recepient.address); - - // check senders balance after vesting: - let balanceSender = await helper.balance.getSubstrateFull(sender.address); - expect(balanceSender.free / nominal).to.eq(699n); - expect(balanceSender.frozen).to.eq(0n); - expect(balanceSender.reserved).to.eq(0n); - - // check recepient balance after vesting: - let balanceRecepient = await helper.balance.getSubstrateFull(recepient.address); - expect(balanceRecepient.free).to.eq(301n * nominal); - expect(balanceRecepient.frozen).to.eq(300n * nominal); - expect(balanceRecepient.reserved).to.eq(0n); - - // Schedules list correct: - expect(schedule).to.has.length(2); - expect(schedule[0]).to.deep.eq(schedule1); - expect(schedule[1]).to.deep.eq(schedule2); - - // Wait first part available: - await helper.wait.forRelayBlockNumber(SCHEDULE_1_START + SCHEDULE_1_PERIOD); - await helper.balance.claim(recepient); - - // check recepient balance after claim (50 tokens claimed, 250 left): - balanceRecepient = await helper.balance.getSubstrateFull(recepient.address); - expect(balanceRecepient.free / nominal).to.eq(300n); - expect(balanceRecepient.frozen).to.eq(250n * nominal); - expect(balanceRecepient.reserved).to.eq(0n); - - // Wait first schedule ends and first part od second schedule: - await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD); - await helper.balance.claim(recepient); - - // check recepient balance after second claim (150 tokens claimed, 100 left): - balanceRecepient = await helper.balance.getSubstrateFull(recepient.address); - expect(balanceRecepient.free / nominal).to.eq(300n); - expect(balanceRecepient.frozen).to.eq(100n * nominal); - expect(balanceRecepient.reserved).to.eq(0n); - - // Schedules list contain 1 vesting: - schedule = await helper.balance.getVestingSchedules(recepient.address); - expect(schedule).to.has.length(1); - expect(schedule[0]).to.deep.eq(schedule2); - - // Wait 2 schedule ends: - await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD * 2n); - await helper.balance.claim(recepient); - - // check recepient balance after second claim (100 tokens claimed, 0 left): - balanceRecepient = await helper.balance.getSubstrateFull(recepient.address); - expect(balanceRecepient.free / nominal).to.eq(300n); - expect(balanceRecepient.frozen).to.eq(0n); - expect(balanceRecepient.reserved).to.eq(0n); - - // check sender balance does not changed: - balanceSender = await helper.balance.getSubstrateFull(sender.address); - expect(balanceSender.free / nominal).to.eq(699n); - expect(balanceSender.frozen).to.eq(0n); - expect(balanceSender.reserved).to.eq(0n); - }); - - itSub('cannot send more tokens than have', async ({helper}) => { - const [sender, receiver] = await helper.arrange.createAccounts([1000n, 1n], donor); - const schedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 100n * nominal}; - const manyPeriodsSchedule = {start: 0n, period: 1n, periodCount: 100n, perPeriod: 10n * nominal}; - const oneBigSumSchedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 5000n * nominal}; - - // Sender cannot send vestedTransfer to self or other - await expect(helper.balance.vestedTransfer(sender, sender.address, manyPeriodsSchedule)).to.be.rejectedWith(/^vesting.InsufficientBalanceToLock$/); - await expect(helper.balance.vestedTransfer(sender, receiver.address, manyPeriodsSchedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/); - await expect(helper.balance.vestedTransfer(sender, sender.address, oneBigSumSchedule)).to.be.rejectedWith(/^vesting.InsufficientBalanceToLock$/); - await expect(helper.balance.vestedTransfer(sender, receiver.address, oneBigSumSchedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/); - - const balanceSender = await helper.balance.getSubstrateFull(sender.address); - const balanceReceiver = await helper.balance.getSubstrateFull(receiver.address); - - // Sender's balance has not changed - expect(balanceSender.free / nominal).to.eq(999n); - expect(balanceSender.frozen).to.eq(0n); - expect(balanceSender.reserved).to.eq(0n); - - // Receiver's balance has not changed - expect(balanceReceiver.free).to.be.eq(1n * nominal); - expect(balanceReceiver.frozen).to.be.eq(0n); - expect(balanceReceiver.reserved).to.be.eq(0n); - - // Receiver cannot send vestedTransfer back because of freeze - await expect(helper.balance.vestedTransfer(receiver, sender.address, schedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/); - }); - - itSub('cannot send vestedTransfer with incorrect parameters', async ({helper}) => { - const [sender, receiver] = await helper.arrange.createAccounts([1000n, 1n], donor); - const incorrectperiodSchedule = {start: 0n, period: 0n, periodCount: 10n, perPeriod: 10n * nominal}; - const incorrectPeriodCountSchedule = {start: 0n, period: 1n, periodCount: 0n, perPeriod: 10n * nominal}; - const incorrectPerPeriodSchedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 0n * nominal}; - - await expect(helper.balance.vestedTransfer(sender, sender.address, incorrectperiodSchedule)).to.be.rejectedWith(/vesting.ZeroVestingPeriod/); - await expect(helper.balance.vestedTransfer(sender, receiver.address, incorrectPeriodCountSchedule)).to.be.rejectedWith(/vesting.ZeroVestingPeriod/); - await expect(helper.balance.vestedTransfer(sender, receiver.address, incorrectPerPeriodSchedule)).to.be.rejectedWith(/vesting.AmountLow/); - - const balanceSender = await helper.balance.getSubstrateFull(sender.address); - // Sender's balance has not changed - expect(balanceSender.free / nominal).to.eq(999n); - expect(balanceSender.frozen).to.eq(0n); - expect(balanceSender.reserved).to.eq(0n); - }); -}); --- a/js-packages/tests/src/xcm/lowLevelXcmQuartz.test.ts +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util/index.js'; -import {QUARTZ_CHAIN, QTZ_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, shidenUrl, SAFE_XCM_VERSION, XcmTestHelper, TRANSFER_AMOUNT, SENDER_BUDGET, relayUrl} from './xcm.types.js'; -import {hexToString} from '@polkadot/util'; - -const testHelper = new XcmTestHelper('quartz'); - -describeXCM('[XCMLL] Integration test: Exchanging tokens with Karura', () => { - let alice: IKeyringPair; - let randomAccount: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - [randomAccount] = await helper.arrange.createAccounts([0n], alice); - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingKaruraPlaygrounds(karuraUrl, async (helper) => { - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }; - - const metadata = { - name: 'Quartz', - symbol: 'QTZ', - decimals: 18, - minimalBalance: 1000000000000000000n, - }; - - const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) => - hexToString(v.toJSON()['symbol'])) as string[]; - - if(!assets.includes('QTZ')) { - await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata); - } else { - console.log('QTZ token already registered on Karura assetRegistry pallet'); - } - await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); - }); - - await usingPlaygrounds(async (helper) => { - await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET); - }); - }); - - itSub('Should connect and send QTZ to Karura', async () => { - await testHelper.sendUnqTo('karura', randomAccount); - }); - - itSub('Should connect to Karura and send QTZ back', async () => { - await testHelper.sendUnqBack('karura', alice, randomAccount); - }); - - itSub('Karura can send only up to its balance', async () => { - await testHelper.sendOnlyOwnedBalance('karura', alice); - }); -}); -// These tests are relevant only when -// the the corresponding foreign assets are not registered -describeXCM('[XCMLL] Integration test: Quartz rejects non-native tokens', () => { - let alice: IKeyringPair; - - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - - - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - }); - - itSub('Quartz rejects KAR tokens from Karura', async () => { - await testHelper.rejectNativeTokensFrom('karura', alice); - }); - - itSub('Quartz rejects MOVR tokens from Moonriver', async () => { - await testHelper.rejectNativeTokensFrom('moonriver', alice); - }); - - itSub('Quartz rejects SDN tokens from Shiden', async () => { - await testHelper.rejectNativeTokensFrom('shiden', alice); - }); -}); - -describeXCM('[XCMLL] Integration test: Exchanging QTZ with Moonriver', () => { - // Quartz constants - let alice: IKeyringPair; - let quartzAssetLocation; - - let randomAccountQuartz: IKeyringPair; - let randomAccountMoonriver: IKeyringPair; - - // Moonriver constants - let assetId: string; - - const quartzAssetMetadata = { - name: 'xcQuartz', - symbol: 'xcQTZ', - decimals: 18, - isFrozen: false, - minimalBalance: 1n, - }; - - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice); - - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { - const alithAccount = helper.account.alithAccount(); - const baltatharAccount = helper.account.baltatharAccount(); - const dorothyAccount = helper.account.dorothyAccount(); - - randomAccountMoonriver = helper.account.create(); - - // >>> Sponsoring Dorothy >>> - console.log('Sponsoring Dorothy.......'); - await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n); - console.log('Sponsoring Dorothy.......DONE'); - // <<< Sponsoring Dorothy <<< - - quartzAssetLocation = { - XCM: { - parents: 1, - interior: {X1: {Parachain: QUARTZ_CHAIN}}, - }, - }; - const existentialDeposit = 1n; - const isSufficient = true; - const unitsPerSecond = 1n; - const numAssetsWeightHint = 0; - if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) { - console.log('Quartz asset already registered on Moonriver'); - } else { - const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({ - location: quartzAssetLocation, - metadata: quartzAssetMetadata, - existentialDeposit, - isSufficient, - unitsPerSecond, - numAssetsWeightHint, - }); - - console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal); - - await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal); - } - // >>> Acquire Quartz AssetId Info on Moonriver >>> - console.log('Acquire Quartz AssetId Info on Moonriver.......'); - - assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString(); - - console.log('QTZ asset ID is %s', assetId); - console.log('Acquire Quartz AssetId Info on Moonriver.......DONE'); - // >>> Acquire Quartz AssetId Info on Moonriver >>> - - // >>> Sponsoring random Account >>> - console.log('Sponsoring random Account.......'); - await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n); - console.log('Sponsoring random Account.......DONE'); - // <<< Sponsoring random Account <<< - }); - - await usingPlaygrounds(async (helper) => { - await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT); - }); - }); - - itSub('Should connect and send QTZ to Moonriver', async () => { - await testHelper.sendUnqTo('moonriver', randomAccountQuartz, randomAccountMoonriver); - }); - - itSub('Should connect to Moonriver and send QTZ back', async () => { - await testHelper.sendUnqBack('moonriver', alice, randomAccountQuartz); - }); - - itSub('Moonriver can send only up to its balance', async () => { - await testHelper.sendOnlyOwnedBalance('moonriver', alice); - }); - - itSub('Should not accept reserve transfer of QTZ from Moonriver', async () => { - await testHelper.rejectReserveTransferUNQfrom('moonriver', alice); - }); -}); - -describeXCM('[XCMLL] Integration test: Exchanging tokens with Shiden', () => { - let alice: IKeyringPair; - let randomAccount: IKeyringPair; - - const QTZ_ASSET_ID_ON_SHIDEN = 18_446_744_073_709_551_633n; // The value is taken from the live Shiden - const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n; // The value is taken from the live Shiden - - // Quartz -> Shiden - const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden - const unitsPerSecond = 500_451_000_000_000_000_000n; // The value is taken from the live Shiden - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - randomAccount = helper.arrange.createEmptyAccount(); - await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET); - console.log('sender: ', randomAccount.address); - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingShidenPlaygrounds(shidenUrl, async (helper) => { - if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) { - console.log('1. Create foreign asset and metadata'); - await helper.getSudo().assets.forceCreate( - alice, - QTZ_ASSET_ID_ON_SHIDEN, - alice.address, - QTZ_MINIMAL_BALANCE_ON_SHIDEN, - ); - - await helper.assets.setMetadata( - alice, - QTZ_ASSET_ID_ON_SHIDEN, - 'Quartz', - 'QTZ', - Number(QTZ_DECIMALS), - ); - - console.log('2. Register asset location on Shiden'); - const assetLocation = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }; - - await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]); - - console.log('3. Set QTZ payment for XCM execution on Shiden'); - await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]); - } else { - console.log('QTZ is already registered on Shiden'); - } - console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)'); - await helper.balance.transferToSubstrate(alice, randomAccount.address, shidenInitialBalance); - }); - }); - - itSub('Should connect and send QTZ to Shiden', async () => { - await testHelper.sendUnqTo('shiden', randomAccount); - }); - - itSub('Should connect to Shiden and send QTZ back', async () => { - await testHelper.sendUnqBack('shiden', alice, randomAccount); - }); - - itSub('Shiden can send only up to its balance', async () => { - await testHelper.sendOnlyOwnedBalance('shiden', alice); - }); - - itSub('Should not accept reserve transfer of QTZ from Shiden', async () => { - await testHelper.rejectReserveTransferUNQfrom('shiden', alice); - }); -}); - -describeXCM('[XCMLL] Integration test: The relay can do some root ops', () => { - let sudoer: IKeyringPair; - - before(async function () { - await usingRelayPlaygrounds(relayUrl, async (_, privateKey) => { - sudoer = await privateKey('//Alice'); - }); - }); - - // At the moment there is no reliable way - // to establish the correspondence between the `ExecutedDownward` event - // and the relay's sent message due to `SetTopic` instruction - // containing an unpredictable topic silently added by the relay's messages on the router level. - // This changes the message hash on arrival to our chain. - // - // See: - // * The relay's router: https://github.com/paritytech/polkadot-sdk/blob/f60318f68687e601c47de5ad5ca88e2c3f8139a7/polkadot/runtime/westend/src/xcm_config.rs#L83 - // * The `WithUniqueTopic` helper: https://github.com/paritytech/polkadot-sdk/blob/945ebbbcf66646be13d5b1d1bc26c8b0d3296d9e/polkadot/xcm/xcm-builder/src/routing.rs#L36 - // - // Because of this, we insert time gaps between tests so - // different `ExecutedDownward` events won't interfere with each other. - afterEach(async () => { - await usingPlaygrounds(async (helper) => { - await helper.wait.newBlocks(3); - }); - }); - - itSub('The relay can set storage', async () => { - await testHelper.relayIsPermittedToSetStorage(sudoer, 'plain'); - }); - - itSub('The relay can batch set storage', async () => { - await testHelper.relayIsPermittedToSetStorage(sudoer, 'batch'); - }); - - itSub('The relay can batchAll set storage', async () => { - await testHelper.relayIsPermittedToSetStorage(sudoer, 'batchAll'); - }); - - itSub('The relay can forceBatch set storage', async () => { - await testHelper.relayIsPermittedToSetStorage(sudoer, 'forceBatch'); - }); - - itSub('[negative] The relay cannot set balance', async () => { - await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'plain'); - }); - - itSub('[negative] The relay cannot set balance via batch', async () => { - await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batch'); - }); - - itSub('[negative] The relay cannot set balance via batchAll', async () => { - await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batchAll'); - }); - - itSub('[negative] The relay cannot set balance via forceBatch', async () => { - await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'forceBatch'); - }); - - itSub('[negative] The relay cannot set balance via dispatchAs', async () => { - await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'dispatchAs'); - }); -}); --- a/js-packages/tests/src/xcm/lowLevelXcmUnique.test.ts +++ /dev/null @@ -1,430 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import config from '../config.js'; -import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds} from '../util/index.js'; -import {nToBigInt} from '@polkadot/util'; -import {hexToString} from '@polkadot/util'; -import {ASTAR_DECIMALS, SAFE_XCM_VERSION, SENDER_BUDGET, UNIQUE_CHAIN, UNQ_DECIMALS, XcmTestHelper, acalaUrl, astarUrl, moonbeamUrl, polkadexUrl, relayUrl, uniqueAssetId} from './xcm.types.js'; - -const testHelper = new XcmTestHelper('unique'); - - - - -describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => { - let alice: IKeyringPair; - let randomAccount: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - console.log(config.acalaUrl); - randomAccount = helper.arrange.createEmptyAccount(); - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingAcalaPlaygrounds(acalaUrl, async (helper) => { - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }, - }; - - const metadata = { - name: 'Unique Network', - symbol: 'UNQ', - decimals: 18, - minimalBalance: 1250_000_000_000_000_000n, - }; - const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) => - hexToString(v.toJSON()['symbol'])) as string[]; - - if(!assets.includes('UNQ')) { - await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata); - } else { - console.log('UNQ token already registered on Acala assetRegistry pallet'); - } - await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); - }); - - await usingPlaygrounds(async (helper) => { - await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET); - }); - }); - - itSub('Should connect and send UNQ to Acala', async () => { - await testHelper.sendUnqTo('acala', randomAccount); - }); - - itSub('Should connect to Acala and send UNQ back', async () => { - await testHelper.sendUnqBack('acala', alice, randomAccount); - }); - - itSub('Acala can send only up to its balance', async () => { - await testHelper.sendOnlyOwnedBalance('acala', alice); - }); - - itSub('Should not accept reserve transfer of UNQ from Acala', async () => { - await testHelper.rejectReserveTransferUNQfrom('acala', alice); - }); -}); - -describeXCM('[XCMLL] Integration test: Exchanging tokens with Polkadex', () => { - let alice: IKeyringPair; - let randomAccount: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - randomAccount = helper.arrange.createEmptyAccount(); - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { - const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', [])) - .toJSON() as []) - .map(nToBigInt).length != 0; - /* - Check whether the Unique token has been added - to the whitelist, since an error will occur - if it is added again. Needed for debugging - when this test is run multiple times. - */ - if(isWhitelisted) { - console.log('UNQ token is already whitelisted on Polkadex'); - } else { - await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId); - } - - await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); - }); - - await usingPlaygrounds(async (helper) => { - await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET); - }); - }); - - itSub('Should connect and send UNQ to Polkadex', async () => { - await testHelper.sendUnqTo('polkadex', randomAccount); - }); - - - itSub('Should connect to Polkadex and send UNQ back', async () => { - await testHelper.sendUnqBack('polkadex', alice, randomAccount); - }); - - itSub('Polkadex can send only up to its balance', async () => { - await testHelper.sendOnlyOwnedBalance('polkadex', alice); - }); - - itSub('Should not accept reserve transfer of UNQ from Polkadex', async () => { - await testHelper.rejectReserveTransferUNQfrom('polkadex', alice); - }); -}); - -// These tests are relevant only when -// the the corresponding foreign assets are not registered -describeXCM('[XCMLL] Integration test: Unique rejects non-native tokens', () => { - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - }); - - itSub('Unique rejects ACA tokens from Acala', async () => { - await testHelper.rejectNativeTokensFrom('acala', alice); - }); - - itSub('Unique rejects GLMR tokens from Moonbeam', async () => { - await testHelper.rejectNativeTokensFrom('moonbeam', alice); - }); - - itSub('Unique rejects ASTR tokens from Astar', async () => { - await testHelper.rejectNativeTokensFrom('astar', alice); - }); - - itSub('Unique rejects PDX tokens from Polkadex', async () => { - await testHelper.rejectNativeTokensFrom('polkadex', alice); - }); -}); - -describeXCM('[XCMLL] Integration test: Exchanging UNQ with Moonbeam', () => { - // Unique constants - let alice: IKeyringPair; - let uniqueAssetLocation; - - let randomAccountUnique: IKeyringPair; - let randomAccountMoonbeam: IKeyringPair; - - // Moonbeam constants - let assetId: string; - - const uniqueAssetMetadata = { - name: 'xcUnique', - symbol: 'xcUNQ', - decimals: 18, - isFrozen: false, - minimalBalance: 1n, - }; - - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice); - - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { - const alithAccount = helper.account.alithAccount(); - const baltatharAccount = helper.account.baltatharAccount(); - const dorothyAccount = helper.account.dorothyAccount(); - - randomAccountMoonbeam = helper.account.create(); - - // >>> Sponsoring Dorothy >>> - console.log('Sponsoring Dorothy.......'); - await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n); - console.log('Sponsoring Dorothy.......DONE'); - // <<< Sponsoring Dorothy <<< - uniqueAssetLocation = { - XCM: { - parents: 1, - interior: {X1: {Parachain: UNIQUE_CHAIN}}, - }, - }; - const existentialDeposit = 1n; - const isSufficient = true; - const unitsPerSecond = 1n; - const numAssetsWeightHint = 0; - - if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) { - console.log('Unique asset already registered on Moonbeam'); - } else { - const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({ - location: uniqueAssetLocation, - metadata: uniqueAssetMetadata, - existentialDeposit, - isSufficient, - unitsPerSecond, - numAssetsWeightHint, - }); - - console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal); - - await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal); - } - - // >>> Acquire Unique AssetId Info on Moonbeam >>> - console.log('Acquire Unique AssetId Info on Moonbeam.......'); - - assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString(); - - console.log('UNQ asset ID is %s', assetId); - console.log('Acquire Unique AssetId Info on Moonbeam.......DONE'); - - // >>> Sponsoring random Account >>> - console.log('Sponsoring random Account.......'); - await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n); - console.log('Sponsoring random Account.......DONE'); - // <<< Sponsoring random Account <<< - }); - - await usingPlaygrounds(async (helper) => { - await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, SENDER_BUDGET); - }); - }); - - itSub('Should connect and send UNQ to Moonbeam', async () => { - await testHelper.sendUnqTo('moonbeam', randomAccountUnique, randomAccountMoonbeam); - }); - - itSub('Should connect to Moonbeam and send UNQ back', async () => { - await testHelper.sendUnqBack('moonbeam', alice, randomAccountUnique); - }); - - itSub('Moonbeam can send only up to its balance', async () => { - await testHelper.sendOnlyOwnedBalance('moonbeam', alice); - }); - - itSub('Should not accept reserve transfer of UNQ from Moonbeam', async () => { - await testHelper.rejectReserveTransferUNQfrom('moonbeam', alice); - }); -}); - -describeXCM('[XCMLL] Integration test: Exchanging tokens with Astar', () => { - let alice: IKeyringPair; - let randomAccount: IKeyringPair; - - const UNQ_ASSET_ID_ON_ASTAR = 18_446_744_073_709_551_631n; // The value is taken from the live Astar - const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n; // The value is taken from the live Astar - - // Unique -> Astar - const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar. - const unitsPerSecond = 9_451_000_000_000_000_000n; // The value is taken from the live Astar - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - randomAccount = helper.arrange.createEmptyAccount(); - await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET); - console.log('randomAccount', randomAccount.address); - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingAstarPlaygrounds(astarUrl, async (helper) => { - if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) { - console.log('1. Create foreign asset and metadata'); - await helper.getSudo().assets.forceCreate( - alice, - UNQ_ASSET_ID_ON_ASTAR, - alice.address, - UNQ_MINIMAL_BALANCE_ON_ASTAR, - ); - - await helper.assets.setMetadata( - alice, - UNQ_ASSET_ID_ON_ASTAR, - 'Unique Network', - 'UNQ', - Number(UNQ_DECIMALS), - ); - - console.log('2. Register asset location on Astar'); - const assetLocation = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }, - }; - - await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]); - - console.log('3. Set UNQ payment for XCM execution on Astar'); - await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]); - } else { - console.log('UNQ is already registered on Astar'); - } - console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)'); - await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance); - }); - }); - - itSub('Should connect and send UNQ to Astar', async () => { - await testHelper.sendUnqTo('astar', randomAccount); - }); - - itSub('Should connect to Astar and send UNQ back', async () => { - await testHelper.sendUnqBack('astar', alice, randomAccount); - }); - - itSub('Astar can send only up to its balance', async () => { - await testHelper.sendOnlyOwnedBalance('astar', alice); - }); - - itSub('Should not accept reserve transfer of UNQ from Astar', async () => { - await testHelper.rejectReserveTransferUNQfrom('astar', alice); - }); -}); - -describeXCM('[XCMLL] Integration test: The relay can do some root ops', () => { - let sudoer: IKeyringPair; - - before(async function () { - await usingRelayPlaygrounds(relayUrl, async (_, privateKey) => { - sudoer = await privateKey('//Alice'); - }); - }); - - // At the moment there is no reliable way - // to establish the correspondence between the `ExecutedDownward` event - // and the relay's sent message due to `SetTopic` instruction - // containing an unpredictable topic silently added by the relay's messages on the router level. - // This changes the message hash on arrival to our chain. - // - // See: - // * The relay's router: https://github.com/paritytech/polkadot-sdk/blob/f60318f68687e601c47de5ad5ca88e2c3f8139a7/polkadot/runtime/westend/src/xcm_config.rs#L83 - // * The `WithUniqueTopic` helper: https://github.com/paritytech/polkadot-sdk/blob/945ebbbcf66646be13d5b1d1bc26c8b0d3296d9e/polkadot/xcm/xcm-builder/src/routing.rs#L36 - // - // Because of this, we insert time gaps between tests so - // different `ExecutedDownward` events won't interfere with each other. - afterEach(async () => { - await usingPlaygrounds(async (helper) => { - await helper.wait.newBlocks(3); - }); - }); - - itSub('The relay can set storage', async () => { - await testHelper.relayIsPermittedToSetStorage(sudoer, 'plain'); - }); - - itSub('The relay can batch set storage', async () => { - await testHelper.relayIsPermittedToSetStorage(sudoer, 'batch'); - }); - - itSub('The relay can batchAll set storage', async () => { - await testHelper.relayIsPermittedToSetStorage(sudoer, 'batchAll'); - }); - - itSub('The relay can forceBatch set storage', async () => { - await testHelper.relayIsPermittedToSetStorage(sudoer, 'forceBatch'); - }); - - itSub('[negative] The relay cannot set balance', async () => { - await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'plain'); - }); - - itSub('[negative] The relay cannot set balance via batch', async () => { - await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batch'); - }); - - itSub('[negative] The relay cannot set balance via batchAll', async () => { - await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batchAll'); - }); - - itSub('[negative] The relay cannot set balance via forceBatch', async () => { - await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'forceBatch'); - }); - - itSub('[negative] The relay cannot set balance via dispatchAs', async () => { - await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'dispatchAs'); - }); -}); --- a/js-packages/tests/src/xcm/xcm.types.ts +++ /dev/null @@ -1,621 +0,0 @@ -import type {IKeyringPair} from '@polkadot/types/types'; -import {hexToString} from '@polkadot/util'; -import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '../util/index.js'; -import {DevUniqueHelper, Event} from '@unique/playgrounds/src/unique.dev.js'; -import config from '../config.js'; - -export const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037); -export const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000); -export const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000); -export const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004); -export const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006); -export const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040); - -export const QUARTZ_CHAIN = +(process.env.RELAY_QUARTZ_ID || 2095); -export const STATEMINE_CHAIN = +(process.env.RELAY_STATEMINE_ID || 1000); -export const KARURA_CHAIN = +(process.env.RELAY_KARURA_ID || 2000); -export const MOONRIVER_CHAIN = +(process.env.RELAY_MOONRIVER_ID || 2023); -export const SHIDEN_CHAIN = +(process.env.RELAY_SHIDEN_ID || 2007); - -export const relayUrl = config.relayUrl; -export const statemintUrl = config.statemintUrl; -export const statemineUrl = config.statemineUrl; - -export const acalaUrl = config.acalaUrl; -export const moonbeamUrl = config.moonbeamUrl; -export const astarUrl = config.astarUrl; -export const polkadexUrl = config.polkadexUrl; - -export const karuraUrl = config.karuraUrl; -export const moonriverUrl = config.moonriverUrl; -export const shidenUrl = config.shidenUrl; - -export const SAFE_XCM_VERSION = 3; - - -export const RELAY_DECIMALS = 12; -export const STATEMINE_DECIMALS = 12; -export const KARURA_DECIMALS = 12; -export const SHIDEN_DECIMALS = 18n; -export const QTZ_DECIMALS = 18n; - -export const ASTAR_DECIMALS = 18n; -export const UNQ_DECIMALS = 18n; - -export const maxWaitBlocks = 6; - -export const uniqueMultilocation = { - parents: 1, - interior: { - X1: { - Parachain: UNIQUE_CHAIN, - }, - }, -}; -export const uniqueVersionedMultilocation = { - V3: uniqueMultilocation, -}; - -export const uniqueAssetId = { - Concrete: uniqueMultilocation, -}; - -export const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => { - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash - && event.outcome.isFailedToTransactAsset); -}; -export const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => { - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash - && event.outcome.isUntrustedReserveLocation); -}; - -export const expectDownwardXcmNoPermission = async (helper: DevUniqueHelper) => { - // The correct messageHash for downward messages can't be reliably obtained - await helper.wait.expectEvent(maxWaitBlocks, Event.DmpQueue.ExecutedDownward, event => event.outcome.asIncomplete[1].isNoPermission); -}; - -export const expectDownwardXcmComplete = async (helper: DevUniqueHelper) => { - // The correct messageHash for downward messages can't be reliably obtained - await helper.wait.expectEvent(maxWaitBlocks, Event.DmpQueue.ExecutedDownward, event => event.outcome.isComplete); -}; - -export const NETWORKS = { - acala: usingAcalaPlaygrounds, - astar: usingAstarPlaygrounds, - polkadex: usingPolkadexPlaygrounds, - moonbeam: usingMoonbeamPlaygrounds, - moonriver: usingMoonriverPlaygrounds, - karura: usingKaruraPlaygrounds, - shiden: usingShidenPlaygrounds, -} as const; -type NetworkNames = keyof typeof NETWORKS; - -type NativeRuntime = 'opal' | 'quartz' | 'unique'; - -export function mapToChainId(networkName: keyof typeof NETWORKS): number { - switch (networkName) { - case 'acala': - return ACALA_CHAIN; - case 'astar': - return ASTAR_CHAIN; - case 'moonbeam': - return MOONBEAM_CHAIN; - case 'polkadex': - return POLKADEX_CHAIN; - case 'moonriver': - return MOONRIVER_CHAIN; - case 'karura': - return KARURA_CHAIN; - case 'shiden': - return SHIDEN_CHAIN; - } -} - -export function mapToChainUrl(networkName: NetworkNames): string { - switch (networkName) { - case 'acala': - return acalaUrl; - case 'astar': - return astarUrl; - case 'moonbeam': - return moonbeamUrl; - case 'polkadex': - return polkadexUrl; - case 'moonriver': - return moonriverUrl; - case 'karura': - return karuraUrl; - case 'shiden': - return shidenUrl; - } -} - -export function getDevPlayground(name: NetworkNames) { - return NETWORKS[name]; -} - -export const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n; -export const SENDER_BUDGET = 2n * TRANSFER_AMOUNT; -export const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n; -export const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT; -export const TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT = 100_000_000_000n; - -export class XcmTestHelper { - private _balanceUniqueTokenInit: bigint = 0n; - private _balanceUniqueTokenMiddle: bigint = 0n; - private _balanceUniqueTokenFinal: bigint = 0n; - private _unqFees: bigint = 0n; - private _nativeRuntime: NativeRuntime; - - constructor(runtime: NativeRuntime) { - this._nativeRuntime = runtime; - } - - private _getNativeId() { - switch (this._nativeRuntime) { - case 'opal': - // To-Do - return 1001; - case 'quartz': - return QUARTZ_CHAIN; - case 'unique': - return UNIQUE_CHAIN; - } - } - - private _isAddress20FormatFor(network: NetworkNames) { - switch (network) { - case 'moonbeam': - case 'moonriver': - return true; - default: - return false; - } - } - - private _runtimeVersionedMultilocation() { - return { - V3: { - parents: 1, - interior: { - X1: { - Parachain: this._getNativeId(), - }, - }, - }, - }; - } - - private _uniqueChainMultilocationForRelay() { - return { - V3: { - parents: 0, - interior: { - X1: {Parachain: this._getNativeId()}, - }, - }, - }; - } - - async sendUnqTo( - networkName: keyof typeof NETWORKS, - randomAccount: IKeyringPair, - randomAccountOnTargetChain = randomAccount, - ) { - const networkUrl = mapToChainUrl(networkName); - const targetPlayground = getDevPlayground(networkName); - await usingPlaygrounds(async (helper) => { - this._balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address); - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: mapToChainId(networkName), - }, - }, - }, - }; - - const beneficiary = { - V2: { - parents: 0, - interior: { - X1: ( - this._isAddress20FormatFor(networkName) ? - { - AccountKey20: { - network: 'Any', - key: randomAccountOnTargetChain.address, - }, - } - : - { - AccountId32: { - network: 'Any', - id: randomAccountOnTargetChain.addressRaw, - }, - } - ), - }, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT, - }, - }, - ], - }; - const feeAssetItem = 0; - - await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - this._balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); - - this._unqFees = this._balanceUniqueTokenInit - this._balanceUniqueTokenMiddle - TRANSFER_AMOUNT; - console.log('[%s -> %s] transaction fees: %s', this._nativeRuntime, networkName, helper.util.bigIntToDecimals(this._unqFees)); - expect(this._unqFees > 0n, 'Negative fees, looks like nothing was transferred').to.be.true; - - await targetPlayground(networkUrl, async (helper) => { - /* - Since only the parachain part of the Polkadex - infrastructure is launched (without their - solochain validators), processing incoming - assets will lead to an error. - This error indicates that the Polkadex chain - received a message from the Unique network, - since the hash is being checked to ensure - it matches what was sent. - */ - if(networkName == 'polkadex') { - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash); - } else { - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == messageSent.messageHash); - } - }); - - }); - } - - async sendUnqBack( - networkName: keyof typeof NETWORKS, - sudoer: IKeyringPair, - randomAccountOnUnq: IKeyringPair, - ) { - const networkUrl = mapToChainUrl(networkName); - - const targetPlayground = getDevPlayground(networkName); - await usingPlaygrounds(async (helper) => { - - const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - randomAccountOnUnq.addressRaw, - { - Concrete: { - parents: 1, - interior: { - X1: {Parachain: this._getNativeId()}, - }, - }, - }, - SENDBACK_AMOUNT, - ); - - let xcmProgramSent: any; - - - await targetPlayground(networkUrl, async (helper) => { - if('getSudo' in helper) { - await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), xcmProgram); - xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - } else if('fastDemocracy' in helper) { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), xcmProgram]); - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall); - xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - } - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash); - - this._balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountOnUnq.address); - - expect(this._balanceUniqueTokenFinal).to.be.equal(this._balanceUniqueTokenInit - this._unqFees - STAYED_ON_TARGET_CHAIN); - - }); - } - - async sendOnlyOwnedBalance( - networkName: keyof typeof NETWORKS, - sudoer: IKeyringPair, - ) { - const networkUrl = mapToChainUrl(networkName); - const targetPlayground = getDevPlayground(networkName); - - const targetChainBalance = 10000n * (10n ** UNQ_DECIMALS); - - await usingPlaygrounds(async (helper) => { - const targetChainSovereignAccount = helper.address.paraSiblingSovereignAccount(mapToChainId(networkName)); - await helper.getSudo().balance.setBalanceSubstrate(sudoer, targetChainSovereignAccount, targetChainBalance); - const moreThanTargetChainHas = 2n * targetChainBalance; - - const targetAccount = helper.arrange.createEmptyAccount(); - - const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - moreThanTargetChainHas, - ); - - let maliciousXcmProgramSent: any; - - - await targetPlayground(networkUrl, async (helper) => { - if('getSudo' in helper) { - await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgram); - maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - } else if('fastDemocracy' in helper) { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgram]); - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall); - maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - } - }); - - await expectFailedToTransact(helper, maliciousXcmProgramSent); - - const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(0n); - }); - } - - async rejectReserveTransferUNQfrom(networkName: keyof typeof NETWORKS, sudoer: IKeyringPair) { - const networkUrl = mapToChainUrl(networkName); - const targetPlayground = getDevPlayground(networkName); - - await usingPlaygrounds(async (helper) => { - const testAmount = 10_000n * (10n ** UNQ_DECIMALS); - const targetAccount = helper.arrange.createEmptyAccount(); - - const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 1, - interior: { - X1: { - Parachain: this._getNativeId(), - }, - }, - }, - }, - testAmount, - ); - - const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - testAmount, - ); - - let maliciousXcmProgramFullIdSent: any; - let maliciousXcmProgramHereIdSent: any; - const maxWaitBlocks = 3; - - // Try to trick Unique using full UNQ identification - await targetPlayground(networkUrl, async (helper) => { - if('getSudo' in helper) { - await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId); - maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - } - // Moonbeam case - else if('fastDemocracy' in helper) { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]); - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal(`${networkName} try to act like a reserve location for UNQ using path asset identification`,batchCall); - - maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - } - }); - - - await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent); - - let accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - - // Try to trick Unique using shortened UNQ identification - await targetPlayground(networkUrl, async (helper) => { - if('getSudo' in helper) { - await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId); - maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - } - else if('fastDemocracy' in helper) { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId]); - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal(`${networkName} try to act like a reserve location for UNQ using "here" asset identification`, batchCall); - - maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - } - }); - - await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent); - - accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - }); - } - - async rejectNativeTokensFrom(networkName: keyof typeof NETWORKS, sudoerOnTargetChain: IKeyringPair) { - const networkUrl = mapToChainUrl(networkName); - const targetPlayground = getDevPlayground(networkName); - let messageSent: any; - - await usingPlaygrounds(async (helper) => { - const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( - helper.arrange.createEmptyAccount().addressRaw, - { - Concrete: { - parents: 1, - interior: { - X1: { - Parachain: mapToChainId(networkName), - }, - }, - }, - }, - TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT, - ); - await targetPlayground(networkUrl, async (helper) => { - if('getSudo' in helper) { - await helper.getSudo().xcm.send(sudoerOnTargetChain, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId); - messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - } else if('fastDemocracy' in helper) { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]); - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal(`${networkName} sending native tokens to the Unique via fast democracy`, batchCall); - - messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - } - }); - await expectFailedToTransact(helper, messageSent); - }); - } - - private async _relayXcmTransactSetStorage(variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch') { - // eslint-disable-next-line require-await - return await usingPlaygrounds(async (helper) => { - const relayForceKV = () => { - const random = Math.random(); - const key = `relay-forced-key (instance: ${random})`; - const val = `relay-forced-value (instance: ${random})`; - const call = helper.constructApiCall('api.tx.system.setStorage', [[[key, val]]]).method.toHex(); - - return { - call, - key, - val, - }; - }; - - if(variant == 'plain') { - const kv = relayForceKV(); - return { - program: helper.arrange.makeUnpaidSudoTransactProgram({ - weightMultiplier: 1, - call: kv.call, - }), - kvs: [kv], - }; - } else { - const kv0 = relayForceKV(); - const kv1 = relayForceKV(); - - const batchCall = helper.constructApiCall(`api.tx.utility.${variant}`, [[kv0.call, kv1.call]]).method.toHex(); - return { - program: helper.arrange.makeUnpaidSudoTransactProgram({ - weightMultiplier: 2, - call: batchCall, - }), - kvs: [kv0, kv1], - }; - } - }); - } - - async relayIsPermittedToSetStorage(relaySudoer: IKeyringPair, variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch') { - const {program, kvs} = await this._relayXcmTransactSetStorage(variant); - - await usingRelayPlaygrounds(relayUrl, async (helper) => { - await helper.getSudo().executeExtrinsic(relaySudoer, 'api.tx.xcmPallet.send', [ - this._uniqueChainMultilocationForRelay(), - program, - ]); - }); - - await usingPlaygrounds(async (helper) => { - await expectDownwardXcmComplete(helper); - - for(const kv of kvs) { - const forcedValue = await helper.callRpc('api.rpc.state.getStorage', [kv.key]); - expect(hexToString(forcedValue.toHex())).to.be.equal(kv.val); - } - }); - } - - private async _relayXcmTransactSetBalance(variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch' | 'dispatchAs') { - // eslint-disable-next-line require-await - return await usingPlaygrounds(async (helper) => { - const emptyAccount = helper.arrange.createEmptyAccount().address; - - const forceSetBalanceCall = helper.constructApiCall('api.tx.balances.forceSetBalance', [emptyAccount, 10_000n]).method.toHex(); - - let call; - - if(variant == 'plain') { - call = forceSetBalanceCall; - - } else if(variant == 'dispatchAs') { - call = helper.constructApiCall('api.tx.utility.dispatchAs', [ - { - system: 'Root', - }, - forceSetBalanceCall, - ]).method.toHex(); - } else { - call = helper.constructApiCall(`api.tx.utility.${variant}`, [[forceSetBalanceCall]]).method.toHex(); - } - - return { - program: helper.arrange.makeUnpaidSudoTransactProgram({ - weightMultiplier: 1, - call, - }), - emptyAccount, - }; - }); - } - - async relayIsNotPermittedToSetBalance( - relaySudoer: IKeyringPair, - variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch' | 'dispatchAs', - ) { - const {program, emptyAccount} = await this._relayXcmTransactSetBalance(variant); - - await usingRelayPlaygrounds(relayUrl, async (helper) => { - await helper.getSudo().executeExtrinsic(relaySudoer, 'api.tx.xcmPallet.send', [ - this._uniqueChainMultilocationForRelay(), - program, - ]); - }); - - await usingPlaygrounds(async (helper) => { - await expectDownwardXcmNoPermission(helper); - expect(await helper.balance.getSubstrate(emptyAccount)).to.be.equal(0n); - }); - } -} --- a/js-packages/tests/src/xcm/xcmOpal.test.ts +++ /dev/null @@ -1,409 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import config from '../config.js'; -import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/index.js'; - -const STATEMINE_CHAIN = +(process.env.RELAY_WESTMINT_ID || 1000); -const UNIQUE_CHAIN = +(process.env.RELAY_OPAL_ID || 2095); - -const relayUrl = config.relayUrl; -const westmintUrl = config.westmintUrl; - -const STATEMINE_PALLET_INSTANCE = 50; -const ASSET_ID = 100; -const ASSET_METADATA_DECIMALS = 18; -const ASSET_METADATA_NAME = 'USDT'; -const ASSET_METADATA_DESCRIPTION = 'USDT'; -const ASSET_METADATA_MINIMAL_BALANCE = 1n; - -const RELAY_DECIMALS = 12; -const WESTMINT_DECIMALS = 12; - -const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n; - -// 10,000.00 (ten thousands) USDT -const ASSET_AMOUNT = 1_000_000_000_000_000_000_000n; - -describeXCM('[XCM] Integration test: Exchanging USDT with Westmint', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - let balanceStmnBefore: bigint; - let balanceStmnAfter: bigint; - - let balanceOpalBefore: bigint; - let balanceOpalAfter: bigint; - let balanceOpalFinal: bigint; - - let balanceBobBefore: bigint; - let balanceBobAfter: bigint; - let balanceBobFinal: bigint; - - let balanceBobRelayTokenBefore: bigint; - let balanceBobRelayTokenAfter: bigint; - - - before(async () => { - await usingPlaygrounds(async (_helper, privateKey) => { - alice = await privateKey('//Alice'); - bob = await privateKey('//Bob'); // funds donor - }); - - await usingWestmintPlaygrounds(westmintUrl, async (helper) => { - // 350.00 (three hundred fifty) DOT - const fundingAmount = 3_500_000_000_000n; - - await helper.assets.create(alice, ASSET_ID, alice.address, ASSET_METADATA_MINIMAL_BALANCE); - await helper.assets.setMetadata(alice, ASSET_ID, ASSET_METADATA_NAME, ASSET_METADATA_DESCRIPTION, ASSET_METADATA_DECIMALS); - await helper.assets.mint(alice, ASSET_ID, alice.address, ASSET_AMOUNT); - - // funding parachain sovereing account (Parachain: 2095) - const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN); - await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, fundingAmount); - }); - - - await usingPlaygrounds(async (helper) => { - const location = { - V2: { - parents: 1, - interior: {X3: [ - { - Parachain: STATEMINE_CHAIN, - }, - { - PalletInstance: STATEMINE_PALLET_INSTANCE, - }, - { - GeneralIndex: ASSET_ID, - }, - ]}, - }, - }; - - const metadata = - { - name: ASSET_ID, - symbol: ASSET_METADATA_NAME, - decimals: ASSET_METADATA_DECIMALS, - minimalBalance: ASSET_METADATA_MINIMAL_BALANCE, - }; - await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata); - balanceOpalBefore = await helper.balance.getSubstrate(alice.address); - }); - - - // Providing the relay currency to the unique sender account - await usingRelayPlaygrounds(relayUrl, async (helper) => { - const destination = { - V2: { - parents: 0, - interior: {X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }}; - - const beneficiary = { - V2: { - parents: 0, - interior: {X1: { - AccountId32: { - network: 'Any', - id: alice.addressRaw, - }, - }}, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: 50_000_000_000_000_000n, - }, - }, - ], - }; - - const feeAssetItem = 0; - - await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - }); - - }); - - itSub('Should connect and send USDT from Westmint to Opal', async ({helper}) => { - await usingWestmintPlaygrounds(westmintUrl, async (helper) => { - const dest = { - V2: { - parents: 1, - interior: {X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }}; - - const beneficiary = { - V2: { - parents: 0, - interior: {X1: { - AccountId32: { - network: 'Any', - id: alice.addressRaw, - }, - }}, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: { - X2: [ - { - PalletInstance: STATEMINE_PALLET_INSTANCE, - }, - { - GeneralIndex: ASSET_ID, - }, - ]}, - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT, - }, - }, - ], - }; - - const feeAssetItem = 0; - - balanceStmnBefore = await helper.balance.getSubstrate(alice.address); - await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited'); - - balanceStmnAfter = await helper.balance.getSubstrate(alice.address); - - // common good parachain take commission in it native token - console.log( - '[Westmint -> Opal] transaction fees on Westmint: %s WND', - helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS), - ); - expect(balanceStmnBefore > balanceStmnAfter).to.be.true; - - }); - - - // ensure that asset has been delivered - await helper.wait.newBlocks(3); - - // expext collection id will be with id 1 - const free = await helper.ft.getBalance(1, {Substrate: alice.address}); - - balanceOpalAfter = await helper.balance.getSubstrate(alice.address); - - console.log( - '[Westmint -> Opal] transaction fees on Opal: %s USDT', - helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, ASSET_METADATA_DECIMALS), - ); - console.log( - '[Westmint -> Opal] transaction fees on Opal: %s OPL', - helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore), - ); - - // commission has not paid in USDT token - expect(free == TRANSFER_AMOUNT).to.be.true; - // ... and parachain native token - expect(balanceOpalAfter == balanceOpalBefore).to.be.true; - }); - - itSub('Should connect and send USDT from Unique to Statemine back', async ({helper}) => { - const destination = { - V2: { - parents: 1, - interior: {X2: [ - { - Parachain: STATEMINE_CHAIN, - }, - { - AccountId32: { - network: 'Any', - id: alice.addressRaw, - }, - }, - ]}, - }, - }; - - const currencies: [any, bigint][] = [ - [ - { - ForeignAssetId: 0, - }, - //10_000_000_000_000_000n, - TRANSFER_AMOUNT, - ], - [ - { - NativeAssetId: 'Parent', - }, - 400_000_000_000_000n, - ], - ]; - - const feeItem = 1; - - await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited'); - - // the commission has been paid in parachain native token - balanceOpalFinal = await helper.balance.getSubstrate(alice.address); - expect(balanceOpalAfter > balanceOpalFinal).to.be.true; - - await usingWestmintPlaygrounds(westmintUrl, async (helper) => { - await helper.wait.newBlocks(3); - - // The USDT token never paid fees. Its amount not changed from begin value. - // Also check that xcm transfer has been succeeded - expect((await helper.assets.account(ASSET_ID, alice.address))! == ASSET_AMOUNT).to.be.true; - }); - }); - - itSub('Should connect and send Relay token to Unique', async ({helper}) => { - const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n; - - balanceBobBefore = await helper.balance.getSubstrate(bob.address); - balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); - - // Providing the relay currency to the unique sender account - await usingRelayPlaygrounds(relayUrl, async (helper) => { - const destination = { - V2: { - parents: 0, - interior: {X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }}; - - const beneficiary = { - V2: { - parents: 0, - interior: {X1: { - AccountId32: { - network: 'Any', - id: bob.addressRaw, - }, - }}, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT_RELAY, - }, - }, - ], - }; - - const feeAssetItem = 0; - - await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - }); - - await helper.wait.newBlocks(3); - - balanceBobAfter = await helper.balance.getSubstrate(bob.address); - balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); - - const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; - console.log( - 'Relay (Westend) to Opal transaction fees: %s OPL', - helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore), - ); - console.log( - 'Relay (Westend) to Opal transaction fees: %s WND', - helper.util.bigIntToDecimals(wndFee, WESTMINT_DECIMALS), - ); - expect(balanceBobBefore == balanceBobAfter).to.be.true; - expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true; - }); - - itSub('Should connect and send Relay token back', async ({helper}) => { - let relayTokenBalanceBefore: bigint; - let relayTokenBalanceAfter: bigint; - await usingRelayPlaygrounds(relayUrl, async (helper) => { - relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address); - }); - - const destination = { - V2: { - parents: 1, - interior: { - X1:{ - AccountId32: { - network: 'Any', - id: bob.addressRaw, - }, - }, - }, - }, - }; - - const currencies: any = [ - [ - { - NativeAssetId: 'Parent', - }, - 50_000_000_000_000_000n, - ], - ]; - - const feeItem = 0; - - await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited'); - - balanceBobFinal = await helper.balance.getSubstrate(bob.address); - console.log('[Opal -> Relay (Westend)] transaction fees: %s OPL', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal)); - - await usingRelayPlaygrounds(relayUrl, async (helper) => { - await helper.wait.newBlocks(10); - relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address); - - const diff = relayTokenBalanceAfter - relayTokenBalanceBefore; - console.log('[Opal -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS)); - expect(diff > 0, 'Relay tokens was not delivered back').to.be.true; - }); - }); -}); --- a/js-packages/tests/src/xcm/xcmQuartz.test.ts +++ /dev/null @@ -1,1636 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util/index.js'; -import {DevUniqueHelper, Event} from '@unique/playgrounds/src/unique.dev.js'; -import {STATEMINE_CHAIN, QUARTZ_CHAIN, KARURA_CHAIN, MOONRIVER_CHAIN, SHIDEN_CHAIN, STATEMINE_DECIMALS, KARURA_DECIMALS, QTZ_DECIMALS, RELAY_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, relayUrl, shidenUrl, statemineUrl} from './xcm.types.js'; -import {hexToString} from '@polkadot/util'; - - - -const STATEMINE_PALLET_INSTANCE = 50; - -const TRANSFER_AMOUNT = 2000000000000000000000000n; - -const FUNDING_AMOUNT = 3_500_000_0000_000_000n; - -const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n; - -const USDT_ASSET_ID = 100; -const USDT_ASSET_METADATA_DECIMALS = 18; -const USDT_ASSET_METADATA_NAME = 'USDT'; -const USDT_ASSET_METADATA_DESCRIPTION = 'USDT'; -const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n; -const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n; - -const SAFE_XCM_VERSION = 2; - -describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - let balanceStmnBefore: bigint; - let balanceStmnAfter: bigint; - - let balanceQuartzBefore: bigint; - let balanceQuartzAfter: bigint; - let balanceQuartzFinal: bigint; - - let balanceBobBefore: bigint; - let balanceBobAfter: bigint; - let balanceBobFinal: bigint; - - let balanceBobRelayTokenBefore: bigint; - let balanceBobRelayTokenAfter: bigint; - - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - bob = await privateKey('//Bob'); // sovereign account on Statemine(t) funds donor - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingRelayPlaygrounds(relayUrl, async (helper) => { - // Fund accounts on Statemine(t) - await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, alice.addressRaw, FUNDING_AMOUNT); - await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, bob.addressRaw, FUNDING_AMOUNT); - }); - - await usingStateminePlaygrounds(statemineUrl, async (helper) => { - const sovereignFundingAmount = 3_500_000_000n; - - await helper.assets.create( - alice, - USDT_ASSET_ID, - alice.address, - USDT_ASSET_METADATA_MINIMAL_BALANCE, - ); - await helper.assets.setMetadata( - alice, - USDT_ASSET_ID, - USDT_ASSET_METADATA_NAME, - USDT_ASSET_METADATA_DESCRIPTION, - USDT_ASSET_METADATA_DECIMALS, - ); - await helper.assets.mint( - alice, - USDT_ASSET_ID, - alice.address, - USDT_ASSET_AMOUNT, - ); - - // funding parachain sovereing account on Statemine(t). - // The sovereign account should be created before any action - // (the assets pallet on Statemine(t) check if the sovereign account exists) - const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(QUARTZ_CHAIN); - await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount); - }); - - - await usingPlaygrounds(async (helper) => { - const location = { - V2: { - parents: 1, - interior: {X3: [ - { - Parachain: STATEMINE_CHAIN, - }, - { - PalletInstance: STATEMINE_PALLET_INSTANCE, - }, - { - GeneralIndex: USDT_ASSET_ID, - }, - ]}, - }, - }; - - const metadata = - { - name: USDT_ASSET_ID, - symbol: USDT_ASSET_METADATA_NAME, - decimals: USDT_ASSET_METADATA_DECIMALS, - minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE, - }; - await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata); - balanceQuartzBefore = await helper.balance.getSubstrate(alice.address); - }); - - - // Providing the relay currency to the quartz sender account - // (fee for USDT XCM are paid in relay tokens) - await usingRelayPlaygrounds(relayUrl, async (helper) => { - const destination = { - V2: { - parents: 0, - interior: {X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }}; - - const beneficiary = { - V2: { - parents: 0, - interior: {X1: { - AccountId32: { - network: 'Any', - id: alice.addressRaw, - }, - }}, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT_RELAY, - }, - }, - ], - }; - - const feeAssetItem = 0; - - await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - }); - - }); - - itSub('Should connect and send USDT from Statemine to Quartz', async ({helper}) => { - await usingStateminePlaygrounds(statemineUrl, async (helper) => { - const dest = { - V2: { - parents: 1, - interior: {X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }}; - - const beneficiary = { - V2: { - parents: 0, - interior: {X1: { - AccountId32: { - network: 'Any', - id: alice.addressRaw, - }, - }}, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: { - X2: [ - { - PalletInstance: STATEMINE_PALLET_INSTANCE, - }, - { - GeneralIndex: USDT_ASSET_ID, - }, - ]}, - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT, - }, - }, - ], - }; - - const feeAssetItem = 0; - - balanceStmnBefore = await helper.balance.getSubstrate(alice.address); - await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited'); - - balanceStmnAfter = await helper.balance.getSubstrate(alice.address); - - // common good parachain take commission in it native token - console.log( - '[Statemine -> Quartz] transaction fees on Statemine: %s WND', - helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINE_DECIMALS), - ); - expect(balanceStmnBefore > balanceStmnAfter).to.be.true; - - }); - - - // ensure that asset has been delivered - await helper.wait.newBlocks(3); - - // expext collection id will be with id 1 - const free = await helper.ft.getBalance(1, {Substrate: alice.address}); - - balanceQuartzAfter = await helper.balance.getSubstrate(alice.address); - - console.log( - '[Statemine -> Quartz] transaction fees on Quartz: %s USDT', - helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS), - ); - console.log( - '[Statemine -> Quartz] transaction fees on Quartz: %s QTZ', - helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzBefore), - ); - // commission has not paid in USDT token - expect(free).to.be.equal(TRANSFER_AMOUNT); - // ... and parachain native token - expect(balanceQuartzAfter == balanceQuartzBefore).to.be.true; - }); - - itSub('Should connect and send USDT from Quartz to Statemine back', async ({helper}) => { - const destination = { - V2: { - parents: 1, - interior: {X2: [ - { - Parachain: STATEMINE_CHAIN, - }, - { - AccountId32: { - network: 'Any', - id: alice.addressRaw, - }, - }, - ]}, - }, - }; - - const relayFee = 400_000_000_000_000n; - const currencies: [any, bigint][] = [ - [ - { - ForeignAssetId: 0, - }, - TRANSFER_AMOUNT, - ], - [ - { - NativeAssetId: 'Parent', - }, - relayFee, - ], - ]; - - const feeItem = 1; - - await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited'); - - // the commission has been paid in parachain native token - balanceQuartzFinal = await helper.balance.getSubstrate(alice.address); - console.log('[Quartz -> Statemine] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzFinal)); - expect(balanceQuartzAfter > balanceQuartzFinal).to.be.true; - - await usingStateminePlaygrounds(statemineUrl, async (helper) => { - await helper.wait.newBlocks(3); - - // The USDT token never paid fees. Its amount not changed from begin value. - // Also check that xcm transfer has been succeeded - expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true; - }); - }); - - itSub('Should connect and send Relay token to Quartz', async ({helper}) => { - balanceBobBefore = await helper.balance.getSubstrate(bob.address); - balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); - - await usingRelayPlaygrounds(relayUrl, async (helper) => { - const destination = { - V2: { - parents: 0, - interior: {X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }}; - - const beneficiary = { - V2: { - parents: 0, - interior: {X1: { - AccountId32: { - network: 'Any', - id: bob.addressRaw, - }, - }}, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT_RELAY, - }, - }, - ], - }; - - const feeAssetItem = 0; - - await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - }); - - await helper.wait.newBlocks(3); - - balanceBobAfter = await helper.balance.getSubstrate(bob.address); - balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); - - const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; - const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore; - console.log( - '[Relay (Westend) -> Quartz] transaction fees: %s QTZ', - helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore), - ); - console.log( - '[Relay (Westend) -> Quartz] transaction fees: %s WND', - helper.util.bigIntToDecimals(wndFeeOnQuartz, STATEMINE_DECIMALS), - ); - console.log('[Relay (Westend) -> Quartz] actually delivered: %s WND', wndDiffOnQuartz); - expect(wndFeeOnQuartz == 0n, 'No incoming WND fees should be taken').to.be.true; - expect(balanceBobBefore == balanceBobAfter, 'No incoming QTZ fees should be taken').to.be.true; - }); - - itSub('Should connect and send Relay token back', async ({helper}) => { - let relayTokenBalanceBefore: bigint; - let relayTokenBalanceAfter: bigint; - await usingRelayPlaygrounds(relayUrl, async (helper) => { - relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address); - }); - - const destination = { - V2: { - parents: 1, - interior: { - X1:{ - AccountId32: { - network: 'Any', - id: bob.addressRaw, - }, - }, - }, - }, - }; - - const currencies: any = [ - [ - { - NativeAssetId: 'Parent', - }, - TRANSFER_AMOUNT_RELAY, - ], - ]; - - const feeItem = 0; - - await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited'); - - balanceBobFinal = await helper.balance.getSubstrate(bob.address); - console.log('[Quartz -> Relay (Westend)] transaction fees: %s QTZ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal)); - - await usingRelayPlaygrounds(relayUrl, async (helper) => { - await helper.wait.newBlocks(10); - relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address); - - const diff = relayTokenBalanceAfter - relayTokenBalanceBefore; - console.log('[Quartz -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS)); - expect(diff > 0, 'Relay tokens was not delivered back').to.be.true; - }); - }); -}); - -describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => { - let alice: IKeyringPair; - let randomAccount: IKeyringPair; - - let balanceQuartzTokenInit: bigint; - let balanceQuartzTokenMiddle: bigint; - let balanceQuartzTokenFinal: bigint; - let balanceKaruraTokenInit: bigint; - let balanceKaruraTokenMiddle: bigint; - let balanceKaruraTokenFinal: bigint; - let balanceQuartzForeignTokenInit: bigint; - let balanceQuartzForeignTokenMiddle: bigint; - let balanceQuartzForeignTokenFinal: bigint; - - // computed by a test transfer from prod Quartz to prod Karura. - // 2 QTZ sent https://quartz.subscan.io/xcm_message/kusama-f60d821b049f8835a3005ce7102285006f5b61e9 - // 1.919176000000000000 QTZ received (you can check Karura's chain state in the corresponding block) - const expectedKaruraIncomeFee = 2000000000000000000n - 1919176000000000000n; - const karuraEps = 8n * 10n ** 16n; - - let karuraBackwardTransferAmount: bigint; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - [randomAccount] = await helper.arrange.createAccounts([0n], alice); - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingKaruraPlaygrounds(karuraUrl, async (helper) => { - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }; - - const metadata = { - name: 'Quartz', - symbol: 'QTZ', - decimals: 18, - minimalBalance: 1000000000000000000n, - }; - - const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) => - hexToString(v.toJSON()['symbol'])) as string[]; - - if(!assets.includes('QTZ')) { - await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata); - } else { - console.log('QTZ token already registered on Karura assetRegistry pallet'); - } - await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); - balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address); - balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0}); - }); - - await usingPlaygrounds(async (helper) => { - await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT); - balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address); - }); - }); - - itSub('Should connect and send QTZ to Karura', async ({helper}) => { - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: KARURA_CHAIN, - }, - }, - }, - }; - - const beneficiary = { - V2: { - parents: 0, - interior: { - X1: { - AccountId32: { - network: 'Any', - id: randomAccount.addressRaw, - }, - }, - }, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT, - }, - }, - ], - }; - - const feeAssetItem = 0; - - await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); - - const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT; - expect(qtzFees > 0n, 'Negative fees QTZ, looks like nothing was transferred').to.be.true; - console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees)); - - await usingKaruraPlaygrounds(karuraUrl, async (helper) => { - await helper.wait.newBlocks(3); - - balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0}); - balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); - - const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle; - const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit; - karuraBackwardTransferAmount = qtzIncomeTransfer; - - const karUnqFees = TRANSFER_AMOUNT - qtzIncomeTransfer; - - console.log( - '[Quartz -> Karura] transaction fees on Karura: %s KAR', - helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS), - ); - console.log( - '[Quartz -> Karura] transaction fees on Karura: %s QTZ', - helper.util.bigIntToDecimals(karUnqFees), - ); - console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer)); - expect(karFees == 0n).to.be.true; - - const bigintAbs = (n: bigint) => (n < 0n) ? -n : n; - - expect( - bigintAbs(karUnqFees - expectedKaruraIncomeFee) < karuraEps, - 'Karura took different income fee, check the Karura foreign asset config', - ).to.be.true; - }); - }); - - itSub('Should connect to Karura and send QTZ back', async ({helper}) => { - await usingKaruraPlaygrounds(karuraUrl, async (helper) => { - const destination = { - V2: { - parents: 1, - interior: { - X2: [ - {Parachain: QUARTZ_CHAIN}, - { - AccountId32: { - network: 'Any', - id: randomAccount.addressRaw, - }, - }, - ], - }, - }, - }; - - const id = { - ForeignAsset: 0, - }; - - await helper.xTokens.transfer(randomAccount, id, karuraBackwardTransferAmount, destination, 'Unlimited'); - balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address); - balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id); - - const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal; - const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal; - - console.log( - '[Karura -> Quartz] transaction fees on Karura: %s KAR', - helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS), - ); - console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer)); - - expect(karFees > 0, 'Negative fees KAR, looks like nothing was transferred').to.be.true; - expect(qtzOutcomeTransfer == karuraBackwardTransferAmount).to.be.true; - }); - - await helper.wait.newBlocks(3); - - balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address); - const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle; - expect(actuallyDelivered > 0).to.be.true; - - console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered)); - - const qtzFees = karuraBackwardTransferAmount - actuallyDelivered; - console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees)); - expect(qtzFees == 0n).to.be.true; - }); - - itSub('Karura can send only up to its balance', async ({helper}) => { - // set Karura's sovereign account's balance - const karuraBalance = 10000n * (10n ** QTZ_DECIMALS); - const karuraSovereignAccount = helper.address.paraSiblingSovereignAccount(KARURA_CHAIN); - await helper.getSudo().balance.setBalanceSubstrate(alice, karuraSovereignAccount, karuraBalance); - - const moreThanKaruraHas = karuraBalance * 2n; - - let targetAccountBalance = 0n; - const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); - - const quartzMultilocation = { - V2: { - parents: 1, - interior: { - X1: {Parachain: QUARTZ_CHAIN}, - }, - }, - }; - - const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - moreThanKaruraHas, - ); - - let maliciousXcmProgramSent: any; - const maxWaitBlocks = 5; - - // Try to trick Quartz - await usingKaruraPlaygrounds(karuraUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram); - - maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash - && event.outcome.isFailedToTransactAsset); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(0n); - - // But Karura still can send the correct amount - const validTransferAmount = karuraBalance / 2n; - const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - validTransferAmount, - ); - - await usingKaruraPlaygrounds(karuraUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram); - }); - - await helper.wait.newBlocks(maxWaitBlocks); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(validTransferAmount); - }); - - itSub('Should not accept reserve transfer of QTZ from Karura', async ({helper}) => { - const testAmount = 10_000n * (10n ** QTZ_DECIMALS); - const [targetAccount] = await helper.arrange.createAccounts([0n], alice); - - const quartzMultilocation = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }; - - const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 1, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }, - testAmount, - ); - - const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - testAmount, - ); - - let maliciousXcmProgramFullIdSent: any; - let maliciousXcmProgramHereIdSent: any; - const maxWaitBlocks = 3; - - // Try to trick Quartz using full QTZ identification - await usingKaruraPlaygrounds(karuraUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId); - - maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - let accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - - // Try to trick Quartz using shortened QTZ identification - await usingKaruraPlaygrounds(karuraUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId); - - maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - }); -}); - -// These tests are relevant only when -// the the corresponding foreign assets are not registered -describeXCM('[XCM] Integration test: Quartz rejects non-native tokens', () => { - let alice: IKeyringPair; - let alith: IKeyringPair; - - const testAmount = 100_000_000_000n; - let quartzParachainJunction; - let quartzAccountJunction; - - let quartzParachainMultilocation: any; - let quartzAccountMultilocation: any; - let quartzCombinedMultilocation: any; - - let messageSent: any; - - const maxWaitBlocks = 3; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - - quartzParachainJunction = {Parachain: QUARTZ_CHAIN}; - quartzAccountJunction = { - AccountId32: { - network: 'Any', - id: alice.addressRaw, - }, - }; - - quartzParachainMultilocation = { - V2: { - parents: 1, - interior: { - X1: quartzParachainJunction, - }, - }, - }; - - quartzAccountMultilocation = { - V2: { - parents: 0, - interior: { - X1: quartzAccountJunction, - }, - }, - }; - - quartzCombinedMultilocation = { - V2: { - parents: 1, - interior: { - X2: [quartzParachainJunction, quartzAccountJunction], - }, - }, - }; - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - // eslint-disable-next-line require-await - await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { - alith = helper.account.alithAccount(); - }); - }); - - const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => { - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash - && event.outcome.isFailedToTransactAsset); - }; - - itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => { - await usingKaruraPlaygrounds(karuraUrl, async (helper) => { - const id = { - Token: 'KAR', - }; - const destination = quartzCombinedMultilocation; - await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited'); - - messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await expectFailedToTransact(helper, messageSent); - }); - - itSub('Quartz rejects MOVR tokens from Moonriver', async ({helper}) => { - await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { - const id = 'SelfReserve'; - const destination = quartzCombinedMultilocation; - await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited'); - - messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await expectFailedToTransact(helper, messageSent); - }); - - itSub('Quartz rejects SDN tokens from Shiden', async ({helper}) => { - await usingShidenPlaygrounds(shidenUrl, async (helper) => { - const destinationParachain = quartzParachainMultilocation; - const beneficiary = quartzAccountMultilocation; - const assets = { - V2: [{ - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: testAmount, - }, - }], - }; - const feeAssetItem = 0; - - await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [ - destinationParachain, - beneficiary, - assets, - feeAssetItem, - ]); - - messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await expectFailedToTransact(helper, messageSent); - }); -}); - -describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => { - // Quartz constants - let alice: IKeyringPair; - let quartzAssetLocation; - - let randomAccountQuartz: IKeyringPair; - let randomAccountMoonriver: IKeyringPair; - - // Moonriver constants - let assetId: string; - - const quartzAssetMetadata = { - name: 'xcQuartz', - symbol: 'xcQTZ', - decimals: 18, - isFrozen: false, - minimalBalance: 1n, - }; - - let balanceQuartzTokenInit: bigint; - let balanceQuartzTokenMiddle: bigint; - let balanceQuartzTokenFinal: bigint; - let balanceForeignQtzTokenInit: bigint; - let balanceForeignQtzTokenMiddle: bigint; - let balanceForeignQtzTokenFinal: bigint; - let balanceMovrTokenInit: bigint; - let balanceMovrTokenMiddle: bigint; - let balanceMovrTokenFinal: bigint; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice); - - balanceForeignQtzTokenInit = 0n; - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { - const alithAccount = helper.account.alithAccount(); - const baltatharAccount = helper.account.baltatharAccount(); - const dorothyAccount = helper.account.dorothyAccount(); - - randomAccountMoonriver = helper.account.create(); - - // >>> Sponsoring Dorothy >>> - console.log('Sponsoring Dorothy.......'); - await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n); - console.log('Sponsoring Dorothy.......DONE'); - // <<< Sponsoring Dorothy <<< - - quartzAssetLocation = { - XCM: { - parents: 1, - interior: {X1: {Parachain: QUARTZ_CHAIN}}, - }, - }; - const existentialDeposit = 1n; - const isSufficient = true; - const unitsPerSecond = 1n; - const numAssetsWeightHint = 0; - - if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) { - console.log('Quartz asset already registered on Moonriver'); - } else { - const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({ - location: quartzAssetLocation, - metadata: quartzAssetMetadata, - existentialDeposit, - isSufficient, - unitsPerSecond, - numAssetsWeightHint, - }); - - console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal); - - await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal); - } - // >>> Acquire Quartz AssetId Info on Moonriver >>> - console.log('Acquire Quartz AssetId Info on Moonriver.......'); - - assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString(); - - console.log('QTZ asset ID is %s', assetId); - console.log('Acquire Quartz AssetId Info on Moonriver.......DONE'); - // >>> Acquire Quartz AssetId Info on Moonriver >>> - - // >>> Sponsoring random Account >>> - console.log('Sponsoring random Account.......'); - await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n); - console.log('Sponsoring random Account.......DONE'); - // <<< Sponsoring random Account <<< - - balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address); - }); - - await usingPlaygrounds(async (helper) => { - await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT); - balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address); - }); - }); - - itSub('Should connect and send QTZ to Moonriver', async ({helper}) => { - const currencyId = { - NativeAssetId: 'Here', - }; - const dest = { - V2: { - parents: 1, - interior: { - X2: [ - {Parachain: MOONRIVER_CHAIN}, - {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}}, - ], - }, - }, - }; - const amount = TRANSFER_AMOUNT; - - await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, 'Unlimited'); - - balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address); - expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true; - - const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT; - console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees)); - expect(transactionFees > 0, 'Negative fees QTZ, looks like nothing was transferred').to.be.true; - - await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { - await helper.wait.newBlocks(3); - - balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address); - - const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle; - console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',helper.util.bigIntToDecimals(movrFees)); - expect(movrFees == 0n).to.be.true; - - balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; // BigInt(qtzRandomAccountAsset['balance']); - const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit; - console.log('[Quartz -> Moonriver] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer)); - expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true; - }); - }); - - itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => { - await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { - const asset = { - V2: { - id: { - Concrete: { - parents: 1, - interior: { - X1: {Parachain: QUARTZ_CHAIN}, - }, - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT, - }, - }, - }; - const destination = { - V2: { - parents: 1, - interior: { - X2: [ - {Parachain: QUARTZ_CHAIN}, - {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}}, - ], - }, - }, - }; - - await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, 'Unlimited'); - - balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address); - - const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal; - console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees)); - expect(movrFees > 0, 'Negative fees MOVR, looks like nothing was transferred').to.be.true; - - const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address); - - expect(qtzRandomAccountAsset).to.be.null; - - balanceForeignQtzTokenFinal = 0n; - - const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal; - console.log('[Quartz -> Moonriver] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer)); - expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true; - }); - - await helper.wait.newBlocks(3); - - balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address); - const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle; - expect(actuallyDelivered > 0).to.be.true; - - console.log('[Moonriver -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered)); - - const qtzFees = TRANSFER_AMOUNT - actuallyDelivered; - console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees)); - expect(qtzFees == 0n).to.be.true; - }); - - itSub('Moonriver can send only up to its balance', async ({helper}) => { - // set Moonriver's sovereign account's balance - const moonriverBalance = 10000n * (10n ** QTZ_DECIMALS); - const moonriverSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONRIVER_CHAIN); - await helper.getSudo().balance.setBalanceSubstrate(alice, moonriverSovereignAccount, moonriverBalance); - - const moreThanMoonriverHas = moonriverBalance * 2n; - - let targetAccountBalance = 0n; - const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); - - const quartzMultilocation = { - V2: { - parents: 1, - interior: { - X1: {Parachain: QUARTZ_CHAIN}, - }, - }, - }; - - const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - moreThanMoonriverHas, - ); - - let maliciousXcmProgramSent: any; - const maxWaitBlocks = 3; - - // Try to trick Quartz - await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgram]); - - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal('try to spend more QTZ than Moonriver has', batchCall); - - maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash - && event.outcome.isFailedToTransactAsset); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(0n); - - // But Moonriver still can send the correct amount - const validTransferAmount = moonriverBalance / 2n; - const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - validTransferAmount, - ); - - await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, validXcmProgram]); - - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal('Spend the correct amount of QTZ', batchCall); - }); - - await helper.wait.newBlocks(maxWaitBlocks); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(validTransferAmount); - }); - - itSub('Should not accept reserve transfer of QTZ from Moonriver', async ({helper}) => { - const testAmount = 10_000n * (10n ** QTZ_DECIMALS); - const [targetAccount] = await helper.arrange.createAccounts([0n], alice); - - const quartzMultilocation = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }; - - const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }, - testAmount, - ); - - const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - testAmount, - ); - - let maliciousXcmProgramFullIdSent: any; - let maliciousXcmProgramHereIdSent: any; - const maxWaitBlocks = 3; - - // Try to trick Quartz using full QTZ identification - await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramFullId]); - - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using path asset identification', batchCall); - - maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - let accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - - // Try to trick Quartz using shortened QTZ identification - await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramHereId]); - - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using "here" asset identification', batchCall); - - maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - }); -}); - -describeXCM('[XCM] Integration test: Exchanging tokens with Shiden', () => { - let alice: IKeyringPair; - let sender: IKeyringPair; - - const QTZ_ASSET_ID_ON_SHIDEN = 18_446_744_073_709_551_633n; // The value is taken from the live Shiden - const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n; // The value is taken from the live Shiden - - // Quartz -> Shiden - const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden - const unitsPerSecond = 500_451_000_000_000_000_000n; // The value is taken from the live Shiden - const qtzToShidenTransferred = 10n * (10n ** QTZ_DECIMALS); // 10 QTZ - const qtzToShidenArrived = 7_998_196_000_000_000_000n; // 7.99 ... QTZ, Shiden takes a commision in foreign tokens - - // Shiden -> Quartz - const qtzFromShidenTransfered = 5n * (10n ** QTZ_DECIMALS); // 5 QTZ - const qtzOnShidenLeft = qtzToShidenArrived - qtzFromShidenTransfered; // 2.99 ... QTZ - - let balanceAfterQuartzToShidenXCM: bigint; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - [sender] = await helper.arrange.createAccounts([100n], alice); - console.log('sender', sender.address); - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingShidenPlaygrounds(shidenUrl, async (helper) => { - if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) { - console.log('1. Create foreign asset and metadata'); - await helper.getSudo().assets.forceCreate( - alice, - QTZ_ASSET_ID_ON_SHIDEN, - alice.address, - QTZ_MINIMAL_BALANCE_ON_SHIDEN, - ); - - await helper.assets.setMetadata( - alice, - QTZ_ASSET_ID_ON_SHIDEN, - 'Quartz', - 'QTZ', - Number(QTZ_DECIMALS), - ); - - console.log('2. Register asset location on Shiden'); - const assetLocation = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }; - - await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]); - - console.log('3. Set QTZ payment for XCM execution on Shiden'); - await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]); - } else { - console.log('QTZ is already registered on Shiden'); - } - console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)'); - await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance); - }); - }); - - itSub('Should connect and send QTZ to Shiden', async ({helper}) => { - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: SHIDEN_CHAIN, - }, - }, - }, - }; - - const beneficiary = { - V2: { - parents: 0, - interior: { - X1: { - AccountId32: { - network: 'Any', - id: sender.addressRaw, - }, - }, - }, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: qtzToShidenTransferred, - }, - }, - ], - }; - - // Initial balance is 100 QTZ - const balanceBefore = await helper.balance.getSubstrate(sender.address); - console.log(`Initial balance is: ${balanceBefore}`); - - const feeAssetItem = 0; - await helper.xcm.limitedReserveTransferAssets(sender, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - - // Balance after reserve transfer is less than 90 - balanceAfterQuartzToShidenXCM = await helper.balance.getSubstrate(sender.address); - console.log(`QTZ Balance on Quartz after XCM is: ${balanceAfterQuartzToShidenXCM}`); - console.log(`Quartz's QTZ commission is: ${balanceBefore - balanceAfterQuartzToShidenXCM}`); - expect(balanceBefore - balanceAfterQuartzToShidenXCM > 0).to.be.true; - - await usingShidenPlaygrounds(shidenUrl, async (helper) => { - await helper.wait.newBlocks(3); - const xcQTZbalance = await helper.assets.account(QTZ_ASSET_ID_ON_SHIDEN, sender.address); - const shidenBalance = await helper.balance.getSubstrate(sender.address); - - console.log(`xcQTZ balance on Shiden after XCM is: ${xcQTZbalance}`); - console.log(`Shiden's QTZ commission is: ${qtzToShidenTransferred - xcQTZbalance!}`); - - expect(xcQTZbalance).to.eq(qtzToShidenArrived); - // SHD balance does not changed: - expect(shidenBalance).to.eq(shidenInitialBalance); - }); - }); - - itSub('Should connect to Shiden and send QTZ back', async ({helper}) => { - await usingShidenPlaygrounds(shidenUrl, async (helper) => { - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }; - - const beneficiary = { - V2: { - parents: 0, - interior: { - X1: { - AccountId32: { - network: 'Any', - id: sender.addressRaw, - }, - }, - }, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 1, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }, - fun: { - Fungible: qtzFromShidenTransfered, - }, - }, - ], - }; - - // Initial balance is 1 SDN - const balanceSDNbefore = await helper.balance.getSubstrate(sender.address); - console.log(`SDN balance is: ${balanceSDNbefore}, it does not changed`); - expect(balanceSDNbefore).to.eq(shidenInitialBalance); - - const feeAssetItem = 0; - // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw - await helper.executeExtrinsic(sender, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]); - - // Balance after reserve transfer is less than 1 SDN - const xcQTZbalance = await helper.assets.account(QTZ_ASSET_ID_ON_SHIDEN, sender.address); - const balanceSDN = await helper.balance.getSubstrate(sender.address); - console.log(`xcQTZ balance on Shiden after XCM is: ${xcQTZbalance}`); - - // Assert: xcQTZ balance correctly decreased - expect(xcQTZbalance).to.eq(qtzOnShidenLeft); - // Assert: SDN balance is 0.996... - expect(balanceSDN / (10n ** (SHIDEN_DECIMALS - 3n))).to.eq(996n); - }); - - await helper.wait.newBlocks(3); - const balanceQTZ = await helper.balance.getSubstrate(sender.address); - console.log(`QTZ Balance on Quartz after XCM is: ${balanceQTZ}`); - expect(balanceQTZ).to.eq(balanceAfterQuartzToShidenXCM + qtzFromShidenTransfered); - }); - - itSub('Shiden can send only up to its balance', async ({helper}) => { - // set Shiden's sovereign account's balance - const shidenBalance = 10000n * (10n ** QTZ_DECIMALS); - const shidenSovereignAccount = helper.address.paraSiblingSovereignAccount(SHIDEN_CHAIN); - await helper.getSudo().balance.setBalanceSubstrate(alice, shidenSovereignAccount, shidenBalance); - - const moreThanShidenHas = shidenBalance * 2n; - - let targetAccountBalance = 0n; - const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); - - const quartzMultilocation = { - V2: { - parents: 1, - interior: { - X1: {Parachain: QUARTZ_CHAIN}, - }, - }, - }; - - const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - moreThanShidenHas, - ); - - let maliciousXcmProgramSent: any; - const maxWaitBlocks = 3; - - // Try to trick Quartz - await usingShidenPlaygrounds(shidenUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram); - - maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash - && event.outcome.isFailedToTransactAsset); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(0n); - - // But Shiden still can send the correct amount - const validTransferAmount = shidenBalance / 2n; - const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - validTransferAmount, - ); - - await usingShidenPlaygrounds(shidenUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram); - }); - - await helper.wait.newBlocks(maxWaitBlocks); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(validTransferAmount); - }); - - itSub('Should not accept reserve transfer of QTZ from Shiden', async ({helper}) => { - const testAmount = 10_000n * (10n ** QTZ_DECIMALS); - const [targetAccount] = await helper.arrange.createAccounts([0n], alice); - - const quartzMultilocation = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }; - - const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 1, - interior: { - X1: { - Parachain: QUARTZ_CHAIN, - }, - }, - }, - }, - testAmount, - ); - - const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - testAmount, - ); - - let maliciousXcmProgramFullIdSent: any; - let maliciousXcmProgramHereIdSent: any; - const maxWaitBlocks = 3; - - // Try to trick Quartz using full QTZ identification - await usingShidenPlaygrounds(shidenUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId); - - maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - let accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - - // Try to trick Quartz using shortened QTZ identification - await usingShidenPlaygrounds(shidenUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId); - - maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - }); -}); --- a/js-packages/tests/src/xcm/xcmUnique.test.ts +++ /dev/null @@ -1,1834 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -import type {IKeyringPair} from '@polkadot/types/types'; -import config from '../config.js'; -import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util/index.js'; -import {Event} from '@unique/playgrounds/src/unique.dev.js'; -import {hexToString, nToBigInt} from '@polkadot/util'; -import {ACALA_CHAIN, ASTAR_CHAIN, MOONBEAM_CHAIN, POLKADEX_CHAIN, SAFE_XCM_VERSION, STATEMINT_CHAIN, UNIQUE_CHAIN, expectFailedToTransact, expectUntrustedReserveLocationFail, uniqueAssetId, uniqueVersionedMultilocation} from './xcm.types.js'; - - -const STATEMINT_PALLET_INSTANCE = 50; - -const relayUrl = config.relayUrl; -const statemintUrl = config.statemintUrl; -const acalaUrl = config.acalaUrl; -const moonbeamUrl = config.moonbeamUrl; -const astarUrl = config.astarUrl; -const polkadexUrl = config.polkadexUrl; - -const RELAY_DECIMALS = 12; -const STATEMINT_DECIMALS = 12; -const ACALA_DECIMALS = 12; -const ASTAR_DECIMALS = 18n; -const UNQ_DECIMALS = 18n; - -const TRANSFER_AMOUNT = 2000000000000000000000000n; - -const FUNDING_AMOUNT = 3_500_000_0000_000_000n; - -const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n; - -const USDT_ASSET_ID = 100; -const USDT_ASSET_METADATA_DECIMALS = 18; -const USDT_ASSET_METADATA_NAME = 'USDT'; -const USDT_ASSET_METADATA_DESCRIPTION = 'USDT'; -const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n; -const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n; - -describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => { - let alice: IKeyringPair; - let bob: IKeyringPair; - - let balanceStmnBefore: bigint; - let balanceStmnAfter: bigint; - - let balanceUniqueBefore: bigint; - let balanceUniqueAfter: bigint; - let balanceUniqueFinal: bigint; - - let balanceBobBefore: bigint; - let balanceBobAfter: bigint; - let balanceBobFinal: bigint; - - let balanceBobRelayTokenBefore: bigint; - let balanceBobRelayTokenAfter: bigint; - - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingRelayPlaygrounds(relayUrl, async (helper) => { - // Fund accounts on Statemint - await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT); - await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT); - }); - - await usingStatemintPlaygrounds(statemintUrl, async (helper) => { - const sovereignFundingAmount = 3_500_000_000n; - - await helper.assets.create( - alice, - USDT_ASSET_ID, - alice.address, - USDT_ASSET_METADATA_MINIMAL_BALANCE, - ); - await helper.assets.setMetadata( - alice, - USDT_ASSET_ID, - USDT_ASSET_METADATA_NAME, - USDT_ASSET_METADATA_DESCRIPTION, - USDT_ASSET_METADATA_DECIMALS, - ); - await helper.assets.mint( - alice, - USDT_ASSET_ID, - alice.address, - USDT_ASSET_AMOUNT, - ); - - // funding parachain sovereing account on Statemint. - // The sovereign account should be created before any action - // (the assets pallet on Statemint check if the sovereign account exists) - const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN); - await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount); - }); - - - await usingPlaygrounds(async (helper) => { - const location = { - V2: { - parents: 1, - interior: {X3: [ - { - Parachain: STATEMINT_CHAIN, - }, - { - PalletInstance: STATEMINT_PALLET_INSTANCE, - }, - { - GeneralIndex: USDT_ASSET_ID, - }, - ]}, - }, - }; - - const metadata = - { - name: USDT_ASSET_ID, - symbol: USDT_ASSET_METADATA_NAME, - decimals: USDT_ASSET_METADATA_DECIMALS, - minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE, - }; - await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata); - balanceUniqueBefore = await helper.balance.getSubstrate(alice.address); - }); - - - // Providing the relay currency to the unique sender account - // (fee for USDT XCM are paid in relay tokens) - await usingRelayPlaygrounds(relayUrl, async (helper) => { - const destination = { - V2: { - parents: 0, - interior: {X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }}; - - const beneficiary = { - V2: { - parents: 0, - interior: {X1: { - AccountId32: { - network: 'Any', - id: alice.addressRaw, - }, - }}, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT_RELAY, - }, - }, - ], - }; - - const feeAssetItem = 0; - - await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - }); - - }); - - itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => { - await usingStatemintPlaygrounds(statemintUrl, async (helper) => { - const dest = { - V2: { - parents: 1, - interior: {X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }}; - - const beneficiary = { - V2: { - parents: 0, - interior: {X1: { - AccountId32: { - network: 'Any', - id: alice.addressRaw, - }, - }}, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: { - X2: [ - { - PalletInstance: STATEMINT_PALLET_INSTANCE, - }, - { - GeneralIndex: USDT_ASSET_ID, - }, - ]}, - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT, - }, - }, - ], - }; - - const feeAssetItem = 0; - - balanceStmnBefore = await helper.balance.getSubstrate(alice.address); - await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited'); - - balanceStmnAfter = await helper.balance.getSubstrate(alice.address); - - // common good parachain take commission in it native token - console.log( - '[Statemint -> Unique] transaction fees on Statemint: %s WND', - helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS), - ); - expect(balanceStmnBefore > balanceStmnAfter).to.be.true; - - }); - - - // ensure that asset has been delivered - await helper.wait.newBlocks(3); - - // expext collection id will be with id 1 - const free = await helper.ft.getBalance(1, {Substrate: alice.address}); - - balanceUniqueAfter = await helper.balance.getSubstrate(alice.address); - - console.log( - '[Statemint -> Unique] transaction fees on Unique: %s USDT', - helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS), - ); - console.log( - '[Statemint -> Unique] transaction fees on Unique: %s UNQ', - helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore), - ); - // commission has not paid in USDT token - expect(free).to.be.equal(TRANSFER_AMOUNT); - // ... and parachain native token - expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true; - }); - - itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => { - const destination = { - V2: { - parents: 1, - interior: {X2: [ - { - Parachain: STATEMINT_CHAIN, - }, - { - AccountId32: { - network: 'Any', - id: alice.addressRaw, - }, - }, - ]}, - }, - }; - - const relayFee = 400_000_000_000_000n; - const currencies: [any, bigint][] = [ - [ - { - ForeignAssetId: 0, - }, - TRANSFER_AMOUNT, - ], - [ - { - NativeAssetId: 'Parent', - }, - relayFee, - ], - ]; - - const feeItem = 1; - - await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited'); - - // the commission has been paid in parachain native token - balanceUniqueFinal = await helper.balance.getSubstrate(alice.address); - console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal)); - expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true; - - await usingStatemintPlaygrounds(statemintUrl, async (helper) => { - await helper.wait.newBlocks(3); - - // The USDT token never paid fees. Its amount not changed from begin value. - // Also check that xcm transfer has been succeeded - expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true; - }); - }); - - itSub('Should connect and send Relay token to Unique', async ({helper}) => { - balanceBobBefore = await helper.balance.getSubstrate(bob.address); - balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); - - await usingRelayPlaygrounds(relayUrl, async (helper) => { - const destination = { - V2: { - parents: 0, - interior: {X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }}; - - const beneficiary = { - V2: { - parents: 0, - interior: {X1: { - AccountId32: { - network: 'Any', - id: bob.addressRaw, - }, - }}, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT_RELAY, - }, - }, - ], - }; - - const feeAssetItem = 0; - - await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - }); - - await helper.wait.newBlocks(3); - - balanceBobAfter = await helper.balance.getSubstrate(bob.address); - balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); - - const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; - const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore; - console.log( - '[Relay (Westend) -> Unique] transaction fees: %s UNQ', - helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore), - ); - console.log( - '[Relay (Westend) -> Unique] transaction fees: %s WND', - helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS), - ); - console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique); - expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true; - expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true; - }); - - itSub('Should connect and send Relay token back', async ({helper}) => { - let relayTokenBalanceBefore: bigint; - let relayTokenBalanceAfter: bigint; - await usingRelayPlaygrounds(relayUrl, async (helper) => { - relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address); - }); - - const destination = { - V2: { - parents: 1, - interior: { - X1:{ - AccountId32: { - network: 'Any', - id: bob.addressRaw, - }, - }, - }, - }, - }; - - const currencies: any = [ - [ - { - NativeAssetId: 'Parent', - }, - TRANSFER_AMOUNT_RELAY, - ], - ]; - - const feeItem = 0; - - await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited'); - - balanceBobFinal = await helper.balance.getSubstrate(bob.address); - console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal)); - - await usingRelayPlaygrounds(relayUrl, async (helper) => { - await helper.wait.newBlocks(10); - relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address); - - const diff = relayTokenBalanceAfter - relayTokenBalanceBefore; - console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS)); - expect(diff > 0, 'Relay tokens was not delivered back').to.be.true; - }); - }); -}); - -describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => { - let alice: IKeyringPair; - let randomAccount: IKeyringPair; - - let balanceUniqueTokenInit: bigint; - let balanceUniqueTokenMiddle: bigint; - let balanceUniqueTokenFinal: bigint; - let balanceAcalaTokenInit: bigint; - let balanceAcalaTokenMiddle: bigint; - let balanceAcalaTokenFinal: bigint; - let balanceUniqueForeignTokenInit: bigint; - let balanceUniqueForeignTokenMiddle: bigint; - let balanceUniqueForeignTokenFinal: bigint; - - // computed by a test transfer from prod Unique to prod Acala. - // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48 - // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block) - const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n; - const acalaEps = 8n * 10n ** 16n; - - let acalaBackwardTransferAmount: bigint; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - [randomAccount] = await helper.arrange.createAccounts([0n], alice); - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingAcalaPlaygrounds(acalaUrl, async (helper) => { - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }, - }; - - const metadata = { - name: 'Unique Network', - symbol: 'UNQ', - decimals: 18, - minimalBalance: 1250000000000000000n, - }; - const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) => - hexToString(v.toJSON()['symbol'])) as string[]; - - if(!assets.includes('UNQ')) { - await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata); - } else { - console.log('UNQ token already registered on Acala assetRegistry pallet'); - } - await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); - balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address); - balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0}); - }); - - await usingPlaygrounds(async (helper) => { - await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT); - balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address); - }); - }); - - itSub('Should connect and send UNQ to Acala', async ({helper}) => { - - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: ACALA_CHAIN, - }, - }, - }, - }; - - const beneficiary = { - V2: { - parents: 0, - interior: { - X1: { - AccountId32: { - network: 'Any', - id: randomAccount.addressRaw, - }, - }, - }, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT, - }, - }, - ], - }; - - const feeAssetItem = 0; - - await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); - - const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT; - console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees)); - expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true; - - await usingAcalaPlaygrounds(acalaUrl, async (helper) => { - await helper.wait.newBlocks(3); - - balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0}); - balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); - - const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle; - const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit; - acalaBackwardTransferAmount = unqIncomeTransfer; - - const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer; - - console.log( - '[Unique -> Acala] transaction fees on Acala: %s ACA', - helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS), - ); - console.log( - '[Unique -> Acala] transaction fees on Acala: %s UNQ', - helper.util.bigIntToDecimals(acaUnqFees), - ); - console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer)); - expect(acaFees == 0n).to.be.true; - - const bigintAbs = (n: bigint) => (n < 0n) ? -n : n; - - expect( - bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps, - 'Acala took different income fee, check the Acala foreign asset config', - ).to.be.true; - }); - }); - - itSub('Should connect to Acala and send UNQ back', async ({helper}) => { - await usingAcalaPlaygrounds(acalaUrl, async (helper) => { - const destination = { - V2: { - parents: 1, - interior: { - X2: [ - {Parachain: UNIQUE_CHAIN}, - { - AccountId32: { - network: 'Any', - id: randomAccount.addressRaw, - }, - }, - ], - }, - }, - }; - - const id = { - ForeignAsset: 0, - }; - - await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited'); - balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address); - balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id); - - const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal; - const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal; - - console.log( - '[Acala -> Unique] transaction fees on Acala: %s ACA', - helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS), - ); - console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer)); - - expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true; - expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true; - }); - - await helper.wait.newBlocks(3); - - balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address); - const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle; - expect(actuallyDelivered > 0).to.be.true; - - console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered)); - - const unqFees = acalaBackwardTransferAmount - actuallyDelivered; - console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees)); - expect(unqFees == 0n).to.be.true; - }); - - itSub('Acala can send only up to its balance', async ({helper}) => { - // set Acala's sovereign account's balance - const acalaBalance = 10000n * (10n ** UNQ_DECIMALS); - const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN); - await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance); - - const moreThanAcalaHas = acalaBalance * 2n; - - let targetAccountBalance = 0n; - const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); - - const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - moreThanAcalaHas, - ); - - let maliciousXcmProgramSent: any; - const maxWaitBlocks = 3; - - // Try to trick Unique - await usingAcalaPlaygrounds(acalaUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram); - - maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash - && event.outcome.isFailedToTransactAsset); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(0n); - - // But Acala still can send the correct amount - const validTransferAmount = acalaBalance / 2n; - const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - validTransferAmount, - ); - - await usingAcalaPlaygrounds(acalaUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, validXcmProgram); - }); - - await helper.wait.newBlocks(maxWaitBlocks); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(validTransferAmount); - }); - - itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => { - const testAmount = 10_000n * (10n ** UNQ_DECIMALS); - const [targetAccount] = await helper.arrange.createAccounts([0n], alice); - - const uniqueMultilocation = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }, - }; - - const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - uniqueAssetId, - testAmount, - ); - - const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - testAmount, - ); - - let maliciousXcmProgramFullIdSent: any; - let maliciousXcmProgramHereIdSent: any; - const maxWaitBlocks = 3; - - // Try to trick Unique using full UNQ identification - await usingAcalaPlaygrounds(acalaUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId); - - maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - let accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - - // Try to trick Unique using shortened UNQ identification - await usingAcalaPlaygrounds(acalaUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId); - - maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - }); -}); - -describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => { - let alice: IKeyringPair; - let randomAccount: IKeyringPair; - let unqFees: bigint; - let balanceUniqueTokenInit: bigint; - let balanceUniqueTokenMiddle: bigint; - let balanceUniqueTokenFinal: bigint; - const maxWaitBlocks = 6; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - [randomAccount] = await helper.arrange.createAccounts([0n], alice); - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { - const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', [])) - .toJSON() as []) - .map(nToBigInt).length != 0; - /* - Check whether the Unique token has been added - to the whitelist, since an error will occur - if it is added again. Needed for debugging - when this test is run multiple times. - */ - if(isWhitelisted) { - console.log('UNQ token is already whitelisted on Polkadex'); - } else { - await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId); - } - - await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); - }); - - await usingPlaygrounds(async (helper) => { - await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT); - balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address); - }); - }); - - itSub('Should connect and send UNQ to Polkadex', async ({helper}) => { - - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: POLKADEX_CHAIN, - }, - }, - }, - }; - - const beneficiary = { - V2: { - parents: 0, - interior: { - X1: { - AccountId32: { - network: 'Any', - id: randomAccount.addressRaw, - }, - }, - }, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT, - }, - }, - ], - }; - - const feeAssetItem = 0; - - await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); - - unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT; - console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees)); - expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true; - - await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { - /* - Since only the parachain part of the Polkadex - infrastructure is launched (without their - solochain validators), processing incoming - assets will lead to an error. - This error indicates that the Polkadex chain - received a message from the Unique network, - since the hash is being checked to ensure - it matches what was sent. - */ - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash); - }); - }); - - - itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => { - - const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - randomAccount.addressRaw, - uniqueAssetId, - TRANSFER_AMOUNT, - ); - - let xcmProgramSent: any; - - - await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, xcmProgram); - - xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash); - - balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address); - - expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees); - }); - - itSub('Polkadex can send only up to its balance', async ({helper}) => { - const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS); - const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN); - await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance); - const moreThanPolkadexHas = 2n * polkadexBalance; - - const targetAccount = helper.arrange.createEmptyAccount(); - - const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - moreThanPolkadexHas, - ); - - let maliciousXcmProgramSent: any; - - - await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram); - - maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await expectFailedToTransact(helper, maliciousXcmProgramSent); - - const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(0n); - }); - - itSub('Should not accept reserve transfer of UNQ from Polkadex', async ({helper}) => { - const testAmount = 10_000n * (10n ** UNQ_DECIMALS); - const targetAccount = helper.arrange.createEmptyAccount(); - - const uniqueMultilocation = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }, - }; - - const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - uniqueAssetId, - testAmount, - ); - - const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - testAmount, - ); - - let maliciousXcmProgramFullIdSent: any; - let maliciousXcmProgramHereIdSent: any; - const maxWaitBlocks = 3; - - // Try to trick Unique using full UNQ identification - await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId); - - maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent); - - let accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - - // Try to trick Unique using shortened UNQ identification - await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId); - - maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent); - - accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - }); -}); - -// These tests are relevant only when -// the the corresponding foreign assets are not registered -describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => { - let alice: IKeyringPair; - let alith: IKeyringPair; - - const testAmount = 100_000_000_000n; - let uniqueParachainJunction; - let uniqueAccountJunction; - - let uniqueParachainMultilocation: any; - let uniqueAccountMultilocation: any; - let uniqueCombinedMultilocation: any; - let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V2 - - let messageSent: any; - - const maxWaitBlocks = 3; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - - uniqueParachainJunction = {Parachain: UNIQUE_CHAIN}; - uniqueAccountJunction = { - AccountId32: { - network: 'Any', - id: alice.addressRaw, - }, - }; - - uniqueParachainMultilocation = { - V2: { - parents: 1, - interior: { - X1: uniqueParachainJunction, - }, - }, - }; - - uniqueAccountMultilocation = { - V2: { - parents: 0, - interior: { - X1: uniqueAccountJunction, - }, - }, - }; - - uniqueCombinedMultilocation = { - V2: { - parents: 1, - interior: { - X2: [uniqueParachainJunction, uniqueAccountJunction], - }, - }, - }; - - uniqueCombinedMultilocationAcala = { - V2: { - parents: 1, - interior: { - X2: [uniqueParachainJunction, uniqueAccountJunction], - }, - }, - }; - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - // eslint-disable-next-line require-await - await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { - alith = helper.account.alithAccount(); - }); - }); - - - - itSub('Unique rejects ACA tokens from Acala', async ({helper}) => { - await usingAcalaPlaygrounds(acalaUrl, async (helper) => { - const id = { - Token: 'ACA', - }; - const destination = uniqueCombinedMultilocationAcala; - await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited'); - - messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await expectFailedToTransact(helper, messageSent); - }); - - itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => { - await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { - const id = 'SelfReserve'; - const destination = uniqueCombinedMultilocation; - await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited'); - - messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await expectFailedToTransact(helper, messageSent); - }); - - itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => { - await usingAstarPlaygrounds(astarUrl, async (helper) => { - const destinationParachain = uniqueParachainMultilocation; - const beneficiary = uniqueAccountMultilocation; - const assets = { - V2: [{ - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: testAmount, - }, - }], - }; - const feeAssetItem = 0; - - await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [ - destinationParachain, - beneficiary, - assets, - feeAssetItem, - ]); - - messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await expectFailedToTransact(helper, messageSent); - }); - - itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => { - - const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( - helper.arrange.createEmptyAccount().addressRaw, - { - Concrete: { - parents: 1, - interior: { - X1: { - Parachain: POLKADEX_CHAIN, - }, - }, - }, - }, - testAmount, - ); - - await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId); - messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await expectFailedToTransact(helper, messageSent); - }); -}); - -describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => { - // Unique constants - let alice: IKeyringPair; - let uniqueAssetLocation; - - let randomAccountUnique: IKeyringPair; - let randomAccountMoonbeam: IKeyringPair; - - // Moonbeam constants - let assetId: string; - - const uniqueAssetMetadata = { - name: 'xcUnique', - symbol: 'xcUNQ', - decimals: 18, - isFrozen: false, - minimalBalance: 1n, - }; - - let balanceUniqueTokenInit: bigint; - let balanceUniqueTokenMiddle: bigint; - let balanceUniqueTokenFinal: bigint; - let balanceForeignUnqTokenInit: bigint; - let balanceForeignUnqTokenMiddle: bigint; - let balanceForeignUnqTokenFinal: bigint; - let balanceGlmrTokenInit: bigint; - let balanceGlmrTokenMiddle: bigint; - let balanceGlmrTokenFinal: bigint; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice); - - balanceForeignUnqTokenInit = 0n; - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { - const alithAccount = helper.account.alithAccount(); - const baltatharAccount = helper.account.baltatharAccount(); - const dorothyAccount = helper.account.dorothyAccount(); - - randomAccountMoonbeam = helper.account.create(); - - // >>> Sponsoring Dorothy >>> - console.log('Sponsoring Dorothy.......'); - await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n); - console.log('Sponsoring Dorothy.......DONE'); - // <<< Sponsoring Dorothy <<< - - uniqueAssetLocation = { - XCM: { - parents: 1, - interior: {X1: {Parachain: UNIQUE_CHAIN}}, - }, - }; - const existentialDeposit = 1n; - const isSufficient = true; - const unitsPerSecond = 1n; - const numAssetsWeightHint = 0; - - if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) { - console.log('Unique asset is already registered on MoonBeam'); - } else { - const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({ - location: uniqueAssetLocation, - metadata: uniqueAssetMetadata, - existentialDeposit, - isSufficient, - unitsPerSecond, - numAssetsWeightHint, - }); - - console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal); - - await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal); - } - - // >>> Acquire Unique AssetId Info on Moonbeam >>> - console.log('Acquire Unique AssetId Info on Moonbeam.......'); - - assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString(); - console.log('UNQ asset ID is %s', assetId); - console.log('Acquire Unique AssetId Info on Moonbeam.......DONE'); - // >>> Acquire Unique AssetId Info on Moonbeam >>> - - // >>> Sponsoring random Account >>> - console.log('Sponsoring random Account.......'); - await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n); - console.log('Sponsoring random Account.......DONE'); - // <<< Sponsoring random Account <<< - - balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address); - }); - - await usingPlaygrounds(async (helper) => { - await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT); - balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address); - }); - }); - - itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => { - const currencyId = { - NativeAssetId: 'Here', - }; - const dest = { - V2: { - parents: 1, - interior: { - X2: [ - {Parachain: MOONBEAM_CHAIN}, - {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}}, - ], - }, - }, - }; - const amount = TRANSFER_AMOUNT; - - await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited'); - - balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address); - expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true; - - const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT; - console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees)); - expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true; - - await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { - await helper.wait.newBlocks(3); - balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address); - - const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle; - console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees)); - expect(glmrFees == 0n).to.be.true; - - balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!; - - const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit; - console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer)); - expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true; - }); - }); - - itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => { - await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { - const asset = { - V2: { - id: { - Concrete: { - parents: 1, - interior: { - X1: {Parachain: UNIQUE_CHAIN}, - }, - }, - }, - fun: { - Fungible: TRANSFER_AMOUNT, - }, - }, - }; - const destination = { - V2: { - parents: 1, - interior: { - X2: [ - {Parachain: UNIQUE_CHAIN}, - {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}}, - ], - }, - }, - }; - - await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited'); - - balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address); - - const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal; - console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees)); - expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true; - - const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address); - - expect(unqRandomAccountAsset).to.be.null; - - balanceForeignUnqTokenFinal = 0n; - - const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal; - console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer)); - expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true; - }); - - await helper.wait.newBlocks(3); - - balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address); - const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle; - expect(actuallyDelivered > 0).to.be.true; - - console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered)); - - const unqFees = TRANSFER_AMOUNT - actuallyDelivered; - console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees)); - expect(unqFees == 0n).to.be.true; - }); - - itSub('Moonbeam can send only up to its balance', async ({helper}) => { - // set Moonbeam's sovereign account's balance - const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS); - const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN); - await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance); - - const moreThanMoonbeamHas = moonbeamBalance * 2n; - - let targetAccountBalance = 0n; - const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); - - const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - moreThanMoonbeamHas, - ); - - let maliciousXcmProgramSent: any; - const maxWaitBlocks = 3; - - // Try to trick Unique - await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgram]); - - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall); - - maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash - && event.outcome.isFailedToTransactAsset); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(0n); - - // But Moonbeam still can send the correct amount - const validTransferAmount = moonbeamBalance / 2n; - const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - validTransferAmount, - ); - - await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, validXcmProgram]); - - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall); - }); - - await helper.wait.newBlocks(maxWaitBlocks); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(validTransferAmount); - }); - - itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => { - const testAmount = 10_000n * (10n ** UNQ_DECIMALS); - const [targetAccount] = await helper.arrange.createAccounts([0n], alice); - - const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - uniqueAssetId, - testAmount, - ); - - const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - testAmount, - ); - - let maliciousXcmProgramFullIdSent: any; - let maliciousXcmProgramHereIdSent: any; - const maxWaitBlocks = 3; - - // Try to trick Unique using full UNQ identification - await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]); - - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall); - - maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - let accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - - // Try to trick Unique using shortened UNQ identification - await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { - const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramHereId]); - - // Needed to bypass the call filter. - const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); - await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall); - - maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - }); -}); - -describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => { - let alice: IKeyringPair; - let randomAccount: IKeyringPair; - - const UNQ_ASSET_ID_ON_ASTAR = 18_446_744_073_709_551_631n; // The value is taken from the live Astar - const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n; // The value is taken from the live Astar - - // Unique -> Astar - const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar. - const unitsPerSecond = 9_451_000_000_000_000_000n; // The value is taken from the live Astar - const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ - const unqToAstarArrived = 9_962_196_000_000_000_000n; // 9.962 ... UNQ, Astar takes a commision in foreign tokens - - // Astar -> Unique - const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ - const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.962_219_600_000_000_000n UNQ - - let balanceAfterUniqueToAstarXCM: bigint; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - alice = await privateKey('//Alice'); - [randomAccount] = await helper.arrange.createAccounts([100n], alice); - console.log('randomAccount', randomAccount.address); - - // Set the default version to wrap the first message to other chains. - await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); - }); - - await usingAstarPlaygrounds(astarUrl, async (helper) => { - if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) { - console.log('1. Create foreign asset and metadata'); - await helper.getSudo().assets.forceCreate( - alice, - UNQ_ASSET_ID_ON_ASTAR, - alice.address, - UNQ_MINIMAL_BALANCE_ON_ASTAR, - ); - - await helper.assets.setMetadata( - alice, - UNQ_ASSET_ID_ON_ASTAR, - 'Unique Network', - 'UNQ', - Number(UNQ_DECIMALS), - ); - - console.log('2. Register asset location on Astar'); - const assetLocation = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }, - }; - - await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]); - - console.log('3. Set UNQ payment for XCM execution on Astar'); - await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]); - } else { - console.log('UNQ is already registered on Astar'); - } - console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)'); - await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance); - }); - }); - - itSub('Should connect and send UNQ to Astar', async ({helper}) => { - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: ASTAR_CHAIN, - }, - }, - }, - }; - - const beneficiary = { - V2: { - parents: 0, - interior: { - X1: { - AccountId32: { - network: 'Any', - id: randomAccount.addressRaw, - }, - }, - }, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - fun: { - Fungible: unqToAstarTransferred, - }, - }, - ], - }; - - // Initial balance is 100 UNQ - const balanceBefore = await helper.balance.getSubstrate(randomAccount.address); - console.log(`Initial balance is: ${balanceBefore}`); - - const feeAssetItem = 0; - await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); - - // Balance after reserve transfer is less than 90 - balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address); - console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`); - console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`); - expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true; - - await usingAstarPlaygrounds(astarUrl, async (helper) => { - await helper.wait.newBlocks(3); - const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address); - const astarBalance = await helper.balance.getSubstrate(randomAccount.address); - - console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`); - console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`); - - expect(xcUNQbalance).to.eq(unqToAstarArrived); - // Astar balance does not changed - expect(astarBalance).to.eq(astarInitialBalance); - }); - }); - - itSub('Should connect to Astar and send UNQ back', async ({helper}) => { - await usingAstarPlaygrounds(astarUrl, async (helper) => { - const destination = { - V2: { - parents: 1, - interior: { - X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }, - }; - - const beneficiary = { - V2: { - parents: 0, - interior: { - X1: { - AccountId32: { - network: 'Any', - id: randomAccount.addressRaw, - }, - }, - }, - }, - }; - - const assets = { - V2: [ - { - id: { - Concrete: { - parents: 1, - interior: { - X1: { - Parachain: UNIQUE_CHAIN, - }, - }, - }, - }, - fun: { - Fungible: unqFromAstarTransfered, - }, - }, - ], - }; - - // Initial balance is 1 ASTR - const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address); - console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`); - expect(balanceASTRbefore).to.eq(astarInitialBalance); - - const feeAssetItem = 0; - // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw - await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]); - - const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address); - const balanceAstar = await helper.balance.getSubstrate(randomAccount.address); - console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`); - - // Assert: xcUNQ balance correctly decreased - expect(xcUNQbalance).to.eq(unqOnAstarLeft); - // Assert: ASTR balance is 0.996... - expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n); - }); - - await helper.wait.newBlocks(3); - const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address); - console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`); - expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered); - }); - - itSub('Astar can send only up to its balance', async ({helper}) => { - // set Astar's sovereign account's balance - const astarBalance = 10000n * (10n ** UNQ_DECIMALS); - const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN); - await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance); - - const moreThanAstarHas = astarBalance * 2n; - - let targetAccountBalance = 0n; - const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); - - const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - moreThanAstarHas, - ); - - let maliciousXcmProgramSent: any; - const maxWaitBlocks = 3; - - // Try to trick Unique - await usingAstarPlaygrounds(astarUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram); - - maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash - && event.outcome.isFailedToTransactAsset); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(0n); - - // But Astar still can send the correct amount - const validTransferAmount = astarBalance / 2n; - const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - validTransferAmount, - ); - - await usingAstarPlaygrounds(astarUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, validXcmProgram); - }); - - await helper.wait.newBlocks(maxWaitBlocks); - - targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(targetAccountBalance).to.be.equal(validTransferAmount); - }); - - itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => { - const testAmount = 10_000n * (10n ** UNQ_DECIMALS); - const [targetAccount] = await helper.arrange.createAccounts([0n], alice); - - const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - uniqueAssetId, - testAmount, - ); - - const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( - targetAccount.addressRaw, - { - Concrete: { - parents: 0, - interior: 'Here', - }, - }, - testAmount, - ); - - let maliciousXcmProgramFullIdSent: any; - let maliciousXcmProgramHereIdSent: any; - const maxWaitBlocks = 3; - - // Try to trick Unique using full UNQ identification - await usingAstarPlaygrounds(astarUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramFullId); - - maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - let accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - - // Try to trick Unique using shortened UNQ identification - await usingAstarPlaygrounds(astarUrl, async (helper) => { - await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramHereId); - - maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); - }); - - await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash - && event.outcome.isUntrustedReserveLocation); - - accountBalance = await helper.balance.getSubstrate(targetAccount.address); - expect(accountBalance).to.be.equal(0n); - }); -}); --- /dev/null +++ b/js-packages/tests/sub/appPromotion/appPromotion.seqtest.ts @@ -0,0 +1,82 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, Pallets, requirePalletsOrSkip} from '../../util/index.js'; +import {expect} from '../../eth/util/index.js'; + +let superuser: IKeyringPair; +let donor: IKeyringPair; +let palletAdmin: IKeyringPair; + +describe('App promotion', () => { + before(async function () { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.AppPromotion]); + superuser = await privateKey('//Alice'); + donor = await privateKey({url: import.meta.url}); + palletAdmin = await privateKey('//PromotionAdmin'); + const api = helper.getApi(); + await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address}))); + }); + }); + + after(async function () { + await usingPlaygrounds(async (helper) => { + if(helper.fetchMissingPalletNames([Pallets.AppPromotion]).length != 0) return; + const api = helper.getApi(); + await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address}))); + }); + }); + + describe('admin adress', () => { + itSub('can be set by sudo only', async ({helper}) => { + const api = helper.getApi(); + const [nonAdmin] = await helper.arrange.createAccounts([10n], donor); + // nonAdmin can not set admin not from himself nor as a sudo + await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address}))).to.be.rejected; + await expect(helper.signTransaction(nonAdmin, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address})))).to.be.rejected; + }); + + itSub('can be any valid CrossAccountId', async ({helper}) => { + // We are not going to set an eth address as a sponsor, + // but we do want to check, it doesn't break anything; + const api = helper.getApi(); + const [account] = await helper.arrange.createAccounts([10n], donor); + const ethAccount = helper.address.substrateToEth(account.address); + // Alice sets Ethereum address as a sudo. Then Substrate address back... + await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Ethereum: ethAccount})))).to.be.fulfilled; + await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.fulfilled; + + // ...It doesn't break anything; + const collection = await helper.nft.mintCollection(account, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + await expect(helper.signTransaction(account, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; + }); + + itSub('can be reassigned', async ({helper}) => { + const api = helper.getApi(); + const [oldAdmin, newAdmin, collectionOwner] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + + await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: oldAdmin.address})))).to.be.fulfilled; + await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: newAdmin.address})))).to.be.fulfilled; + await expect(helper.signTransaction(oldAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; + + await expect(helper.signTransaction(newAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled; + }); + }); +}); + --- /dev/null +++ b/js-packages/tests/sub/appPromotion/appPromotion.test.ts @@ -0,0 +1,1008 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import { + itSub, usingPlaygrounds, Pallets, requirePalletsOrSkip, LOCKING_PERIOD, UNLOCKING_PERIOD, +} from '../../util/index.js'; +import {DevUniqueHelper} from '@unique/playgrounds/unique.dev.js'; +import {itEth, expect, SponsoringMode} from '../../eth/util/index.js'; + +let donor: IKeyringPair; +let palletAdmin: IKeyringPair; +let nominal: bigint; +let palletAddress: string; +let accounts: IKeyringPair[]; +let usedAccounts: IKeyringPair[] = []; + +async function getAccounts(accountsNumber: number, balance?: bigint) { + let accs: IKeyringPair[] = []; + if(balance) { + await usingPlaygrounds(async (helper) => { + accs = await helper.arrange.createAccounts(new Array(accountsNumber).fill(balance), donor); + }); + } else { + accs = accounts.splice(0, accountsNumber); + } + usedAccounts.push(...accs); + return accs; +} +// App promotion periods: +// LOCKING_PERIOD = 12 blocks of relay +// UNLOCKING_PERIOD = 6 blocks of parachain + +describe('App promotion', () => { + before(async function () { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.AppPromotion]); + donor = await privateKey({url: import.meta.url}); + palletAddress = helper.arrange.calculatePalletAddress('appstake'); + palletAdmin = await privateKey('//PromotionAdmin'); + + nominal = helper.balance.getOneTokenNominal(); + + const accountBalances = new Array(200).fill(1000n); + accounts = await helper.arrange.createAccounts(accountBalances, donor); // create accounts-pool to speed up tests + }); + }); + + afterEach(async () => { + await usingPlaygrounds(async (helper) => { + let unstakeTxs = []; + for(const account of usedAccounts) { + if(unstakeTxs.length === 3) { + await Promise.all(unstakeTxs); + unstakeTxs = []; + } + unstakeTxs.push(helper.staking.unstakeAll(account)); + } + await Promise.all(unstakeTxs); + usedAccounts = []; + expect(await helper.staking.getTotalStaked()).to.eq(0n); // there are no active stakes after each test + // Make sure previousCalculatedRecord is None to avoid problem with payout stakers; + await helper.admin.payoutStakers(palletAdmin, 100); + expect((await helper.getApi().query.appPromotion.previousCalculatedRecord() as any).isNone).to.be.true; + }); + }); + + describe('stake extrinsic', () => { + itSub('should "freeze" staking balance, add it to "staked" map, and increase "totalStaked" amount', async ({helper}) => { + const [staker, recepient] = await getAccounts(2); + const totalStakedBefore = await helper.staking.getTotalStaked(); + + // Minimum stake amount is 100: + await expect(helper.staking.stake(staker, 100n * nominal - 1n)).to.be.rejected; + await helper.staking.stake(staker, 100n * nominal); + + // Staker balance is: frozen: 100, reserved: 0n... + // ...so he can not transfer 900 + expect(await helper.balance.getSubstrateFull(staker.address)).to.contain({frozen: 100n * nominal, reserved: 0n}); + expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: 100n * nominal}]); + await expect(helper.balance.transferToSubstrate(staker, recepient.address, 900n * nominal)).to.be.rejectedWith(/^Token: Frozen$/); + + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(100n * nominal); + expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); + // it is potentially flaky test. Promotion can credited some tokens. Maybe we need to use closeTo? + expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore + 100n * nominal); // total tokens amount staked in app-promotion increased + + await helper.staking.stake(staker, 200n * nominal); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(300n * nominal); + const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(totalStakedPerBlock[0].amount).to.equal(100n * nominal); + expect(totalStakedPerBlock[1].amount).to.equal(200n * nominal); + }); + + [ + {unstake: 'unstakeAll' as const}, + {unstake: 'unstakePartial' as const}, + ].map(testCase => { + itSub(`[${testCase.unstake}] should allow to create maximum 10 stakes for account`, async ({helper}) => { + const [staker] = await getAccounts(1, 2000n); + const ONE_STAKE = 100n * nominal; + for(let i = 0; i < 10; i++) { + await helper.staking.stake(staker, ONE_STAKE); + } + + // can have 10 stakes + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(1000n * nominal); + expect(await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).to.have.length(10); + + await expect(helper.staking.stake(staker, ONE_STAKE)).to.be.rejectedWith('appPromotion.NoPermission'); + + // After unstake can stake again + + // CASE 1: unstakeAll + if(testCase.unstake === 'unstakeAll') { + await helper.staking.unstakeAll(staker); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0); + await helper.staking.stake(staker, 100n * nominal); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.equal(100n * nominal); + } + // CASE 2: unstakePartial + else { + await helper.staking.unstakePartial(staker, ONE_STAKE); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(9); + await helper.staking.stake(staker, 100n * nominal); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(10); + await expect(helper.staking.stake(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.NoPermission'); + await helper.staking.unstakePartial(staker, 150n * nominal); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(9); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.equal(850n * nominal); + } + }); + }); + // TODO: Now AppPromo makes freezes. Probably this test should be changed\removed. + itSub.skip('should allow to stake() if balance is locked with different id', async ({helper}) => { + const [staker] = await getAccounts(1); + + // staker has tokens locked with vesting id: + await helper.balance.vestedTransfer(donor, staker.address, {start: 0n, period: 1n, periodCount: 1n, perPeriod: 200n * nominal}); + expect(await helper.balance.getSubstrateFull(staker.address)) + .to.deep.contain({free: 1200n * nominal, frozen: 200n * nominal, reserved: 0n}); + + // Locked balance can be staked. staker can stake 1200 tokens (minus fee): + await helper.staking.stake(staker, 1000n * nominal); + await helper.staking.stake(staker, 199n * nominal); + // check balances + expect(await helper.balance.getLocked(staker.address)).to.deep.eq([{id: 'ormlvest', amount: 200n * nominal, reasons: 'All'}]); + expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: 1199n * nominal}]); + expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 1199n * nominal}); + expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(1199n); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(1199n * nominal); + + // staker can unstake + await helper.staking.unstakeAll(staker); + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(1199n * nominal); + const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); + await helper.wait.forParachainBlockNumber(pendingUnstake.block); + + // check balances + expect(await helper.balance.getLocked(staker.address)).to.deep.eq([{id: 'ormlvest', amount: 200n * nominal, reasons: 'All'}]); + expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 200n * nominal}); + expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(1199n); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n); + + // staker can transfer balances now + await helper.balance.transferToSubstrate(staker, donor.address, 900n * nominal); + }); + + itSub('should not allow to stake(), if stake amount is more than total free balance minus locked by staking', async ({helper}) => { + const [staker] = await getAccounts(1); + + // Can't stake full balance because Alice needs to pay some fee + await expect(helper.staking.stake(staker, 1000n * nominal)).to.be.rejected; // With('Arithmetic') + await helper.staking.stake(staker, 500n * nominal); + + // Can't stake 500 tkn because Alice has Less than 500 transferable; + await expect(helper.staking.stake(staker, 500n * nominal)).to.be.rejected; // With('Arithmetic'); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(500n * nominal); + }); + + itSub('for different accounts in one block is possible', async ({helper}) => { + const crowd = await getAccounts(4); + + const crowdStartsToStake = crowd.map(user => helper.staking.stake(user, 100n * nominal)); + await expect(Promise.all(crowdStartsToStake)).to.be.fulfilled; + + const crowdStakes = await Promise.all(crowd.map(address => helper.staking.getTotalStaked({Substrate: address.address}))); + expect(crowdStakes).to.deep.equal([100n * nominal, 100n * nominal, 100n * nominal, 100n * nominal]); + }); + }); + + describe('Unstaking', () => { + [ + {method: 'unstakeAll' as const}, + {method: 'unstakePartial' as const}, + ].map(testCase => { + itSub(`[${testCase.method}] should move tokens to "pendingUnstake" and subtract it from totalStaked`, async ({helper}) => { + const [staker, recepient] = await getAccounts(2); + const totalStakedBefore = await helper.staking.getTotalStaked(); + const STAKE_AMOUNT = 900n * nominal; + + await helper.staking.stake(staker, STAKE_AMOUNT); + testCase.method === 'unstakeAll' + ? await helper.staking.unstakeAll(staker) + : await helper.staking.unstakePartial(staker, STAKE_AMOUNT); + + // Right after unstake tokens are still locked + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0); + expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: STAKE_AMOUNT}]); + expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: STAKE_AMOUNT}); + // Staker can not transfer + await expect(helper.balance.transferToSubstrate(staker, recepient.address, 100n * nominal)).to.be.rejectedWith(/^Token: Frozen$/); + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(STAKE_AMOUNT); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); + expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore); + }); + }); + + [ + {method: 'unstakeAll' as const}, + {method: 'unstakePartial' as const}, + ].map(testCase => { + itSub(`[${testCase.method}] should unlock balance after unlocking period ends and remove it from "pendingUnstake"`, async ({helper}) => { + const [staker] = await getAccounts(1); + await helper.staking.stake(staker, 100n * nominal); + testCase.method === 'unstakeAll' + ? await helper.staking.unstakeAll(staker) + : await helper.staking.unstakePartial(staker, 100n * nominal); + const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); + + // Wait for unstaking period. Balance now free ~1000; reserved, frozen, miscFrozeb: 0n + await helper.wait.forParachainBlockNumber(pendingUnstake.block); + expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n}); + expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); + + // staker can transfer: + await helper.balance.transferToSubstrate(staker, donor.address, 998n * nominal); + expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(1n); + }); + }); + + [ + {method: 'unstakeAll' as const}, + {method: 'unstakePartial' as const}, + ].map(testCase => { + itSub(`[${testCase.method}] should successfully unstake multiple stakes`, async ({helper}) => { + const [staker] = await getAccounts(1); + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.stake(staker, 200n * nominal); + await helper.staking.stake(staker, 300n * nominal); + + // staked: [100, 200, 300]; unstaked: 0 + let totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address}); + let pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); + let stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(totalPendingUnstake).to.be.deep.equal(0n); + expect(pendingUnstake).to.be.deep.equal([]); + expect(stakes[0].amount).to.equal(100n * nominal); + expect(stakes[1].amount).to.equal(200n * nominal); + expect(stakes[2].amount).to.equal(300n * nominal); + + // Can unstake multiple stakes + testCase.method === 'unstakeAll' + ? await helper.staking.unstakeAll(staker) + : await helper.staking.unstakePartial(staker, 600n * nominal); + + pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); + totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address}); + stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(totalPendingUnstake).to.be.equal(600n * nominal); + expect(stakes).to.be.deep.equal([]); + expect(pendingUnstake[0].amount).to.equal(600n * nominal); + + expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 600n * nominal}); + expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); + await helper.wait.forParachainBlockNumber(pendingUnstake[0].block); + expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n}); + expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); + }); + }); + + [ + {method: 'unstakeAll' as const}, + {method: 'unstakePartial' as const}, + ].map(testCase => { + itSub(`[${testCase.method}] should not have any effects if no active stakes`, async ({helper}) => { + const [staker] = await getAccounts(1); + + // unstake has no effect if no stakes at all + testCase.method === 'unstakeAll' + ? await helper.staking.unstakeAll(staker) + : await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance'); + + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(0n); + expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); // TODO bigint closeTo helper + + // TODO stake() unstake() waitUnstaked() unstake(); + + // can't unstake if there are only pendingUnstakes + await helper.staking.stake(staker, 100n * nominal); + + if(testCase.method === 'unstakeAll') { + await helper.staking.unstakeAll(staker); + await helper.staking.unstakeAll(staker); + } else { + await helper.staking.unstakePartial(staker, 100n * nominal); + await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance'); + } + + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0); + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); + }); + }); + + [ + {method: 'unstakeAll' as const}, + {method: 'unstakePartial' as const}, + ].map(testCase => { + itSub(`[${testCase.method}] should create different pending-unlock for each unlocking stake`, async ({helper}) => { + const [staker] = await getAccounts(1); + await helper.staking.stake(staker, 100n * nominal); + testCase.method === 'unstakeAll' + ? await helper.staking.unstakeAll(staker) + : await helper.staking.unstakePartial(staker, 100n * nominal); + await helper.staking.stake(staker, 120n * nominal); + testCase.method === 'unstakeAll' + ? await helper.staking.unstakeAll(staker) + : await helper.staking.unstakePartial(staker, 120n * nominal); + + const unstakingPerBlock = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); + expect(unstakingPerBlock).has.length(2); + expect(unstakingPerBlock[0].amount).to.equal(100n * nominal); + expect(unstakingPerBlock[1].amount).to.equal(120n * nominal); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.equal(0); + }); + }); + + [ + {method: 'unstakeAll' as const}, + {method: 'unstakePartial' as const}, + ].map(testCase => { + itSub(`[${testCase.method}] should be possible for 3 accounts in one block`, async ({helper}) => { + const stakers = await getAccounts(3); + + await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal))); + await Promise.all(stakers.map(staker => testCase.method === 'unstakeAll' + ? helper.staking.unstakeAll(staker) + : helper.staking.unstakePartial(staker, 100n * nominal))); + + await Promise.all(stakers.map(async (staker) => { + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); + })); + }); + }); + + itSub('should not be possible for more than 3 accounts in one block', async ({helper}) => { + if(!await helper.arrange.isDevNode()) { + const stakers = await getAccounts(10); + + await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal))); + const unstakingResults = await Promise.allSettled(stakers.map((staker, i) => i % 2 === 0 + ? helper.staking.unstakeAll(staker) + : helper.staking.unstakePartial(staker, 100n * nominal))); + + const successfulUnstakes = unstakingResults.filter(result => result.status === 'fulfilled'); + expect(successfulUnstakes).to.have.length(3); + } + }); + + itSub('Cannot partially unstake more than staked', async ({helper}) => { + const [staker] = await getAccounts(1); + // Staker stakes 300: + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.stake(staker, 200n * nominal); + + // cannot usntake 300.00000...1 + await expect(helper.staking.unstakePartial(staker, 300n * nominal + 1n)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance'); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(2); + + await helper.staking.unstakePartial(staker, 150n * nominal); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(1); + await expect(helper.staking.unstakePartial(staker, 150n * nominal + 1n)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance'); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(1); + + // nothing broken, can unstake full amount: + await helper.staking.unstakePartial(staker, 150n * nominal); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(0); + }); + + itSub('Can partially unstake arbitrary amount', async ({helper}) => { + const [staker] = await getAccounts(1); + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.stake(staker, 200n * nominal); + + // 0. Staker cannot unstake negative amount + await expect(helper.staking.unstakePartial(staker, -1n)).to.be.rejected; + + // 1. Staker can unstake 0 wei + await helper.staking.unstakePartial(staker, 0n); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(300n * nominal); + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(0n); + + // 2. Staker can unstake 1 wei + await helper.staking.unstakePartial(staker, 1n); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(300n * nominal - 1n); + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(1n); + // 2.1 The oldest stake decreased: + let [stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(stake1.amount).to.eq(100n * nominal - 1n); + expect(stake2.amount).to.eq(200n * nominal); + + // 3. Staker can unstake all but 1 wei + await helper.staking.unstakePartial(staker, 100n * nominal - 2n); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(200n * nominal + 1n); + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(100n * nominal - 1n); + [stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(stake1.amount).to.eq(1n); + expect(stake2.amount).to.eq(200n * nominal); + }); + + itSub('can mix different type of unstakes', async ({helper}) => { + const [staker] = await getAccounts(1); + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.stake(staker, 200n * nominal); + + await helper.staking.unstakePartial(staker, 50n * nominal); + await helper.staking.unstakeAll(staker); + expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n); + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(300n * nominal); + + const [_unstake1, unstake2] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); + await helper.wait.forParachainBlockNumber(unstake2.block); + + expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([]); + expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n}); + expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(999n); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n); + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(0n); + expect(await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address})).to.deep.eq([]); + }); + }); + + describe('collection sponsoring', () => { + itSub('should actually sponsor transactions', async ({helper}) => { + const api = helper.getApi(); + const [collectionOwner, tokenSender, receiver] = await getAccounts(3); + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'Name', description: 'Description', tokenPrefix: 'Prefix', limits: {sponsorTransferTimeout: 0}}); + const token = await collection.mintToken(collectionOwner, {Substrate: tokenSender.address}); + await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId)); + const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress); + + await token.transfer(tokenSender, {Substrate: receiver.address}); + expect (await token.getOwner()).to.be.deep.equal({Substrate: receiver.address}); + const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress); + + // senders balance the same, transaction has sponsored + expect (await helper.balance.getSubstrate(tokenSender.address)).to.be.equal(1000n * nominal); + expect (palletBalanceBefore > palletBalanceAfter).to.be.true; + }); + + itSub('can not be set by non admin', async ({helper}) => { + const api = helper.getApi(); + const [collectionOwner, nonAdmin] = await getAccounts(2); + + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + + await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; + expect((await collection.getData())?.raw.sponsorship).to.equal('Disabled'); + }); + + itSub('should set pallet address as confirmed admin', async ({helper}) => { + const api = helper.getApi(); + const [collectionOwner, oldSponsor] = await getAccounts(2); + + // Can set sponsoring for collection without sponsor + const collectionWithoutSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'No-sponsor', description: 'New Collection', tokenPrefix: 'Promotion'}); + await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithoutSponsor.collectionId))).to.be.fulfilled; + expect((await collectionWithoutSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); + + // Can set sponsoring for collection with unconfirmed sponsor + const collectionWithUnconfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Unconfirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: oldSponsor.address}}); + expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: oldSponsor.address}); + await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithUnconfirmedSponsor.collectionId))).to.be.fulfilled; + expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); + + // Can set sponsoring for collection with confirmed sponsor + const collectionWithConfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Confirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: oldSponsor.address}}); + await collectionWithConfirmedSponsor.confirmSponsorship(oldSponsor); + await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithConfirmedSponsor.collectionId))).to.be.fulfilled; + expect((await collectionWithConfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); + }); + + itSub('can be overwritten by collection owner', async ({helper}) => { + const api = helper.getApi(); + const [collectionOwner, newSponsor] = await getAccounts(2); + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + const collectionId = collection.collectionId; + + await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionId))).to.be.fulfilled; + + // Collection limits still can be changed by the owner + expect(await collection.setLimits(collectionOwner, {sponsorTransferTimeout: 0})).to.be.true; + expect((await collection.getData())?.raw.limits.sponsorTransferTimeout).to.be.equal(0); + expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); + + // Collection sponsor can be changed too + expect((await collection.setSponsor(collectionOwner, newSponsor.address))).to.be.true; + expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: newSponsor.address}); + }); + + itSub('should not overwrite collection limits set by the owner earlier', async ({helper}) => { + const [owner] = await getAccounts(1); + const api = helper.getApi(); + const limits = {ownerCanDestroy: true, ownerCanTransfer: true, sponsorTransferTimeout: 0}; + const collectionWithLimits = await helper.nft.mintCollection(owner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits}); + + await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithLimits.collectionId))).to.be.fulfilled; + expect((await collectionWithLimits.getData())?.raw.limits).to.be.deep.contain(limits); + }); + + itSub('should reject transaction if collection doesn\'t exist', async ({helper}) => { + const api = helper.getApi(); + const [collectionOwner] = await getAccounts(1); + + // collection has never existed + await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(999999999))).to.be.rejected; + // collection has been burned + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + await collection.burn(collectionOwner); + + await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected; + }); + }); + + describe('stopSponsoringCollection', () => { + itSub('can not be called by non-admin', async ({helper}) => { + const api = helper.getApi(); + const [collectionOwner, nonAdmin] = await getAccounts(2); + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + + await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled; + + await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.rejected; + expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); + }); + + itSub('should set sponsoring as disabled', async ({helper}) => { + const api = helper.getApi(); + const [collectionOwner, recepient] = await getAccounts(2); + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits: {sponsorTransferTimeout: 0}}); + const token = await collection.mintToken(collectionOwner, {Substrate: collectionOwner.address}); + + await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId)); + await helper.signTransaction(palletAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId)); + + expect((await collection.getData())?.raw.sponsorship).to.be.equal('Disabled'); + + // Transactions are not sponsored anymore: + const ownerBalanceBefore = await helper.balance.getSubstrate(collectionOwner.address); + await token.transfer(collectionOwner, {Substrate: recepient.address}); + const ownerBalanceAfter = await helper.balance.getSubstrate(collectionOwner.address); + expect(ownerBalanceAfter < ownerBalanceBefore).to.be.equal(true); + }); + + itSub('should not affect collection which is not sponsored by pallete', async ({helper}) => { + const api = helper.getApi(); + const [collectionOwner] = await getAccounts(1); + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: collectionOwner.address}}); + await collection.confirmSponsorship(collectionOwner); + + await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: collectionOwner.address}); + }); + + itSub('should reject transaction if collection does not exist', async ({helper}) => { + const [collectionOwner] = await getAccounts(1); + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + + await collection.burn(collectionOwner); + await expect(helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringCollection', [collection.collectionId], true)).to.be.rejectedWith('common.CollectionNotFound'); + await expect(helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringCollection', [999_999_999], true)).to.be.rejectedWith('common.CollectionNotFound'); + }); + }); + + describe('contract sponsoring', () => { + itEth('should set palletes address as a sponsor', async ({helper}) => { + const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); + const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); + + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address]); + + expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true; + expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner); + expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({ + confirmed: { + substrate: palletAddress, + }, + }); + }); + + itEth('should overwrite sponsoring mode and existed sponsor', async ({helper}) => { + const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); + const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); + + await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled; + + // Contract is self sponsored + expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.be.deep.equal({ + confirmed: { + ethereum: flipper.options.address.toLowerCase(), + }, + }); + + // set promotion sponsoring + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true); + + // new sponsor is pallet address + expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true; + expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner); + expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({ + confirmed: { + substrate: palletAddress, + }, + }); + }); + + itEth('can be overwritten by contract owner', async ({helper}) => { + const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); + const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); + + // contract sponsored by pallet + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true); + + // owner sets self sponsoring + await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected; + + expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true; + expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner); + expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({ + confirmed: { + ethereum: flipper.options.address.toLowerCase(), + }, + }); + }); + + itEth('can not be set by non admin', async ({helper}) => { + const [nonAdmin] = await getAccounts(1); + const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); + const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); + + await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled; + + // nonAdmin calls sponsorContract + await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true)).to.be.rejectedWith('appPromotion.NoPermission'); + + // contract still self-sponsored + expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({ + confirmed: { + ethereum: flipper.options.address.toLowerCase(), + }, + }); + }); + + itEth('should actually sponsor transactions', async ({helper}) => { + // Contract caller + const caller = await helper.eth.createAccountWithBalance(donor, 1000n); + const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress); + + // Deploy flipper + const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner); + const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); + + // Owner sets to sponsor every tx + await contractHelper.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: contractOwner}); + await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner}); + await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address, 1000n); // transferBalanceToEth(api, alice, flipper.options.address, 1000n); + + // Set promotion to the Flipper + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true); + + // Caller calls Flipper + await flipper.methods.flip().send({from: caller}); + expect(await flipper.methods.getValue().call()).to.be.true; + + // The contracts and caller balances have not changed + const callerBalance = await helper.balance.getEthereum(caller); + const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address); + expect(callerBalance).to.be.equal(1000n * nominal); + expect(1000n * nominal === contractBalanceAfter).to.be.true; + + // The pallet balance has decreased + const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress); + expect(palletBalanceAfter < palletBalanceBefore).to.be.true; + }); + }); + + describe('stopSponsoringContract', () => { + itEth('should remove pallet address from contract sponsors', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor, 1000n); + const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); + await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address); + const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); + + await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner}); + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true); + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address], true); + + expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.false; + expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner); + expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({ + disabled: null, + }); + + await flipper.methods.flip().send({from: caller}); + expect(await flipper.methods.getValue().call()).to.be.true; + + const callerBalance = await helper.balance.getEthereum(caller); + const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address); + + // caller payed for call + expect(1000n * nominal > callerBalance).to.be.true; + expect(contractBalanceAfter).to.be.equal(100n * nominal); + }); + + itEth('can not be called by non-admin', async ({helper}) => { + const [nonAdmin] = await getAccounts(1); + const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); + + await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address]); + await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address])) + .to.be.rejectedWith(/appPromotion\.NoPermission/); + }); + + itEth('should not affect a contract which is not sponsored by pallete', async ({helper}) => { + const [nonAdmin] = await getAccounts(1); + const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase(); + const flipper = await helper.eth.deployFlipper(contractOwner); + const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner); + await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled; + + await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address], true)).to.be.rejectedWith('appPromotion.NoPermission'); + }); + }); + + describe('payoutStakers', () => { + itSub('can not be called by non admin', async ({helper}) => { + const [nonAdmin] = await getAccounts(1); + await expect(helper.admin.payoutStakers(nonAdmin, 100)).to.be.rejectedWith('appPromotion.NoPermission'); + }); + + itSub('should increase total staked', async ({helper}) => { + const [staker] = await getAccounts(1); + const totalStakedBefore = await helper.staking.getTotalStaked(); + await helper.staking.stake(staker, 100n * nominal); + + // Wait for rewards and pay + const [stakedInBlock] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock.block)); + + const payout = await helper.admin.payoutStakers(palletAdmin, 100); + const totalPayout = payout.reduce((prev, payout) => prev + payout.payout, 0n); + const stakerReward = payout.find(p => p.staker === staker.address); + + expect(stakerReward?.payout).to.eq(calculateIncome(100n * nominal) - (100n * nominal)); + + const totalStakedAfter = await helper.staking.getTotalStaked(); + expect(totalStakedAfter).to.equal(totalStakedBefore + (100n * nominal) + totalPayout); + // staker can unstake + await helper.staking.unstakeAll(staker); + expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedAfter - calculateIncome(100n * nominal)); + }); + + itSub('should credit 0.05% for staking period', async ({helper}) => { + const [staker] = await getAccounts(1); + + await waitPromotionPeriodDoesntEnd(helper); + + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.stake(staker, 200n * nominal); + + // wait rewards are available: + const [_stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block)); + + const payoutToStaker = (await helper.admin.payoutStakers(palletAdmin, 100)).find((payout) => payout.staker === staker.address)!.payout; + expect(payoutToStaker + 300n * nominal).to.equal(calculateIncome(300n * nominal)); + + const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + const income1 = calculateIncome(100n * nominal); + const income2 = calculateIncome(200n * nominal); + expect(totalStakedPerBlock[0].amount).to.equal(income1); + expect(totalStakedPerBlock[1].amount).to.equal(income2); + + const stakerBalance = await helper.balance.getSubstrateFull(staker.address); + expect(stakerBalance).to.contain({frozen: income1 + income2, reserved: 0n}); + expect(stakerBalance.free / nominal).to.eq(999n); + }); + + itSub('shoud be paid for more than one period if payments was missed', async ({helper}) => { + const [staker] = await getAccounts(1); + + await helper.staking.stake(staker, 100n * nominal); + // wait for two rewards are available: + let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD); + + await helper.admin.payoutStakers(palletAdmin, 100); + [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + const frozenBalanceShouldBe = calculateIncome(100n * nominal, 2); + expect(stake.amount).to.be.equal(frozenBalanceShouldBe); + + const stakerFullBalance = await helper.balance.getSubstrateFull(staker.address); + + expect(stakerFullBalance).to.contain({reserved: 0n, frozen: frozenBalanceShouldBe}); + }); + + itSub('should not be credited for pending-unstaked tokens', async ({helper}) => { + // staker unstakes before rewards been payed + const [staker] = await getAccounts(1); + await helper.staking.stake(staker, 100n * nominal); + const [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD); + await helper.staking.unstakeAll(staker); + + // so he did not receive any rewards + const totalBalanceBefore = await helper.balance.getSubstrate(staker.address); + await helper.admin.payoutStakers(palletAdmin, 100); + const totalBalanceAfter = await helper.balance.getSubstrate(staker.address); + + expect(totalBalanceBefore).to.be.equal(totalBalanceAfter); + }); + + itSub('should bring compound interest', async ({helper}) => { + const [staker] = await getAccounts(1); + + await helper.staking.stake(staker, 100n * nominal); + + let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block)); + + await helper.admin.payoutStakers(palletAdmin, 100); + [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(stake.amount).to.equal(calculateIncome(100n * nominal)); + + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD); + await helper.admin.payoutStakers(palletAdmin, 100); + [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + expect(stake.amount).to.equal(calculateIncome(100n * nominal, 2)); + }); + + itSub('can calculate reward for tiny stake', async ({helper}) => { + const [staker] = await getAccounts(1); + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.unstakePartial(staker, 100n * nominal - 1n); + + const [_stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block)); + + const stakerPayout = await payUntilRewardFor(staker.address, helper); + expect(stakerPayout.stake).to.eq(100n * nominal + 1n); + }); + + itSub('can eventually pay all rewards', async ({helper}) => { + const stakers = await getAccounts(30); + // Create 30 stakes: + await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal))); + + let unstakingTxs = []; + for(const staker of stakers) { + if(unstakingTxs.length == 3) { + await Promise.all(unstakingTxs); + unstakingTxs = []; + } + unstakingTxs.push(helper.staking.unstakePartial(staker, 100n * nominal - 1n)); + } + + const [staker] = await getAccounts(1); + await helper.staking.stake(staker, 100n * nominal); + const [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block)); + + let payouts; + do { + payouts = await helper.admin.payoutStakers(palletAdmin, 20); + } while(payouts.length !== 0); + }); + }); + + describe('events', () => { + [ + {method: 'unstakePartial' as const}, + {method: 'unstakeAll' as const}, + ].map(testCase => { + itSub(testCase.method, async ({helper}) => { + const unstakeParams: [] | [bigint] = testCase.method === 'unstakePartial' + ? [100n * nominal - 1n] + : []; + const [staker] = await getAccounts(1); + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.stake(staker, 200n * nominal); + const {result} = await helper.executeExtrinsic(staker, `api.tx.appPromotion.${testCase.method}`, unstakeParams); + + const event = result.events.find(e => e.event.section === 'appPromotion' && e.event.method === 'Unstake'); + const unstakerEvents = event?.event.data[0].toString(); + const unstakedEvents = BigInt(event?.event.data[1].toString()); + expect(unstakerEvents).to.eq(staker.address); + expect(unstakedEvents).to.eq(testCase.method === 'unstakeAll' ? 300n * nominal : 100n * nominal - 1n); + }); + }); + + itSub('stake', async ({helper}) => { + const [staker] = await getAccounts(1); + const {result} = await helper.executeExtrinsic(staker, 'api.tx.appPromotion.stake', [100n * nominal]); + + const event = result.events.find(e => e.event.section === 'appPromotion' && e.event.method === 'Stake'); + const stakerEvents = event?.event.data[0].toString(); + const stakedEvents = BigInt(event?.event.data[1].toString()); + expect(stakerEvents).to.eq(staker.address); + expect(stakedEvents).to.eq(100n * nominal); + }); + + // Flaky + itSub.skip('payoutStakers', async ({helper}) => { + const [staker1, staker2] = await getAccounts(2); + const STAKE1 = 100n * nominal; + const STAKE2 = 200n * nominal; + await helper.staking.stake(staker1, STAKE1); + await helper.staking.stake(staker2, STAKE2); + + const [stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker2.address}); + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block)); + + const results = await helper.admin.payoutStakers(palletAdmin, 100); + const stakersEvents = results.filter(ev => ev.staker === staker1.address || ev.staker === staker2.address); + expect(stakersEvents).has.length(2); + expect(stakersEvents).has.not.ordered.members([ + {staker: staker1.address, stake: STAKE1, payout: calculateIncome(STAKE1) - STAKE1}, + {staker: staker2.address, stake: STAKE2, payout: calculateIncome(STAKE2) - STAKE2}, + ]); + }); + }); +}); + + +// Sometimes is is required to make a cycle in order for the payment to be calculated for a specific account +async function payUntilRewardFor(account: string, helper: DevUniqueHelper) { + for(let i = 0; i < 3; i++) { + const payouts = await helper.admin.payoutStakers(palletAdmin, 100); + const accountPayout = payouts.find(p => p.staker === account); + if(accountPayout) return accountPayout; + } + throw Error(`Cannot find payout for ${account}`); +} + +function calculateIncome(base: bigint, iter = 0, calcPeriod: bigint = UNLOCKING_PERIOD): bigint { + const DAY = 7200n; + const ACCURACY = 1_000_000_000n; + // 5n / 10_000n = 0.05% p/day + const income = base + base * (ACCURACY * (calcPeriod * 5n) / (10_000n * DAY)) / ACCURACY ; + + if(iter > 1) { + return calculateIncome(income, iter - 1, calcPeriod); + } else return income; +} + +function rewardAvailableInBlock(stakedInBlock: bigint) { + if(stakedInBlock % LOCKING_PERIOD === 0n) return stakedInBlock + LOCKING_PERIOD; + return (stakedInBlock - stakedInBlock % LOCKING_PERIOD) + (LOCKING_PERIOD * 2n); +} + +// Wait while promotion period less than specified block, to avoid boundary cases +// 0 if this should be the beginning of the period. +async function waitPromotionPeriodDoesntEnd(helper: DevUniqueHelper, waitBlockLessThan = LOCKING_PERIOD / 3n) { + const relayBlockNumber = (await helper.callRpc('api.query.parachainSystem.validationData', [])).value.relayParentNumber.toNumber(); // await helper.chain.getLatestBlockNumber(); + const currentPeriodBlock = BigInt(relayBlockNumber) % LOCKING_PERIOD; + + if(currentPeriodBlock > waitBlockLessThan) { + await helper.wait.forRelayBlockNumber(BigInt(relayBlockNumber) + LOCKING_PERIOD - currentPeriodBlock); + } +} --- /dev/null +++ b/js-packages/tests/sub/nesting/admin.test.ts @@ -0,0 +1,87 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, usingPlaygrounds} from '../../util/index.js'; +import {CrossAccountId} from '@unique/playgrounds/unique.js'; + +describe('Nesting by collection admin', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 10n, 10n], donor); + }); + }); + + [ + {restricted: true}, + {restricted: false}, + ].map(testCase => { + itSub(`can nest tokens if "collectionAdmin" permission set ${testCase.restricted ? ', in restricted mode' : ''}`, async ({helper}) => { + const collectionA = await helper.nft.mintCollection(alice); + await collectionA.addAdmin(alice, {Substrate: bob.address}); + const collectionB = await helper.nft.mintCollection(alice); + await collectionB.addAdmin(alice, {Substrate: bob.address}); + // Collection has permission for collectionAdmin to nest: + await collectionA.setPermissions(alice, {nesting: + {collectionAdmin: true, restricted: testCase.restricted ? [collectionA.collectionId, collectionB.collectionId] : null}, + }); + // Token for nesting in from collectionA: + const targetTokenA = await collectionA.mintToken(alice, {Substrate: charlie.address}); + + // 1. Create an immediately nested tokens: + // 1.1 From own collection: + const nestedTokenA = await collectionA.mintToken(bob, targetTokenA.nestingAccount()); + // 1.2 From different collection: + const nestedTokenB = await collectionB.mintToken(bob, targetTokenA.nestingAccount()); + expect(await nestedTokenA.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address}); + expect(await nestedTokenA.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount())); + expect(await nestedTokenB.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address}); + expect(await nestedTokenB.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount())); + + // 2. Create a token to be nested and nest: + const newNestedTokenA = await collectionA.mintToken(bob); + const newNestedTokenB = await collectionB.mintToken(bob); + // 2.1 From own collection: + await newNestedTokenA.nest(bob, targetTokenA); + // 2.2 From different collection: + await newNestedTokenB.nest(bob, targetTokenA); + expect(await newNestedTokenB.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address}); + expect(await newNestedTokenB.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount())); + }); + }); + + itSub('can operate together with token owner if "collectionAdmin" and "tokenOwner" permissions set', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true, tokenOwner: true}}}); + await collection.addAdmin(alice, {Substrate: bob.address}); + const targetToken = await collection.mintToken(alice, {Substrate: charlie.address}); + + // Admin can create an immediately nested token: + const nestedToken = await collection.mintToken(bob, targetToken.nestingAccount()); + expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address}); + expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); + + // Owner can create and and nest: + const newToken = await collection.mintToken(alice, {Substrate: charlie.address}); + await newToken.nest(charlie, targetToken); + expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address}); + expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); + }); +}); --- /dev/null +++ b/js-packages/tests/sub/nesting/common.test.ts @@ -0,0 +1,164 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js'; +import {CrossAccountId, UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/unique.js'; + +let alice: IKeyringPair; +let bob: IKeyringPair; + +describe('Common nesting tests', () => { + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + [ + {mode: 'nft' as const, restrictedMode: true, requiredPallets: []}, + {mode: 'nft' as const, restrictedMode: false, requiredPallets: []}, + {mode: 'rft' as const, restrictedMode: true, requiredPallets: [Pallets.ReFungible]}, + {mode: 'rft' as const, restrictedMode: false, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => { + itSub.ifWithPallets(`Token owner can nest ${testCase.mode.toUpperCase()} in NFT if "tokenOwner" permission set ${testCase.restrictedMode ? 'in restricted mode': ''}`, testCase.requiredPallets, async ({helper}) => { + // Only NFT can be target for nesting in + const targetNFTCollection = await helper.nft.mintCollection(alice); + const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address}); + + const collectionForNesting = await helper[testCase.mode].mintCollection(bob); + // permissions should be set: + await targetNFTCollection.setPermissions(alice, { + nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null}, + }); + + // 1. Bob can immediately create nested token: + const nestedToken1 = testCase.mode === 'nft' + ? await (collectionForNesting as UniqueNFTCollection).mintToken(bob, targetTokenBob.nestingAccount()) + : await (collectionForNesting as UniqueRFTCollection).mintToken(bob, 10n, targetTokenBob.nestingAccount()); + expect(await nestedToken1.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); + expect(await nestedToken1.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenBob.nestingAccount())); + + // 2. Bob can mint and nest token: + const nestedToken2 = await collectionForNesting.mintToken(bob); + await nestedToken2.nest(bob, targetTokenBob); + expect(await nestedToken2.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); + expect(await nestedToken2.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenBob.nestingAccount())); + }); + }); + + + [ + {restrictedMode: true}, + {restrictedMode: false}, + ].map(testCase => { + itSub(`Token owner can nest FT in NFT if "tokenOwner" permission set ${testCase.restrictedMode ? 'in restricted mode': ''}`, async ({helper}) => { + // Only NFT allows nesting, permissions should be set: + const targetNFTCollection = await helper.nft.mintCollection(alice); + const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address}); + + const collectionForNesting = await helper.ft.mintCollection(bob); + // permissions should be set: + await targetNFTCollection.setPermissions(alice, { + nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null}, + }); + + // 1. Alice can immediately create nested tokens: + await collectionForNesting.mint(bob, 100n, targetTokenBob.nestingAccount()); + expect(await collectionForNesting.getTop10Owners()).deep.eq([CrossAccountId.toLowerCase(targetTokenBob.nestingAccount())]); + expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(100n); + + // 2. Alice can mint and nest token: + await collectionForNesting.mint(bob, 100n); + await collectionForNesting.transfer(bob, targetTokenBob.nestingAccount(), 50n); + expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(150n); + expect(await targetTokenBob.getChildren()).to.be.deep.equal([{collectionId: collectionForNesting.collectionId, tokenId: 0}]); + }); + }); + + [ + {restrictedMode: true}, + {restrictedMode: false}, + ].map(testCase => { + itSub(`Token owner can nest Native FT in NFT if "tokenOwner" permission set ${testCase.restrictedMode ? 'in restricted mode': ''}`, async ({helper}) => { + // Only NFT allows nesting, permissions should be set: + const targetNFTCollection = await helper.nft.mintCollection(alice); + const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address}); + expect(await targetTokenBob.getChildren()).to.be.empty; + + const collectionForNesting = helper.ft.getCollectionObject(0); + // permissions should be set: + await targetNFTCollection.setPermissions(alice, { + nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null}, + }); + + // Bob can nest Native FT into their NFT: + await collectionForNesting.transfer(bob, targetTokenBob.nestingAccount(), 50n); + expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(50n); + // Native FT should't be visible in NFT children: + expect(await targetTokenBob.getChildren()).to.be.deep.equal([]); + }); + }); + + + itSub.ifWithPallets('Owner can unnest tokens using transferFrom', [Pallets.ReFungible], async ({helper}) => { + const collectionToNest = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const tokenA = await collectionToNest.mintToken(alice); + const tokenB = await collectionToNest.mintToken(alice); + + // Create a nested token + const nftCollectionToBeNested = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const rftCollectionToBeNested = await helper.rft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const ftCollectionToBeNested = await helper.ft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const nativeFtCollectionToBeNested = helper.ft.getCollectionObject(0); + + const nestedNFT = await nftCollectionToBeNested.mintToken(alice, tokenA.nestingAccount()); + const nestedRFT = await rftCollectionToBeNested.mintToken(alice, 100n, tokenA.nestingAccount()); + await ftCollectionToBeNested.mint(alice, 100n, tokenA.nestingAccount()); + await nativeFtCollectionToBeNested.transfer(alice, tokenA.nestingAccount(), 100n); + expect(await nestedNFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenA.nestingAccount())); + expect(await nestedRFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenA.nestingAccount())); + expect(await ftCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(100n); + expect(await nativeFtCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(100n); + + expect(await tokenA.getChildren()).to.be.length(3); + expect(await tokenB.getChildren()).to.be.length(0); + + // Transfer the nested token to another token + await nestedNFT.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount()); + await nestedRFT.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n); + await ftCollectionToBeNested.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n); + await nativeFtCollectionToBeNested.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n); + + expect(await nestedNFT.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); + expect(await nestedNFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenB.nestingAccount())); + + expect(await nestedRFT.getBalance(tokenB.nestingAccount())).to.equal(25n); + expect(await nestedRFT.getBalance(tokenA.nestingAccount())).to.equal(75n); + + expect(await ftCollectionToBeNested.getBalance(tokenB.nestingAccount())).to.equal(25n); + expect(await ftCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(75n); + + expect(await nativeFtCollectionToBeNested.getBalance(tokenB.nestingAccount())).to.equal(25n); + expect(await nativeFtCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(75n); + + // RFT, FT, and without native FT + expect(await tokenA.getChildren()).to.be.length(2); + // NFT, RFT, FT, and without native FT + expect(await tokenB.getChildren()).to.be.length(3); + }); +}); --- /dev/null +++ b/js-packages/tests/sub/nesting/e2e.test.ts @@ -0,0 +1,145 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, usingPlaygrounds} from '../../util/index.js'; +import {CrossAccountId} from '@unique/playgrounds/unique.js'; + +describe('Composite nesting tests', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); + }); + }); + + itSub('Checks token children e2e', async ({helper}) => { + const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const collectionB = await helper.ft.mintCollection(alice); + const collectionC = await helper.rft.mintCollection(alice); + const collectionNative = helper.ft.getCollectionObject(0); + + const targetToken = await collectionA.mintToken(alice); + expect((await targetToken.getChildren()).length).to.be.equal(0, 'Children length check at creation'); + + // Create a nested NFT token + const tokenA = await collectionA.mintToken(alice, targetToken.nestingAccount()); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + ]).and.has.length(1); + + // Create then nest + const tokenB = await collectionA.mintToken(alice); + await tokenB.nest(alice, targetToken); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + {tokenId: tokenB.tokenId, collectionId: collectionA.collectionId}, + ]).and.has.length(2); + + // Move token B to a different user outside the nesting tree + await tokenB.unnest(alice, targetToken, {Substrate: bob.address}); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + ]).and.has.length(1); + + // Create a fungible token in another collection and then nest + await collectionB.mint(alice, 10n); + await collectionB.transfer(alice, targetToken.nestingAccount(), 2n); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + {tokenId: 0, collectionId: collectionB.collectionId}, + ]).and.has.length(2); + + // Create a refungible token in another collection and then nest + const tokenC = await collectionC.mintToken(alice, 10n); + await tokenC.transfer(alice, targetToken.nestingAccount(), 2n); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + {tokenId: 0, collectionId: collectionB.collectionId}, + {tokenId: tokenC.tokenId, collectionId: collectionC.collectionId}, + ]).and.has.length(3); + + // Nest native fungible token into another collection + await collectionNative.transfer(alice, targetToken.nestingAccount(), 2n); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + {tokenId: 0, collectionId: collectionB.collectionId}, + {tokenId: tokenC.tokenId, collectionId: collectionC.collectionId}, + ]).and.has.length(3); + + // Burn all nested pieces + await tokenC.burnFrom(alice, targetToken.nestingAccount(), 2n); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + {tokenId: 0, collectionId: collectionB.collectionId}, + ]) + .and.has.length(2); + + // Move part of the fungible token inside token A deeper in the nesting tree + await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n); + expect(await targetToken.getChildren()).to.be.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + {tokenId: 0, collectionId: collectionB.collectionId}, + ]).and.has.length(2); + // Nested token also has children now: + expect(await tokenA.getChildren()).to.have.deep.members([ + {tokenId: 0, collectionId: collectionB.collectionId}, + ]).and.has.length(1); + + // Move the remaining part of the fungible token inside token A deeper in the nesting tree + await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + ]).and.has.length(1); + expect(await tokenA.getChildren()).to.have.deep.members([ + {tokenId: 0, collectionId: collectionB.collectionId}, + ]).and.has.length(1); + }); + + /// TODO review this test + itSub('Performs the full suite: bundles a token, transfers, and unnests', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const targetToken = await collection.mintToken(alice); + + // Create an immediately nested token + const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount()); + expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); + expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); + + // Create a token to be nested + const newToken = await collection.mintToken(alice); + + // Nest + await newToken.nest(alice, targetToken); + expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); + expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); + + // Move bundle to different user + await targetToken.transfer(alice, {Substrate: bob.address}); + expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); + expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); + expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); + expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); + + // Unnest + await newToken.unnest(bob, targetToken, {Substrate: bob.address}); + expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); + expect(await newToken.getOwner()).to.be.deep.equal({Substrate: bob.address}); + }); +}); --- /dev/null +++ b/js-packages/tests/sub/nesting/nesting.negative.test.ts @@ -0,0 +1,294 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js'; +import {UniqueFTCollection, UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection, UniqueRFToken} from '@unique/playgrounds/unique.js'; +import {itEth} from '../../eth/util/index.js'; + +let alice: IKeyringPair; +let bob: IKeyringPair; +let charlie: IKeyringPair; + +describe('Negative Test: Nesting', () => { + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + [ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => { + itSub.ifWithPallets(`Owner cannot nest ${testCase.mode.toUpperCase()} if nesting is disabled`, testCase.requiredPallets, async ({helper}) => { + // Create default collection, permissions are not set: + const aliceNFTCollection = await helper.nft.mintCollection(alice); + const targetToken = await aliceNFTCollection.mintToken(alice); + + const collectionForNesting = await helper[testCase.mode].mintCollection(alice); + + // 1. Alice cannot create immediately nested tokens: + const nestingTx = testCase.mode === 'nft' + ? (collectionForNesting as UniqueNFTCollection).mintToken(alice, targetToken.nestingAccount()) + : (collectionForNesting as UniqueRFTCollection).mintToken(alice, 10n, targetToken.nestingAccount()); + await expect(nestingTx).to.be.rejectedWith('common.UserIsNotAllowedToNest'); + + // 2. Alice cannot mint and nest token: + const nestedToken2 = await collectionForNesting.mintToken(alice); + await expect(nestedToken2.nest(alice, targetToken)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); + }); + }); + + [ + {mode: 'ft'}, + {mode: 'nativeFt'}, + ].map(testCase => { + itSub(`Owner cannot nest [${testCase.mode}] if nesting is disabled (except for native fungible collection)`, async ({helper}) => { + // Create default collection, permissions are not set: + const aliceNFTCollection = await helper.nft.mintCollection(alice); + const targetToken = await aliceNFTCollection.mintToken(alice); + + const collectionForNesting = testCase.mode === 'ft' ? await helper.ft.mintCollection(alice) : helper.ft.getCollectionObject(0); + + // Alice cannot create immediately nested tokens: + if(testCase.mode === 'ft') { + await expect(collectionForNesting.mint(alice, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); + } else { + await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 100n)).to.be.not.rejected; + } + + // Alice can't mint and nest tokens: + if(testCase.mode === 'ft') { + await collectionForNesting.mint(alice, 100n); + } + + if(testCase.mode === 'ft') { + await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); + } else { + await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n)).to.be.not.rejected; + } + }); + }); + + [ + {mode: 'nft' as const}, + {mode: 'rft' as const}, + {mode: 'ft' as const}, + {mode: 'native ft' as const}, + ].map(testCase => { + itSub(`Non-owner and non-admin cannot nest ${testCase.mode.toUpperCase()} in someone else's tokens (except for native fungible collection)`, async ({helper}) => { + const targetCollection = await helper.nft.mintCollection(alice, {permissions: + {nesting: {tokenOwner: true, collectionAdmin: true}}, + }); + const targetToken = await targetCollection.mintToken(alice); + + const nestedCollectionBob = await ( + testCase.mode === 'native ft' + ? helper.ft.getCollectionObject(0) + : helper[testCase.mode].mintCollection(bob) + ); + + let nestedTokenBob: UniqueNFToken | UniqueRFToken; + switch (testCase.mode) { + case 'nft': nestedTokenBob = await (nestedCollectionBob as UniqueNFTCollection).mintToken(bob); break; + case 'rft': nestedTokenBob = await (nestedCollectionBob as UniqueRFTCollection).mintToken(bob, 100n); break; + case 'ft': await (nestedCollectionBob as UniqueFTCollection).mint(bob, 100n); break; + case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n)).to.be.rejectedWith('common.UnsupportedOperation'); break; + } + + // Bob non-owner of targetToken and non admin of targetCollection, so + // 1. cannot mint nested token: + switch (testCase.mode) { + case 'nft': await expect((nestedCollectionBob as UniqueNFTCollection).mintToken(bob, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break; + case 'rft': await expect((nestedCollectionBob as UniqueRFTCollection).mintToken(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break; + case 'ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break; + case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UnsupportedOperation'); break; + } + + // 2. cannot nest existing token: + switch (testCase.mode) { + case 'nft': + case 'rft': await expect(nestedTokenBob!.transfer(bob, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break; + case 'ft': await expect((nestedCollectionBob as UniqueFTCollection).transfer(bob, targetToken.nestingAccount(), 100n)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break; + case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).transfer(bob, targetToken.nestingAccount(), 100n)).to.be.not.rejected; break; + } + }); + }); + + [ + {mode: 'nft' as const, nesting: {tokenOwner: true, collectionAdmin: false}}, + {mode: 'nft' as const, nesting: {tokenOwner: false, collectionAdmin: true}}, + ].map(testCase => { + itSub(`${testCase.nesting.tokenOwner ? 'Admin' : 'Token owner'} cannot nest when only ${testCase.nesting.tokenOwner ? 'tokenOwner' : 'collectionAdmin'} is allowed`, async ({helper}) => { + // Create collection with tokenOwner or create collection with collectionAdmin permission: + const targetCollection = await helper.nft.mintCollection(alice, {permissions: {nesting: testCase.nesting}}); + const targetTokenCharlie = await targetCollection.mintToken(alice, {Substrate: charlie.address}); + await targetCollection.addAdmin(alice, {Substrate: bob.address}); + + const nestedCollectionCharlie = await helper[testCase.mode].mintCollection(charlie); + const nestedCollectionBob = await helper[testCase.mode].mintCollection(bob); + // if nesting permissions restricted for token owner – minter is bob (admin), + // if collectionAdmin – charlie (owner) + testCase.nesting.tokenOwner + ? await expect(nestedCollectionBob.mintToken(bob, targetTokenCharlie.nestingAccount())).to.be.rejectedWith(/common\.UserIsNotAllowedToNest/) + : await expect(nestedCollectionCharlie.mintToken(charlie, targetTokenCharlie.nestingAccount())).to.be.rejectedWith(/common\.UserIsNotAllowedToNest/); + }); + }); + + itSub.ifWithPallets('Cannot nest in non existing token (except for native fungible collection)', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.nft.mintCollection(alice); + // To avoid UserIsNotAllowedToNest error + await helper.collection.setPermissions(alice, collection.collectionId, {nesting: {collectionAdmin: true}}); + + // The list of non-existing tokens: + const tokenFromNonExistingCollection = helper.nft.getTokenObject(9999999, 1); + const tokenBurnt = await collection.mintToken(alice); + await tokenBurnt.burn(alice); + const tokenNotMintedYet = helper.nft.getTokenObject(collection.collectionId, 2); + + // The list of collections to nest tokens from: + const nftCollectionForNesting = await helper.nft.mintCollection(alice); + const rftCollectionForNesting = await helper.rft.mintCollection(alice); + const ftCollectionForNesting = await helper.ft.mintCollection(alice); + const nativeFtCollectionForNesting = helper.ft.getCollectionObject(0); + + const testCases = [ + {token: tokenFromNonExistingCollection, error: 'CollectionNotFound'}, + {token: tokenBurnt, error: 'TokenNotFound'}, + {token: tokenNotMintedYet, error: 'TokenNotFound'}, + ]; + + for(const testCase of testCases) { + // 1. Alice cannot create nested token to non-existing token + await expect(nftCollectionForNesting.mintToken(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); + await expect(rftCollectionForNesting.mintToken(alice, 10n, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); + await expect(ftCollectionForNesting.mint(alice, 10n, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); + + // 2. Alice cannot mint and nest token: + const nft = await nftCollectionForNesting.mintToken(alice); + const rft = await rftCollectionForNesting.mintToken(alice, 100n); + await ftCollectionForNesting.mint(alice, 100n); + await expect(nft.transfer(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); + await expect(rft.transfer(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); + await expect(ftCollectionForNesting.transfer(alice, testCase.token.nestingAccount(), 50n)).to.be.rejectedWith(testCase.error); + await expect(nativeFtCollectionForNesting.transfer(alice, testCase.token.nestingAccount(), 50n)).to.be.not.rejected; + } + }); + + itEth.ifWithPallets('Cannot nest in collection address', [Pallets.ReFungible], async({helper}) => { + const existingCollection = await helper.nft.mintCollection(alice); + const existingCollectionAddress = helper.ethAddress.fromCollectionId(existingCollection.collectionId); + const futureCollectionAddress = helper.ethAddress.fromCollectionId(99999999); + + const nftCollectionForNesting = await helper.nft.mintCollection(alice); + + // 1. Alice cannot create nested token to collection address + await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); + await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); + + // 2. Alice cannot mint and nest token to collection address: + const nft = await nftCollectionForNesting.mintToken(alice); + await expect(nft.transfer(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); + await expect(nft.transfer(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); + }); + + itEth.ifWithPallets('Cannot nest in RFT or FT (except for native fungible collection)', [Pallets.ReFungible], async ({helper}) => { + // Create default collection, permissions are not set: + const rftCollection = await helper.rft.mintCollection(alice); + const ftCollection = await helper.ft.mintCollection(alice); + const nativeFtCollection = helper.ft.getCollectionObject(0); + + const rftToken = await rftCollection.mintToken(alice); + await ftCollection.mint(alice, 100n); + + const collectionForNesting = await helper.nft.mintCollection(alice); + + // 1. Alice cannot create immediately nested tokens: + await expect(collectionForNesting.mintToken(alice, rftToken.nestingAccount())).to.be.rejectedWith('refungible.RefungibleDisallowsNesting'); + await expect(collectionForNesting.mintToken(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting'); + await expect(collectionForNesting.mintToken(alice, {Ethereum: helper.ethAddress.fromTokenId(nativeFtCollection.collectionId, 0)})).to.be.rejectedWith('common.UnsupportedOperation'); + + // 2. Alice cannot mint and nest token: + const nestedToken2 = await collectionForNesting.mintToken(alice); + await expect(nestedToken2.nest(alice, rftToken)).to.be.rejectedWith('refungible.RefungibleDisallowsNesting'); + await expect(ftCollection.transfer(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting'); + await expect(nativeFtCollection.transfer(alice, {Ethereum: helper.ethAddress.fromTokenId(nativeFtCollection.collectionId, 0)})).to.be.not.rejected; + }); + + itSub('Cannot nest in restricted collection if collection is not in the list (except native fungible collection)', async ({helper}) => { + const {collectionId: allowedCollectionId} = await helper.nft.mintCollection(alice); + const notAllowedCollectionNFT = await helper.nft.mintCollection(alice); + const notAllowedCollectionRFT = await helper.rft.mintCollection(alice); + const notAllowedCollectionFT = await helper.ft.mintCollection(alice); + const allowedCollectionNativeFT = helper.ft.getCollectionObject(0); + + // Collection restricted to allowedCollectionId + const restrictedCollectionA = await helper.nft.mintCollection(alice, {permissions: + {nesting: {tokenOwner: true, restricted: [allowedCollectionId]}}, + }); + // Create collection with restricted nesting -- even self is not allowed + const restrictedCollectionB = await helper.nft.mintCollection(alice, {permissions: + {nesting: {tokenOwner: true, restricted: []}}, + }); + + const targetTokenA = await restrictedCollectionA.mintToken(alice); + const targetTokenB = await restrictedCollectionB.mintToken(alice); + + // 1. Cannot mint in own collection after allowlisting the accounts: + await expect(restrictedCollectionA.mintToken(alice, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); + await expect(restrictedCollectionB.mintToken(alice, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); + + // 2. Cannot mint from notAllowedCollection: + await expect(notAllowedCollectionNFT.mintToken(alice, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); + await expect(notAllowedCollectionNFT.mintToken(alice, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); + await expect(notAllowedCollectionRFT.mintToken(alice, 100n, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); + await expect(notAllowedCollectionRFT.mintToken(alice, 100n, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); + await expect(notAllowedCollectionFT.mint(alice, 100n, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); + await expect(notAllowedCollectionFT.mint(alice, 100n, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/); + await expect(allowedCollectionNativeFT.transfer(alice, targetTokenA.nestingAccount(), 100n)).to.be.not.rejected; + await expect(allowedCollectionNativeFT.transfer(alice, targetTokenB.nestingAccount(), 100n)).to.be.not.rejected; + }); + + itSub('Cannot create nesting chains greater than 5', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + let token = await collection.mintToken(alice); + + const maxNestingLevel = 5; + + // Create a nested-token matryoshka + for(let i = 0; i < maxNestingLevel; i++) { + token = await collection.mintToken(alice, token.nestingAccount()); + } + + // The nesting depth is limited by `maxNestingLevel` + // 1. Cannot mint: + await expect(collection.mintToken(alice, token.nestingAccount())) + .to.be.rejectedWith(/structure\.DepthLimit/); + // 2. Cannot transfer: + const anotherToken = await collection.mintToken(alice); + await expect(anotherToken.transfer(alice, token.nestingAccount())) + .to.be.rejectedWith(/structure\.DepthLimit/); + // 3. Cannot nest FT pieces: + const ftCollection = await helper.ft.mintCollection(alice); + await expect(ftCollection.mint(alice, 100n, token.nestingAccount())) + .to.be.rejectedWith(/structure\.DepthLimit/); + + expect(await token.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); + expect(await token.getChildren()).to.has.length(0); + }); +}); --- /dev/null +++ b/js-packages/tests/sub/nesting/refungible.test.ts @@ -0,0 +1,61 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js'; + +describe('ReFungible-specific nesting tests', () => { + let alice: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice] = await helper.arrange.createAccounts([200n], donor); + }); + }); + + itSub.ifWithPallets('ReFungible: getTopmostOwner works correctly with Nesting', [Pallets.ReFungible], async({helper}) => { + const collectionNFT = await helper.nft.mintCollection(alice, { + permissions: { + nesting: { + tokenOwner: true, + }, + }, + }); + const collectionRFT = await helper.rft.mintCollection(alice); + + const nft = await collectionNFT.mintToken(alice, {Substrate: alice.address}); + const rft = await collectionRFT.mintToken(alice, 100n, {Substrate: alice.address}); + + expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address}); + + await rft.transfer(alice, nft.nestingAccount(), 40n); + + expect(await rft.getTopmostOwner()).deep.equal(null); + + await rft.transfer(alice, nft.nestingAccount(), 60n); + + expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address}); + + await rft.transferFrom(alice, nft.nestingAccount(), {Substrate: alice.address}, 30n); + + expect(await rft.getTopmostOwner()).deep.equal(null); + + await rft.transferFrom(alice, nft.nestingAccount(), {Substrate: alice.address}, 70n); + + expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address}); + }); +}); --- /dev/null +++ b/js-packages/tests/sub/nesting/unnesting.negative.test.ts @@ -0,0 +1,88 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, usingPlaygrounds} from '../../util/index.js'; +import {CrossAccountId} from '@unique/playgrounds/unique.js'; + +describe('Negative Test: Unnesting', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 50n], donor); + }); + }); + + // TODO: make this test a bit more generic + itSub('Admin (NFT): disallows an Admin to unnest someone else\'s token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {limits: {ownerCanTransfer: true}, permissions: {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true}}}); + //await collection.addAdmin(alice, {Substrate: bob.address}); + const targetToken = await collection.mintToken(alice, {Substrate: bob.address}); + await collection.addToAllowList(alice, {Substrate: bob.address}); + await collection.addToAllowList(alice, targetToken.nestingAccount()); + + // Try to nest somebody else's token + const newToken = await collection.mintToken(bob); + await expect(newToken.nest(alice, targetToken)) + .to.be.rejectedWith(/common\.NoPermission/); + + // Try to unnest a token belonging to someone else as collection admin + const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount()); + await expect(nestedToken.unnest(alice, targetToken, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); + + expect(await targetToken.getChildren()).to.be.length(1); + expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address}); + expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount())); + }); + + [ + {mode: 'ft' as const}, + {mode: 'native ft' as const}, + ].map(md => [ + {mode: md.mode, restrictedMode: true}, + {mode: md.mode, restrictedMode: false}, + ].map(testCase => { + itSub(`Fungible: disallows a non-Owner to unnest someone else's token [${testCase.mode}${testCase.restrictedMode ? ' (Restricted nesting)' : ''}]`, async ({helper}) => { + const collectionNFT = await helper.nft.mintCollection(alice); + const collectionFT = await ( + testCase.mode === 'ft' + ? helper.ft.mintCollection(alice) + : helper.ft.getCollectionObject(0) + ); + const targetToken = await collectionNFT.mintToken(alice, {Substrate: bob.address}); + + await collectionNFT.setPermissions(alice, {nesting: { + collectionAdmin: true, tokenOwner: true, restricted: testCase.restrictedMode ? [collectionFT.collectionId] : null, + }}); + + // Nest some tokens as Alice into Bob's token + await ( + testCase.mode === 'ft' + ? collectionFT.mint(alice, 5n, targetToken.nestingAccount()) + : collectionFT.transfer(alice, targetToken.nestingAccount(), 5n) + ); + + // Try to pull it out as Alice still + await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: bob.address}, 1n)) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(5n); + }); + })); +}); --- /dev/null +++ b/js-packages/tests/sub/refungible/burn.test.ts @@ -0,0 +1,127 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js'; + +describe('Refungible: burn', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); + }); + }); + + itSub('can burn some pieces', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + expect(await collection.doesTokenExist(token.tokenId)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); + await token.burn(alice, 99n); + expect(await collection.doesTokenExist(token.tokenId)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(1n); + }); + + itSub('can burn all pieces', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + + expect(await collection.doesTokenExist(token.tokenId)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); + + await token.burn(alice, 100n); + expect(await collection.doesTokenExist(token.tokenId)).to.be.false; + }); + + itSub('burn pieces for multiple users', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + + await token.transfer(alice, {Substrate: bob.address}, 60n); + + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n); + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n); + + await token.burn(alice, 40n); + + expect(await collection.doesTokenExist(token.tokenId)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n); + + await token.burn(bob, 59n); + + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(1n); + expect(await collection.doesTokenExist(token.tokenId)).to.be.true; + + await token.burn(bob, 1n); + + expect(await collection.doesTokenExist(token.tokenId)).to.be.false; + }); + + itSub('burn pieces by admin', async function({helper}) { + const collection = await helper.rft.mintCollection(alice); + await collection.setLimits(alice, {ownerCanTransfer: true}); + await collection.addAdmin(alice, {Substrate: bob.address}); + const token = await collection.mintToken(alice, 100n); + + await token.burnFrom(bob, {Substrate: alice.address}, 100n); + expect(await token.doesExist()).to.be.false; + }); +}); + +describe('Refungible: burn negative tests', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor); + }); + }); + + itSub('cannot burn non-owned token pieces', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice); + const aliceToken = await collection.mintToken(alice, 10n, {Substrate: alice.address}); + const bobToken = await collection.mintToken(alice, 10n, {Substrate: bob.address}); + + // 1. Cannot burn non-owned token: + await expect(bobToken.burn(alice, 0n)).to.be.rejectedWith('common.TokenValueTooLow'); + await expect(bobToken.burn(alice, 5n)).to.be.rejectedWith('common.TokenValueTooLow'); + // 2. Cannot burn non-existing token: + await expect(helper.rft.burnToken(alice, 99999, 10)).to.be.rejectedWith('common.CollectionNotFound'); + await expect(helper.rft.burnToken(alice, collection.collectionId, 99999)).to.be.rejectedWith('common.TokenValueTooLow'); + // 3. Can burn zero amount of owned tokens (EIP-20) + await aliceToken.burn(alice, 0n); + + // 4. Storage is not corrupted: + expect(await aliceToken.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]); + expect(await bobToken.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]); + + // 4.1 Tokens can be transfered: + await aliceToken.transfer(alice, {Substrate: bob.address}, 10n); + await bobToken.transfer(bob, {Substrate: alice.address}, 10n); + expect(await aliceToken.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]); + expect(await bobToken.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]); + }); +}); --- /dev/null +++ b/js-packages/tests/sub/refungible/nesting.test.ts @@ -0,0 +1,148 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../../util/index.js'; + +describe('Refungible nesting', () => { + let alice: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + const donor = await privateKey({url: import.meta.url}); + [alice, charlie] = await helper.arrange.createAccounts([50n, 10n], donor); + }); + }); + + [ + {restrictedMode: true}, + {restrictedMode: false}, + ].map(testCase => { + itSub(`Owner can nest their token${testCase.restrictedMode ? ': Restricted mode' : ''}`, async ({helper}) => { + const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}}}); + const collectionRFT = await helper.rft.mintCollection(alice); + const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address}); + + await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionRFT.collectionId] : null}}); + await collectionNFT.addToAllowList(alice, {Substrate: charlie.address}); + await collectionNFT.addToAllowList(alice, targetToken.nestingAccount()); + + await collectionRFT.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await collectionRFT.addToAllowList(alice, {Substrate: charlie.address}); + await collectionRFT.addToAllowList(alice, targetToken.nestingAccount()); + + // Create an immediately nested token + const nestedToken = await collectionRFT.mintToken(charlie, 5n, targetToken.nestingAccount()); + expect(await nestedToken.getBalance(targetToken.nestingAccount())).to.be.equal(5n); + + // Create a token to be nested and nest + const newToken = await collectionRFT.mintToken(charlie, 5n); + await newToken.transfer(charlie, targetToken.nestingAccount(), 2n); + expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(2n); + expect(await newToken.getBalance({Substrate: charlie.address})).to.be.equal(3n); + }); + }); + + itSub('Owner can unnest nested token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const targetToken = await collection.mintToken(alice); + + // Owner mints nested RFT token: + const collectionRFT = await helper.rft.mintCollection(alice); + const token = await collectionRFT.mintToken(alice, 10n, targetToken.nestingAccount()); + + // 1.1 Owner can partially unnest token pieces with transferFrom: + await token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n); + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(9n); + expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(1n); + + // 1.2 Owner can unnest all pieces: + await token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 1n); + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(10n); + expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n); + }); + + itSub('Owner can burn nested token pieces', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const targetToken = await collection.mintToken(alice); + + // Owner mints nested RFT token: + const collectionRFT = await helper.rft.mintCollection(alice); + const token = await collectionRFT.mintToken(alice, 100n, {Substrate: alice.address}); + await token.transfer(alice, targetToken.nestingAccount(), 30n); + + // 1.1 Owner can partially burnFrom nested pieces: + await token.burnFrom(alice, targetToken.nestingAccount(), 10n); + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(70n); + expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(20n); + expect(await targetToken.getChildren()).to.has.length(1); + + // 1.1 Owner can burnFrom all nested pieces: + await token.burnFrom(alice, targetToken.nestingAccount(), 20n); + expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n); + expect(await targetToken.getChildren()).to.has.length(0); + expect(await token.doesExist()).to.be.true; + + // 2. Target token does not contain any pieces and can be burnt: + await targetToken.burn(alice); + expect(await targetToken.doesExist()).to.be.false; + }); +}); + +describe('Refungible nesting negative tests', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 50n], donor); + }); + }); + + [ + {restrictedMode: true}, + {restrictedMode: false}, + ].map(testCase => { + itSub(`non-Owner cannot nest someone else's token${testCase.restrictedMode ? ': Restricted mode' : ''}`, async ({helper}) => { + const collectionNFT = await helper.nft.mintCollection(alice); + const collectionRFT = await helper.rft.mintCollection(alice); + const targetToken = await collectionNFT.mintToken(alice); + + await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionRFT.collectionId] : null}}); + await collectionNFT.addToAllowList(alice, {Substrate: bob.address}); + await collectionNFT.addToAllowList(alice, targetToken.nestingAccount()); + + // Try to create a token to be nested and nest + const newToken = await collectionRFT.mintToken(alice); + await expect(newToken.transfer(bob, targetToken.nestingAccount())).to.be.rejectedWith(/common\.TokenValueTooLow/); + + expect(await targetToken.getChildren()).to.be.length(0); + expect(await newToken.getBalance({Substrate: alice.address})).to.be.equal(1n); + + // Nest some tokens as Alice into Bob's token + await newToken.transfer(alice, targetToken.nestingAccount()); + + // Try to pull it out + await expect(newToken.transferFrom(bob, targetToken.nestingAccount(), {Substrate: alice.address}, 1n)) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(1n); + }); + }); +}); --- /dev/null +++ b/js-packages/tests/sub/refungible/repartition.test.ts @@ -0,0 +1,96 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js'; + +describe('integration test: Refungible functionality:', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); + }); + }); + + itSub('Repartition', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + + expect(await token.repartition(alice, 200n)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(200n); + expect(await token.getTotalPieces()).to.be.equal(200n); + + expect(await token.transfer(alice, {Substrate: bob.address}, 110n)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(90n); + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(110n); + + await expect(token.repartition(alice, 80n)) + .to.eventually.be.rejectedWith(/refungible\.RepartitionWhileNotOwningAllPieces/); + + expect(await token.transfer(alice, {Substrate: bob.address}, 90n)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n); + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(200n); + + expect(await token.repartition(bob, 150n)).to.be.true; + await expect(token.transfer(bob, {Substrate: alice.address}, 160n)) + .to.eventually.be.rejectedWith(/common\.TokenValueTooLow/); + }); + + itSub('Repartition with increased amount', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + await token.repartition(alice, 200n); + const chainEvents = helper.chainLog.slice(-1)[0].events; + const event = chainEvents.find((event: any) => event.section === 'common' && event.method === 'ItemCreated'); + expect(event).to.deep.include({ + section: 'common', + method: 'ItemCreated', + index: [66, 2], + data: [ + collection.collectionId, + token.tokenId, + {substrate: alice.address}, + 100n, + ], + }); + }); + + itSub('Repartition with decreased amount', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + await token.repartition(alice, 50n); + const chainEvents = helper.chainLog.slice(-1)[0].events; + const event = chainEvents.find((event: any) => event.section === 'common' && event.method === 'ItemDestroyed'); + expect(event).to.deep.include({ + section: 'common', + method: 'ItemDestroyed', + index: [66, 3], + data: [ + collection.collectionId, + token.tokenId, + {substrate: alice.address}, + 50n, + ], + }); + }); +}); + --- /dev/null +++ b/js-packages/tests/sub/refungible/transfer.test.ts @@ -0,0 +1,73 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js'; + +describe('Refungible transfer tests', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + + donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); + }); + }); + + itSub('Can transfer token pieces', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + + expect(await token.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true; + // 1. Can transfer less or equal than have: + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n); + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n); + }); + + itSub('Cannot transfer incorrect amount of token pieces', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const tokenAlice = await collection.mintToken(alice, 10n, {Substrate: alice.address}); + const tokenBob = await collection.mintToken(alice, 10n, {Substrate: bob.address}); + + // 1. Alice cannot transfer Bob's token: + await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.TokenValueTooLow'); + await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow'); + await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 10n)).to.be.rejectedWith('common.TokenValueTooLow'); + await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 100n)).to.be.rejectedWith('common.TokenValueTooLow'); + + // 2. Alice cannot transfer non-existing token: + await expect(collection.transferToken(alice, 100, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.TokenValueTooLow'); + await expect(collection.transferToken(alice, 100, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow'); + + // 3. Cannot transfer more than have: + await expect(tokenAlice.transfer(alice, {Substrate: bob.address}, 11n)) + .to.eventually.be.rejectedWith(/common\.TokenValueTooLow/); + + // 4. Zero transfer allowed (EIP-20): + await tokenAlice.transfer(alice, {Substrate: charlie.address}, 0n); + + expect(await tokenAlice.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]); + expect(await tokenBob.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]); + expect(await tokenAlice.getBalance({Substrate: alice.address})).to.eq(10n); + expect(await tokenBob.getBalance({Substrate: bob.address})).to.eq(10n); + expect(await tokenBob.getBalance({Substrate: charlie.address})).to.eq(0n); + }); +}); --- /dev/null +++ b/js-packages/tests/transfer.test.ts @@ -0,0 +1,345 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itEth, usingEthPlaygrounds} from './eth/util/index.js'; +import {itSub, Pallets, usingPlaygrounds, expect} from './util/index.js'; +import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js'; + +describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); + }); + }); + + itSub('Balance transfers and check balance', async ({helper}) => { + const alicesBalanceBefore = await helper.balance.getSubstrate(alice.address); + const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address); + + expect(await helper.balance.transferToSubstrate(alice, bob.address, 1n)).to.be.true; + + const alicesBalanceAfter = await helper.balance.getSubstrate(alice.address); + const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address); + + expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true; + expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true; + }); + + itSub('Inability to pay fees error message is correct', async ({helper}) => { + const [zero] = await helper.arrange.createAccounts([0n], donor); + + // console.error = () => {}; + // The following operation throws an error into the console and the logs. Pay it no heed as long as the test succeeds. + await expect(helper.balance.transferToSubstrate(zero, donor.address, 1n)) + .to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low'); + }); + + itSub('[nft] User can transfer owned token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-1-NFT', description: '', tokenPrefix: 'T'}); + const nft = await collection.mintToken(alice); + + await nft.transfer(alice, {Substrate: bob.address}); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: bob.address}); + }); + + itSub('[fungible] User can transfer owned token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-1-FT', description: '', tokenPrefix: 'T'}); + await collection.mint(alice, 10n); + + await collection.transfer(alice, {Substrate: bob.address}, 9n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(9n); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n); + }); + + itSub.ifWithPallets('[refungible] User can transfer owned token', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-1-RFT', description: '', tokenPrefix: 'T'}); + const rft = await collection.mintToken(alice, 10n); + + await rft.transfer(alice, {Substrate: bob.address}, 9n); + expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(9n); + expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(1n); + }); + + itSub('[nft] Collection admin can transfer owned token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-2-NFT', description: '', tokenPrefix: 'T'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + + const nft = await collection.mintToken(bob, {Substrate: bob.address}); + await nft.transfer(bob, {Substrate: alice.address}); + + expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); + }); + + itSub('[fungible] Collection admin can transfer owned token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-2-FT', description: '', tokenPrefix: 'T'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + + await collection.mint(bob, 10n, {Substrate: bob.address}); + await collection.transfer(bob, {Substrate: alice.address}, 1n); + + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(9n); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n); + }); + + itSub.ifWithPallets('[refungible] Collection admin can transfer owned token', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-2-RFT', description: '', tokenPrefix: 'T'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + + const rft = await collection.mintToken(bob, 10n, {Substrate: bob.address}); + await rft.transfer(bob, {Substrate: alice.address}, 1n); + + expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(9n); + expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(1n); + }); +}); + +describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); + }); + }); + + + itSub('[nft] Transfer with not existed collection_id', async ({helper}) => { + await expect(helper.nft.transferToken(alice, NON_EXISTENT_COLLECTION_ID, 1, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('[fungible] Transfer with not existed collection_id', async ({helper}) => { + await expect(helper.ft.transfer(alice, NON_EXISTENT_COLLECTION_ID, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub.ifWithPallets('[refungible] Transfer with not existed collection_id', [Pallets.ReFungible], async ({helper}) => { + await expect(helper.rft.transferToken(alice, NON_EXISTENT_COLLECTION_ID, 1, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('[nft] Transfer with deleted collection_id', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-1-NFT', description: '', tokenPrefix: 'T'}); + const nft = await collection.mintToken(alice); + + await nft.burn(alice); + await collection.burn(alice); + + await expect(nft.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('[fungible] Transfer with deleted collection_id', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-1-FT', description: '', tokenPrefix: 'T'}); + await collection.mint(alice, 10n); + + await collection.burnTokens(alice, 10n); + await collection.burn(alice); + + await expect(collection.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub.ifWithPallets('[refungible] Transfer with deleted collection_id', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-1-RFT', description: '', tokenPrefix: 'T'}); + const rft = await collection.mintToken(alice, 10n); + + await rft.burn(alice, 10n); + await collection.burn(alice); + + await expect(rft.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub('[nft] Transfer with not existed item_id', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-2-NFT', description: '', tokenPrefix: 'T'}); + await expect(collection.transferToken(alice, 1, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenNotFound/); + }); + + itSub('[fungible] Transfer with not existed item_id', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-2-FT', description: '', tokenPrefix: 'T'}); + await expect(collection.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + }); + + itSub.ifWithPallets('[refungible] Transfer with not existed item_id', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-2-RFT', description: '', tokenPrefix: 'T'}); + await expect(collection.transferToken(alice, 1, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + }); + + itSub('Zero transfer NFT', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-3-NFT', description: '', tokenPrefix: 'T'}); + const tokenAlice = await collection.mintToken(alice, {Substrate: alice.address}); + const tokenBob = await collection.mintToken(alice, {Substrate: bob.address}); + // 1. Zero transfer of own tokens allowed: + await helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: bob.address}, collection.collectionId, tokenAlice.tokenId, 0]); + // 2. Zero transfer of non-owned tokens not allowed: + await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: alice.address}, collection.collectionId, tokenBob.tokenId, 0])).to.be.rejectedWith('common.NoPermission'); + // 3. Zero transfer of non-existing tokens not allowed: + await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: alice.address}, collection.collectionId, 10, 0])).to.be.rejectedWith('common.TokenNotFound'); + expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: alice.address}); + expect(await tokenBob.getOwner()).to.deep.eq({Substrate: bob.address}); + // 4. Storage is not corrupted: + await tokenAlice.transfer(alice, {Substrate: bob.address}); + await tokenBob.transfer(bob, {Substrate: alice.address}); + expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: bob.address}); + expect(await tokenBob.getOwner()).to.deep.eq({Substrate: alice.address}); + }); + + itSub('[nft] Transfer with deleted item_id', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-3-NFT', description: '', tokenPrefix: 'T'}); + const nft = await collection.mintToken(alice); + + await nft.burn(alice); + + await expect(nft.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenNotFound/); + }); + + itSub('[fungible] Transfer with deleted item_id', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-3-FT', description: '', tokenPrefix: 'T'}); + await collection.mint(alice, 10n); + + await collection.burnTokens(alice, 10n); + + await expect(collection.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + }); + + itSub.ifWithPallets('[refungible] Transfer with deleted item_id', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-3-RFT', description: '', tokenPrefix: 'T'}); + const rft = await collection.mintToken(alice, 10n); + + await rft.burn(alice, 10n); + + await expect(rft.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + }); + + itSub('[nft] Transfer with recipient that is not owner', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-4-NFT', description: '', tokenPrefix: 'T'}); + const nft = await collection.mintToken(alice); + + await expect(nft.transfer(bob, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.NoPermission/); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); + }); + + itSub('[fungible] Transfer with recipient that is not owner', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-4-FT', description: '', tokenPrefix: 'T'}); + await collection.mint(alice, 10n); + + await expect(collection.transfer(bob, {Substrate: bob.address}, 9n)) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(0n); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(10n); + }); + + itSub.ifWithPallets('[refungible] Transfer with recipient that is not owner', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-1-RFT', description: '', tokenPrefix: 'T'}); + const rft = await collection.mintToken(alice, 10n); + + await expect(rft.transfer(bob, {Substrate: bob.address}, 9n)) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(0n); + expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(10n); + }); +}); + +describe('Transfers to self (potentially over substrate-evm boundary)', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = await privateKey({url: import.meta.url}); + }); + }); + + itEth('Transfers to self. In case of same frontend', async ({helper}) => { + const [owner] = await helper.arrange.createAccounts([10n], donor); + const collection = await helper.ft.mintCollection(owner, {}); + await collection.mint(owner, 100n); + + const ownerProxy = helper.address.substrateToEth(owner.address); + + // transfer to own proxy + await collection.transfer(owner, {Ethereum: ownerProxy}, 10n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n); + expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n); + + // transfer-from own proxy to own proxy again + await collection.transferFrom(owner, {Ethereum: ownerProxy}, {Ethereum: ownerProxy}, 5n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n); + expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n); + }); + + itEth('Transfers to self. In case of substrate-evm boundary', async ({helper}) => { + const [owner] = await helper.arrange.createAccounts([10n], donor); + const collection = await helper.ft.mintCollection(owner, {}); + await collection.mint(owner, 100n); + + const ownerProxy = helper.address.substrateToEth(owner.address); + + // transfer to own proxy + await collection.transfer(owner, {Ethereum: ownerProxy}, 10n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n); + expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n); + + // transfer-from own proxy to self + await collection.transferFrom(owner, {Ethereum: ownerProxy}, {Substrate: owner.address}, 5n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(95n); + expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(5n); + }); + + itEth('Transfers to self. In case of inside substrate-evm', async ({helper}) => { + const [owner] = await helper.arrange.createAccounts([10n], donor); + const collection = await helper.ft.mintCollection(owner, {}); + await collection.mint(owner, 100n); + + // transfer to self again + await collection.transfer(owner, {Substrate: owner.address}, 10n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(100n); + + // transfer-from self to self again + await collection.transferFrom(owner, {Substrate: owner.address}, {Substrate: owner.address}, 5n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(100n); + }); + + itEth('Transfers to self. In case of inside substrate-evm when not enought "Fungibles"', async ({helper}) => { + const [owner] = await helper.arrange.createAccounts([10n], donor); + const collection = await helper.ft.mintCollection(owner, {}); + await collection.mint(owner, 10n); + + // transfer to self again + await expect(collection.transfer(owner, {Substrate: owner.address}, 11n)) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + + // transfer-from self to self again + await expect(collection.transferFrom(owner, {Substrate: owner.address}, {Substrate: owner.address}, 12n)) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(10n); + }); +}); --- /dev/null +++ b/js-packages/tests/transferFrom.test.ts @@ -0,0 +1,375 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, Pallets, usingPlaygrounds, expect} from './util/index.js'; +import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js'; + +describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); + }); + }); + + itSub('[nft] Execute the extrinsic and check nftItemList - owner of token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-1', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice); + await nft.approve(alice, {Substrate: bob.address}); + expect(await nft.isApproved({Substrate: bob.address})).to.be.true; + + await nft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: charlie.address}); + }); + + itSub('[fungible] Execute the extrinsic and check nftItemList - owner of token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-2', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 10n); + await collection.approveTokens(alice, {Substrate: bob.address}, 7n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(7n); + + await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 6n); + expect(await collection.getBalance({Substrate: charlie.address})).to.be.equal(6n); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(4n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(1n); + }); + + itSub.ifWithPallets('[refungible] Execute the extrinsic and check nftItemList - owner of token', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-3', description: '', tokenPrefix: 'TF'}); + const rft = await collection.mintToken(alice, 10n); + await rft.approve(alice, {Substrate: bob.address}, 7n); + expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(7n); + + await rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 6n); + expect(await rft.getBalance({Substrate: charlie.address})).to.be.equal(6n); + expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(4n); + expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(1n); + }); + + itSub('Should reduce allowance if value is big', async ({helper}) => { + // fungible + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-4', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 500000n); + + await collection.approveTokens(alice, {Substrate: bob.address}, 500000n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(500000n); + await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 500000n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(0n); + }); + + itSub('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-5', description: '', tokenPrefix: 'TF'}); + await collection.setLimits(alice, {ownerCanTransfer: true}); + + const nft = await collection.mintToken(alice, {Substrate: bob.address}); + await nft.transferFrom(alice, {Substrate: bob.address}, {Substrate: charlie.address}); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: charlie.address}); + }); +}); + +describe('Negative Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({url: import.meta.url}); + [alice, bob, charlie] = await helper.arrange.createAccounts([50n, 10n, 10n], donor); + }); + }); + + itSub('transferFrom for a collection that does not exist', async ({helper}) => { + await expect(helper.collection.approveToken(alice, NON_EXISTENT_COLLECTION_ID, 0, {Substrate: bob.address}, 1n)) + .to.be.rejectedWith(/common\.CollectionNotFound/); + await expect(helper.collection.transferTokenFrom(bob, NON_EXISTENT_COLLECTION_ID, 0, {Substrate: alice.address}, {Substrate: bob.address}, 1n)) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + /* itSub('transferFrom for a collection that was destroyed', async ({helper}) => { + this test copies approve negative test + }); */ + + /* itSub('transferFrom a token that does not exist', async ({helper}) => { + this test copies approve negative test + }); */ + + /* itSub('transferFrom a token that was deleted', async ({helper}) => { + this test copies approve negative test + }); */ + + itSub('[nft] transferFrom for not approved address', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-1', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice); + + await expect(nft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); + }); + + itSub('[fungible] transferFrom for not approved address', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-1', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 10n); + + await expect(collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 5n)) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); + }); + + itSub.ifWithPallets('[refungible] transferFrom for not approved address', [Pallets.ReFungible], async({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-3', description: '', tokenPrefix: 'TF'}); + const rft = await collection.mintToken(alice, 10n); + + await expect(rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); + expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); + }); + + itSub('[nft] transferFrom incorrect token count', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-4', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice); + + await nft.approve(alice, {Substrate: bob.address}); + expect(await nft.isApproved({Substrate: bob.address})).to.be.true; + + await expect(helper.collection.transferTokenFrom( + bob, + collection.collectionId, + nft.tokenId, + {Substrate: alice.address}, + {Substrate: charlie.address}, + 2n, + )).to.be.rejectedWith(/nonfungible\.NonfungibleItemsHaveNoAmount/); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); + }); + + itSub('[fungible] transferFrom incorrect token count', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-5', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 10n); + + await collection.approveTokens(alice, {Substrate: bob.address}, 2n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(2n); + + await expect(collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 5n)) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); + }); + + itSub.ifWithPallets('[refungible] transferFrom incorrect token count', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-6', description: '', tokenPrefix: 'TF'}); + const rft = await collection.mintToken(alice, 10n); + + await rft.approve(alice, {Substrate: bob.address}, 5n); + expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(5n); + + await expect(rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 7n)) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); + expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); + }); + + itSub('[nft] execute transferFrom from account that is not owner of collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-7', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice); + + await expect(nft.approve(charlie, {Substrate: bob.address})).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); + expect(await nft.isApproved({Substrate: bob.address})).to.be.false; + + await expect(nft.transferFrom( + charlie, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); + }); + + itSub('[fungible] execute transferFrom from account that is not owner of collection', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-8', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 10000n); + + await expect(collection.approveTokens(charlie, {Substrate: bob.address}, 1n)).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(0n); + expect(await collection.getApprovedTokens({Substrate: charlie.address}, {Substrate: bob.address})).to.be.eq(0n); + + await expect(collection.transferFrom( + charlie, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10000n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); + }); + + itSub.ifWithPallets('[refungible] execute transferFrom from account that is not owner of collection', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-9', description: '', tokenPrefix: 'TF'}); + const rft = await collection.mintToken(alice, 10000n); + + await expect(rft.approve(charlie, {Substrate: bob.address}, 1n)).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); + expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(0n); + expect(await rft.getApprovedPieces({Substrate: charlie.address}, {Substrate: bob.address})).to.be.eq(0n); + + await expect(rft.transferFrom( + charlie, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10000n); + expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); + }); + + itSub('transferFrom burnt token before approve NFT', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-10', description: '', tokenPrefix: 'TF'}); + await collection.setLimits(alice, {ownerCanTransfer: true}); + const nft = await collection.mintToken(alice); + + await nft.burn(alice); + await expect(nft.approve(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.TokenNotFound/); + + await expect(nft.transferFrom( + bob, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + }); + + itSub('transferFrom burnt token before approve Fungible', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-11', description: '', tokenPrefix: 'TF'}); + await collection.setLimits(alice, {ownerCanTransfer: true}); + await collection.mint(alice, 10n); + + await collection.burnTokens(alice, 10n); + await expect(collection.approveTokens(alice, {Substrate: bob.address})).to.be.not.rejected; + + await expect(collection.transferFrom( + alice, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.TokenValueTooLow/); + }); + + itSub.ifWithPallets('transferFrom burnt token before approve ReFungible', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-12', description: '', tokenPrefix: 'TF'}); + await collection.setLimits(alice, {ownerCanTransfer: true}); + const rft = await collection.mintToken(alice, 10n); + + await rft.burn(alice, 10n); + await expect(rft.approve(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); + + await expect(rft.transferFrom( + alice, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.TokenValueTooLow/); + }); + + itSub('transferFrom burnt token after approve NFT', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-13', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice); + + await nft.approve(alice, {Substrate: bob.address}); + expect(await nft.isApproved({Substrate: bob.address})).to.be.true; + + await nft.burn(alice); + + await expect(nft.transferFrom( + bob, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + }); + + itSub('transferFrom burnt token after approve Fungible', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-14', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 10n); + + await collection.approveTokens(alice, {Substrate: bob.address}); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(1n); + + await collection.burnTokens(alice, 10n); + + await expect(collection.transferFrom( + bob, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.TokenValueTooLow/); + }); + + itSub.ifWithPallets('transferFrom burnt token after approve ReFungible', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-15', description: '', tokenPrefix: 'TF'}); + const rft = await collection.mintToken(alice, 10n); + + await rft.approve(alice, {Substrate: bob.address}, 10n); + expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(10n); + + await rft.burn(alice, 10n); + + await expect(rft.transferFrom( + bob, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + }); + + itSub('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-16', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice, {Substrate: bob.address}); + + await collection.setLimits(alice, {ownerCanTransfer: false}); + + await expect(nft.transferFrom( + alice, + {Substrate: bob.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + }); + + itSub('zero transfer NFT', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Zero', description: 'Zero transfer', tokenPrefix: 'TF'}); + const notApprovedNft = await collection.mintToken(alice, {Substrate: bob.address}); + const approvedNft = await collection.mintToken(alice, {Substrate: bob.address}); + await approvedNft.approve(bob, {Substrate: alice.address}); + + // 1. Cannot zero transferFrom (non-existing token) + await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, 9999, 0])).to.be.rejectedWith('common.ApprovedValueTooLow'); + // 2. Cannot zero transferFrom (not approved token) + await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, notApprovedNft.tokenId, 0])).to.be.rejectedWith('common.ApprovedValueTooLow'); + // 3. Can zero transferFrom (approved token): + await helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, approvedNft.tokenId, 0]); + + // 4.1 approvedNft still approved: + expect(await approvedNft.isApproved({Substrate: alice.address})).to.be.true; + // 4.2 bob is still the owner: + expect(await approvedNft.getOwner()).to.deep.eq({Substrate: bob.address}); + expect(await notApprovedNft.getOwner()).to.deep.eq({Substrate: bob.address}); + // 4.3 Alice can transfer approved nft: + await approvedNft.transferFrom(alice, {Substrate: bob.address}, {Substrate: alice.address}); + expect(await approvedNft.getOwner()).to.deep.eq({Substrate: alice.address}); + }); +}); --- a/js-packages/tests/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig.packages.json", - "compilerOptions": { - "rootDir": "./src", - "outDir": "dist", - "allowSyntheticDefaultImports": true, - "resolveJsonModule": true, - }, - "include": ["./src/**/*.json", "./src/**/*.ts"], - "references": [ - { "path": "../playgrounds/tsconfig.json" } - ] -} \ No newline at end of file --- /dev/null +++ b/js-packages/tests/tx-version-presence.test.ts @@ -0,0 +1,32 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {Metadata} from '@polkadot/types'; +import {itSub, usingPlaygrounds, expect} from './util/index.js'; + +let metadata: Metadata; + +describe('TxVersion is present', () => { + before(async () => { + await usingPlaygrounds(async helper => { + metadata = await helper.callRpc('api.rpc.state.getMetadata', []); + }); + }); + + itSub('Signed extension CheckTxVersion is present', () => { + expect(metadata.asLatest.extrinsic.signedExtensions.map(se => se.identifier.toString())).to.include('CheckTxVersion'); + }); +}); --- /dev/null +++ b/js-packages/tests/util/authorizeEnactUpgrade.ts @@ -0,0 +1,19 @@ +import {readFile} from 'fs/promises'; +import {u8aToHex} from '@polkadot/util'; +import {usingPlaygrounds} from './index.js'; +import {blake2AsHex} from '@polkadot/util-crypto'; + + +const codePath = process.argv[2]; +if(!codePath) throw new Error('missing code path argument'); + +const code = await readFile(codePath); + +await usingPlaygrounds(async (helper, privateKey) => { + const alice = await privateKey('//Alice'); + const hex = blake2AsHex(code); + await helper.getSudo().executeExtrinsic(alice, 'api.tx.parachainSystem.authorizeUpgrade', [hex, true]); + await helper.getSudo().executeExtrinsicUncheckedWeight(alice, 'api.tx.parachainSystem.enactAuthorizedUpgrade', [u8aToHex(code)]); +}); +// We miss disconnect/unref somewhere. +process.exit(0); --- /dev/null +++ b/js-packages/tests/util/createHrmp.ts @@ -0,0 +1,36 @@ +import {usingPlaygrounds} from './index.js'; +import config from '../config.js'; + +const profile = process.argv[2]; +if(!profile) throw new Error('missing profile/relay argument'); + +await usingPlaygrounds(async (helper, privateKey) => { + const bidirOpen = async (a: number, b: number) => { + console.log(`Opening ${a} <=> ${b}`); + await helper.getSudo().executeExtrinsic(alice, 'api.tx.hrmp.forceOpenHrmpChannel', [a, b, 8, 512]); + await helper.getSudo().executeExtrinsic(alice, 'api.tx.hrmp.forceOpenHrmpChannel', [b, a, 8, 512]); + }; + const alice = await privateKey('//Alice'); + switch (profile) { + case 'opal': + await bidirOpen(1001, 1002); + break; + case 'quartz': + await bidirOpen(1001, 1002); + await bidirOpen(1001, 1003); + await bidirOpen(1001, 1004); + await bidirOpen(1001, 1005); + break; + case 'unique': + await bidirOpen(1001, 1002); + await bidirOpen(1001, 1003); + await bidirOpen(1001, 1004); + await bidirOpen(1001, 1005); + await bidirOpen(1001, 1006); + break; + default: + throw new Error(`unknown hrmp config profile: ${profile}`); + } +}, config.relayUrl); +// We miss disconnect/unref somewhere. +process.exit(0); --- /dev/null +++ b/js-packages/tests/util/frankensteinMigrate.ts @@ -0,0 +1,9 @@ +import {migration as locksToFreezesMigration} from '../migrations/942057-appPromotion/index.js'; +export interface Migration { + before: () => Promise, + after: () => Promise, +} + +export const migrations: {[key: string]: Migration} = { + 'v942057': locksToFreezesMigration, +}; --- /dev/null +++ b/js-packages/tests/util/globalSetup.ts @@ -0,0 +1,119 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +import { + usingPlaygrounds, Pallets, DONOR_FUNDING, MINIMUM_DONOR_FUND, LOCKING_PERIOD, UNLOCKING_PERIOD, makeNames, +} from './index.js'; +import * as path from 'path'; +import {promises as fs} from 'fs'; + +const {dirname} = makeNames(import.meta.url); + +// This function should be called before running test suites. +const globalSetup = async (): Promise => { + await usingPlaygrounds(async (helper, privateKey) => { + try { + // 1. Wait node producing blocks + await helper.wait.newBlocks(1, 600_000); + + // 2. Create donors for test files + await fundFilenamesWithRetries(3) + .then((result) => { + if(!result) throw Error('Some problems with fundFilenamesWithRetries'); + }); + + // 3. Configure App Promotion + const missingPallets = helper.fetchMissingPalletNames([Pallets.AppPromotion]); + if(missingPallets.length === 0) { + const superuser = await privateKey('//Alice'); + const palletAddress = helper.arrange.calculatePalletAddress('appstake'); + const palletAdmin = await privateKey('//PromotionAdmin'); + const api = helper.getApi(); + await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address}))); + const nominal = helper.balance.getOneTokenNominal(); + await helper.balance.transferToSubstrate(superuser, palletAdmin.address, 10000n * nominal); + await helper.balance.transferToSubstrate(superuser, palletAddress, 10000n * nominal); + await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [api.tx.configuration + .setAppPromotionConfigurationOverride({ + recalculationInterval: LOCKING_PERIOD, + pendingInterval: UNLOCKING_PERIOD})], true); + } + } catch (error) { + console.error(error); + throw Error('Error during globalSetup'); + } + }); +}; + +async function getFiles(rootPath: string): Promise { + const files = await fs.readdir(rootPath, {withFileTypes: true}); + const filenames: string[] = []; + for(const entry of files) { + const res = path.resolve(rootPath, entry.name); + if(entry.isDirectory()) { + filenames.push(...await getFiles(res)); + } else { + filenames.push(res); + } + } + return filenames; +} + +const fundFilenames = async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const oneToken = helper.balance.getOneTokenNominal(); + const alice = await privateKey('//Alice'); + const nonce = await helper.chain.getNonce(alice.address); + const filenames = await getFiles(path.resolve(dirname, '..')); + + // batching is actually undesireable, it takes away the time while all the transactions actually succeed + const batchSize = 300; + let balanceGrantedCounter = 0; + for(let b = 0; b < filenames.length; b += batchSize) { + const tx: Promise[] = []; + let batchBalanceGrantedCounter = 0; + for(let i = 0; batchBalanceGrantedCounter < batchSize && b + i < filenames.length; i++) { + const f = filenames[b + i]; + if(!f.endsWith('.test.ts') && !f.endsWith('seqtest.ts') || f.includes('.outdated')) continue; + const account = await privateKey({filename: f, ignoreFundsPresence: true}); + const aliceBalance = await helper.balance.getSubstrate(account.address); + + if(aliceBalance < MINIMUM_DONOR_FUND * oneToken) { + tx.push(helper.executeExtrinsic( + alice, + 'api.tx.balances.transfer', + [account.address, DONOR_FUNDING * oneToken], + true, + {nonce: nonce + balanceGrantedCounter++}, + ).then(() => true).catch(() => {console.error(`Transaction to ${path.basename(f)} registered as failed. Strange.`); return false;})); + batchBalanceGrantedCounter++; + } + } + + if(tx.length > 0) { + console.log(`Granting funds to ${batchBalanceGrantedCounter} filename accounts.`); + const result = await Promise.all(tx); + if(result && result.lastIndexOf(false) > -1) throw new Error('The transactions actually probably succeeded, should check the balances.'); + } + } + + if(balanceGrantedCounter == 0) console.log('No account needs additional funding.'); + }); +}; + +const fundFilenamesWithRetries = (retriesLeft: number): Promise => { + if(retriesLeft <= 0) return Promise.resolve(false); + return fundFilenames() + .then(() => Promise.resolve(true)) + .catch(e => { + console.error(e); + console.error(`Some transactions might have failed. ${retriesLeft > 1 ? 'Retrying...' : 'Something is wrong.'}\n`); + return fundFilenamesWithRetries(--retriesLeft); + }); +}; + +globalSetup().catch(e => { + console.error('Setup error'); + console.error(e); + process.exit(1); +}); --- /dev/null +++ b/js-packages/tests/util/identitySetter.ts @@ -0,0 +1,229 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// SPDX-License-Identifier: Apache-2.0 +// +// Pulls identities and sub-identities from a chain and then makes a preimage to later force upload them into another. +// Only changed or previously non-existent data are inserted. +// +// Usage: `yarn setIdentities [relay WS URL] [parachain WS URL] [user key] +// Example: `yarn setIdentities wss://polkadot-rpc.dwellir.com ws://localhost:9944 escape pattern miracle train sudden cart adapt embark wedding alien lamp mesh` + +import {encodeAddress} from '@polkadot/keyring'; +import type {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, Pallets} from './index.js'; +import {ChainHelperBase} from '@unique/playgrounds/unique.js'; + +const relayUrl = process.argv[2] ?? 'ws://localhost:9844'; +const paraUrl = process.argv[3] ?? 'ws://localhost:9944'; +const key = process.argv.length > 4 ? process.argv.slice(4).join(' ') : '//Alice'; + +export function extractAccountId(key: any): string { + return (key as any).toHuman()[0]; +} + +export function extractIdentityInfo(value: any): object { + const heart = (value as any).unwrap(); + const identity = heart.toJSON(); + identity.judgements = heart.judgements.toHuman(); + return identity; +} + +export function extractIdentity(key: any, value: any): [string, any] { + return [extractAccountId(key), extractIdentityInfo(value)]; +} + +export async function getIdentities(helper: ChainHelperBase, noneCasePredicate?: (key: any, value: any) => void) { + const identities: [string, any][] = []; + for(const [key, v] of await helper.getApi().query.identity.identityOf.entries()) { + const value = v as any; + if(value.isNone) { + if(noneCasePredicate) noneCasePredicate(key, value); + continue; + } + identities.push(extractIdentity(key, value)); + } + return identities; +} + +// whether the existing chain data is more important than the coming +function isCurrentChainDataPriority(helper: ChainHelperBase, currentChainId: number | undefined, relayChainId: number) { + if(!currentChainId) return false; + // information has come from local chain, and is automatically superior + if(currentChainId == helper.chain.getChainProperties().ss58Format) return true; + // the lower the id, the more important it is (Polkadot has ss58 prefix = 0, Kusama has ss58 prefix = 2) + return currentChainId < relayChainId; +} + +// construct an object with all data necessary for insertion from storage query results +export function constructSubInfo(identityAccount: string, subQuery: any, supers: any[], ss58?: number): [string, any] { + const deposit = subQuery.toJSON()[0]; + const subIdentities = subQuery.toHuman()[1]; + subIdentities.map((sub: string) => supers.find((sup: any) => sup[0] === sub)); + + return [ + encodeAddress(identityAccount, ss58), [ + deposit, + subIdentities.map((sub: string): [string, object] | null => { + const sup = supers.find((sup: any) => sup[0] === sub); + if(!sup) { + console.error(`Error: Could not find info on super for \nsub-identity account\t${sub} of \nsuper account \t\t${identityAccount}, skipping.`); + return null; + } + return [encodeAddress(sub, ss58), sup[1].toJSON()[1]]; + }).filter((x: any) => x), + ], + ]; +} + +export async function getSubs(helper: ChainHelperBase) { + return (await helper.getApi().query.identity.subsOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]); +} + +export async function getSupers(helper: ChainHelperBase) { + return (await helper.getApi().query.identity.superOf.entries()).map(([key, value]) => [extractAccountId(key), value as any]); +} + +async function uploadPreimage(helper: ChainHelperBase, preimageMaker: IKeyringPair, preimage: string) { + try { + await helper.executeExtrinsic(preimageMaker, 'api.tx.preimage.notePreimage', [preimage]); + } catch (e: any) { + if(e.message.includes('AlreadyNoted')) { + console.warn('Warning: The same preimage already exists on the chain. Nothing was uploaded.'); + } else { + console.error(e); + } + } +} + +// The utility for pulling identity and sub-identity data +const forceInsertIdentities = async (): Promise => { + let relaySS58Prefix = 0; + const identitiesOnRelay: any[] = []; + const subsOnRelay: any[] = []; + const identitiesToRemove: string[] = []; + await usingPlaygrounds(async helper => { + try { + relaySS58Prefix = helper.chain.getChainProperties().ss58Format; + // iterate over every identity + for(const [key, value] of await getIdentities(helper, (key, _value) => identitiesToRemove.push((key as any).toHuman()[0]))) { + // if any of the judgements resulted in a good confirmed outcome, keep this identity + let knownGood = false, reasonable = false; + for(const [_id, judgement] of value.judgements) { + if(judgement == 'KnownGood') knownGood = true; + if(judgement == 'Reasonable') reasonable = true; + } + if(!(reasonable || knownGood)) continue; + // replace the registrator id with the relay chain's ss58 format + value.judgements = [[helper.chain.getChainProperties().ss58Format, knownGood ? 'KnownGood' : 'Reasonable']]; + identitiesOnRelay.push([key, value]); + } + + const sublessIdentities = [...identitiesOnRelay]; + const supersOfSubs = await getSupers(helper); + + // iterate over every sub-identity + for(const [key, value] of await getSubs(helper)) { + // only get subs of the identities interesting to us + const identityIndex = sublessIdentities.findIndex((x: any) => x[0] == key); + if(identityIndex == -1) continue; + sublessIdentities.splice(identityIndex, 1); + subsOnRelay.push(constructSubInfo(key, value, supersOfSubs)); + } + + // mark the rest of sub-identities for deletion with empty arrays + /*for(const [account, _identity] of sublessIdentities) { + subsOnRelay.push([account, ['0', []]]); + }*/ + } catch (error) { + console.error(error); + throw Error('Error during fetching identities'); + } + }, relayUrl); + + await usingPlaygrounds(async (helper, privateKey) => { + if(helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) console.error('pallet-identity is not included in parachain.'); + if(helper.fetchMissingPalletNames([Pallets.Preimage]).length != 0) console.error('pallet-preimage is not included in parachain.'); + + try { + const preimageMaker = await privateKey(key); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const paraIdentities = await getIdentities(helper); + const identitiesToAdd: any[] = []; + const paraAccountsRegistrators: {[name: string]: number} = {}; + + // cross-reference every account for changes + for(const [key, value] of identitiesOnRelay) { + const encodedKey = encodeAddress(key, ss58Format); + + // only update if the identity info does not exist or is changed + const identity = paraIdentities.find(i => i[0] === encodedKey); + if(identity) { + const registratorId = identity[1].judgements[0][0]; + paraAccountsRegistrators[encodedKey] = registratorId; + if(isCurrentChainDataPriority(helper, registratorId, value.judgements[0][0]) + || JSON.stringify(value.info) === JSON.stringify(identity[1].info)) { + continue; + } + } + + identitiesToAdd.push([key, value]); + // exercise caution - in case we have an identity and the realy doesn't, it might mean one of two things: + // 1) it was deleted on the relay; + // 2) it is our own identity, we don't want to delete it. + // identitiesToRemove.push((key as any).toHuman()[0]); + } + + if(identitiesToRemove.length != 0) + await uploadPreimage( + helper, + preimageMaker, + helper.constructApiCall('api.tx.identity.forceRemoveIdentities', [identitiesToRemove]).method.toHex(), + ); + if(identitiesToAdd.length != 0) + await uploadPreimage( + helper, + preimageMaker, + helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]).method.toHex(), + ); + + console.log(`Tried to push ${identitiesToAdd.length} identities` + + ` and found ${identitiesToRemove.length} identities for potential removal.` + + ` Currently there are ${(await helper.getApi().query.identity.identityOf.keys()).length} identities on the chain.`); + + // fill sub-identities + const paraSubs = await getSubs(helper); + const supersOfSubs = await getSupers(helper); + const subsToUpdate: any[] = []; + + for(const [key, value] of subsOnRelay) { + const encodedKey = encodeAddress(key, ss58Format); + const sub = paraSubs.find(i => i[0] === encodedKey); + if(sub) { + // only update if the sub-identity info does not exist or is changed + if(isCurrentChainDataPriority(helper, paraAccountsRegistrators[encodedKey], relaySS58Prefix) + || JSON.stringify(value) === JSON.stringify(constructSubInfo(sub[0], sub[1], supersOfSubs)[1])) { + continue; + } + } else if(value[1].length == 0) + continue; + + subsToUpdate.push([key, value]); + } + + if(subsToUpdate.length != 0) + await uploadPreimage( + helper, + preimageMaker, + helper.constructApiCall('api.tx.identity.forceSetSubs', [subsToUpdate]).method.toHex(), + ); + + console.log(`Also tried to push ${subsToUpdate.length} identities with their sub-identities.` + + ` Currently there are ${(await helper.getApi().query.identity.subsOf.keys()).length} identities with subs.`); + } catch (error) { + console.error(error); + throw Error('Error during setting identities'); + } + }, paraUrl); +}; + +if(process.argv[1] === module.filename) + forceInsertIdentities().catch(() => process.exit(1)); --- /dev/null +++ b/js-packages/tests/util/index.ts @@ -0,0 +1,221 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +import * as path from 'path'; +import * as crypto from 'crypto'; +import type {IKeyringPair} from '@polkadot/types/types/interfaces'; +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSubset from 'chai-subset'; +import {Context} from 'mocha'; +import config from '../config.js'; +import {ChainHelperBase} from '@unique/playgrounds/unique.js'; +import type {ILogger} from '@unique/playgrounds/types.js'; +import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper, DevPolkadexHelper} from '@unique/playgrounds/unique.dev.js'; +import {dirname} from 'path'; +import {fileURLToPath} from 'url'; + +chai.config.truncateThreshold = 0; +chai.use(chaiAsPromised); +chai.use(chaiSubset); +export const expect = chai.expect; + +const getTestHash = (filename: string) => crypto.createHash('md5').update(filename).digest('hex'); + +export const getTestSeed = (filename: string) => `//Alice+${getTestHash(filename)}`; + +async function usingPlaygroundsGeneral( + helperType: new (logger: ILogger) => T, + url: string, + code: (helper: T, privateKey: (seed: string | { filename?: string, url?: string, ignoreFundsPresence?: boolean }) => Promise) => Promise, +): Promise { + const silentConsole = new SilentConsole(); + silentConsole.enable(); + + const helper = new helperType(new SilentLogger()); + let result; + try { + await helper.connect(url); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const privateKey = async (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => { + if(typeof seed === 'string') { + return helper.util.fromSeed(seed, ss58Format); + } + if(seed.url) { + const {filename} = makeNames(seed.url); + seed.filename = filename; + } else if(seed.filename) { + // Pass + } else { + throw new Error('no url nor filename set'); + } + const actualSeed = getTestSeed(seed.filename); + let account = helper.util.fromSeed(actualSeed, ss58Format); + // here's to hoping that no + if(!seed.ignoreFundsPresence && ((helper as any)['balance'] == undefined || await (helper as any).balance.getSubstrate(account.address) < MINIMUM_DONOR_FUND)) { + console.warn(`${path.basename(seed.filename)}: Not enough funds present on the filename account. Using the default one as the donor instead.`); + account = helper.util.fromSeed('//Alice', ss58Format); + } + return account; + }; + result = await code(helper, privateKey); + } + finally { + await helper.disconnect(); + silentConsole.disable(); + } + return result as any as R; +} + +export const usingPlaygrounds = (code: (helper: DevUniqueHelper, privateKey: (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => Promise) => Promise, url: string = config.substrateUrl) => usingPlaygroundsGeneral(DevUniqueHelper, url, code); + +export const usingWestmintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevWestmintHelper, url, code); + +export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevWestmintHelper, url, code); + +export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevWestmintHelper, url, code); + +export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevRelayHelper, url, code); + +export const usingAcalaPlaygrounds = (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevAcalaHelper, url, code); + +export const usingKaruraPlaygrounds = (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevAcalaHelper, url, code); + +export const usingMoonbeamPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevMoonbeamHelper, url, code); + +export const usingMoonriverPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevMoonriverHelper, url, code); + +export const usingAstarPlaygrounds = (url: string, code: (helper: DevAstarHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevAstarHelper, url, code); + +export const usingShidenPlaygrounds = (url: string, code: (helper: DevShidenHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevShidenHelper, url, code); + +export const usingPolkadexPlaygrounds = (url: string, code: (helper: DevPolkadexHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevPolkadexHelper, url, code); + +export const MINIMUM_DONOR_FUND = 4_000_000n; +export const DONOR_FUNDING = 4_000_000n; + +// App-promotion periods: +export const LOCKING_PERIOD = 12n; // 12 blocks of relay +export const UNLOCKING_PERIOD = 6n; // 6 blocks of parachain + +// Native contracts +export const COLLECTION_HELPER = '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f'; +export const CONTRACT_HELPER = '0x842899ECF380553E8a4de75bF534cdf6fBF64049'; + +export enum Pallets { + Inflation = 'inflation', + ReFungible = 'refungible', + Fungible = 'fungible', + NFT = 'nonfungible', + Scheduler = 'scheduler', + UniqueScheduler = 'uniqueScheduler', + AppPromotion = 'apppromotion', + CollatorSelection = 'collatorselection', + Session = 'session', + Identity = 'identity', + Democracy = 'democracy', + Council = 'council', + //CouncilMembership = 'councilmembership', + TechnicalCommittee = 'technicalcommittee', + Fellowship = 'fellowshipcollective', + Preimage = 'preimage', + Maintenance = 'maintenance', + TestUtils = 'testutils', +} + +export function requirePalletsOrSkip(test: Context, helper: DevUniqueHelper, requiredPallets: readonly string[]) { + const missingPallets = helper.fetchMissingPalletNames(requiredPallets); + + if(missingPallets.length > 0) { + const skipMsg = `\tSkipping test '${test.test?.title}'.\n\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`; + console.warn('\x1b[38:5:208m%s\x1b[0m', skipMsg); + test.skip(); + } +} + +export function itSub(name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) { + (opts.only ? it.only : + opts.skip ? it.skip : it)(name, async function () { + await usingPlaygrounds(async (helper, privateKey) => { + if(opts.requiredPallets) { + requirePalletsOrSkip(this, helper, opts.requiredPallets); + } + + await cb({helper, privateKey}); + }); + }); +} +export function itSubIfWithPallet(name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) { + return itSub(name, cb, {requiredPallets: required, ...opts}); +} +itSub.only = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSub(name, cb, {only: true}); +itSub.skip = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSub(name, cb, {skip: true}); + +itSubIfWithPallet.only = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSubIfWithPallet(name, required, cb, {only: true}); +itSubIfWithPallet.skip = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSubIfWithPallet(name, required, cb, {skip: true}); +itSub.ifWithPallets = itSubIfWithPallet; + +export type SchedKind = 'anon' | 'named'; + +export function itSched( + name: string, + cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any, + opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}, +) { + itSub(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts); + itSub(name + ' (named scheduling)', (apis) => cb('named', apis), opts); +} +itSched.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSched(name, cb, {only: true}); +itSched.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSched(name, cb, {skip: true}); +itSched.ifWithPallets = itSchedIfWithPallets; + +function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) { + return itSched(name, cb, {requiredPallets: required, ...opts}); +} + +export function describeXCM(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) { + (process.env.RUN_XCM_TESTS && !opts.skip + ? describe + : describe.skip)(title, fn); +} + +describeXCM.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeXCM(name, fn, {skip: true}); + +export function describeGov(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) { + (process.env.RUN_GOV_TESTS && !opts.skip + ? describe + : describe.skip)(title, fn); +} + +describeGov.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeGov(name, fn, {skip: true}); + +export function sizeOfInt(i: number) { + if(i < 0 || i > 0xffffffff) throw new Error('out of range'); + if(i < 0b11_1111) { + return 1; + } else if(i < 0b11_1111_1111_1111) { + return 2; + } else if(i < 0b11_1111_1111_1111_1111_1111_1111_1111) { + return 4; + } else { + return 5; + } +} + +const UTF8_ENCODER = new TextEncoder(); +export function sizeOfEncodedStr(v: string) { + const encoded = UTF8_ENCODER.encode(v); + return sizeOfInt(encoded.length) + encoded.length; +} + +export function sizeOfProperty(prop: {key: string, value: string}) { + return sizeOfEncodedStr(prop.key) + sizeOfEncodedStr(prop.value); +} + +export function makeNames(url: string) { + const filename = fileURLToPath(url); + return { + filename, + dirname: dirname(filename), + }; +} --- /dev/null +++ b/js-packages/tests/util/relayIdentitiesChecker.ts @@ -0,0 +1,114 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// SPDX-License-Identifier: Apache-2.0 +// +// Checks and reports the differences between identities and sub-identities on two chains. +// +// Usage: `yarn checkRelayIdentities [relay-1 WS URL] [relay-2 WS URL]` +// Example: `yarn checkRelayIdentities wss://polkadot-rpc.dwellir.com wss://kusama-rpc.dwellir.com` + +import {encodeAddress} from '@polkadot/keyring'; +import {usingPlaygrounds} from './index.js'; +import {getIdentities, getSubs, getSupers, constructSubInfo} from './identitySetter.js'; + +const relay1Url = process.argv[2] ?? 'ws://localhost:9844'; +const relay2Url = process.argv[3] ?? 'ws://localhost:9844'; + +async function pullIdentities(relayUrl: string): Promise<[any[], any[]]> { + const identities: any[] = []; + const subs: any[] = []; + + await usingPlaygrounds(async helper => { + try { + // iterate over every identity + for(const [key, value] of await getIdentities(helper)) { + // if any of the judgements resulted in a good confirmed outcome, keep this identity + if(value.toHuman().judgements.filter((x: any) => x[1] == 'Reasonable' || x[1] == 'KnownGood').length == 0) continue; + identities.push([key, value]); + } + + const supersOfSubs = await getSupers(helper); + + // iterate over every sub-identity + for(const [key, value] of await getSubs(helper)) { + // only get subs of the identities interesting to us + if(identities.find((x: any) => x[0] == key) == -1) continue; + subs.push(constructSubInfo(key, value, supersOfSubs)); + } + } catch (error) { + console.error(error); + throw Error(`Error during fetching identities on ${relayUrl}`); + } + }, relayUrl); + + return [identities, subs]; +} + +// The utility for pulling identity and sub-identity data +const checkRelayIdentities = async (): Promise => { + const [identitiesOnRelay1, subIdentitiesOnRelay1] = await pullIdentities(relay1Url); + const [identitiesOnRelay2, subIdentitiesOnRelay2] = await pullIdentities(relay2Url); + + console.log('identities counts:\t', identitiesOnRelay1.length, identitiesOnRelay2.length); + console.log('sub-identities counts:\t', subIdentitiesOnRelay1.length, subIdentitiesOnRelay2.length); + console.log(); + + try { + const matchingAddresses: string[] = []; + const inequalIdentities: {[name: string]: [any, any]} = {}; + + for(const [key1, value1] of identitiesOnRelay1) { + const address = encodeAddress(key1); + const identity2 = identitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2)); + if(!identity2) continue; + matchingAddresses.push(address); + + //const [[key2, value2]] = identitiesOnRelay2.splice(index2, 1); + const [_key2, value2] = identity2; + if(JSON.stringify(value1.info) === JSON.stringify(value2.info)) continue; + inequalIdentities[address] = [value1, value2]; + } + + /*for (const [v1, v2] of Object.values(inequalIdentities)) { + console.log(v1.toHuman().info); + console.log(); + console.log(v2.toHuman().info); + await new Promise(resolve => setTimeout(resolve, 4000)); + }*/ + + console.log(`Accounts with identities on both relays:\t${matchingAddresses.length}`); + console.log(`Sub-identities with conflicting information:\t${Object.entries(inequalIdentities).length}`); + console.log(); + + const inequalSubIdentities = []; + let matchesFound = 0; + for(const address of matchingAddresses) { + const sub1 = subIdentitiesOnRelay1.find(([key1, _value1]) => address === encodeAddress(key1)); + if(!sub1) continue; + const sub2 = subIdentitiesOnRelay2.find(([key2, _value2]) => address === encodeAddress(key2)); + if(!sub2) continue; + + const [value1, value2] = [sub1[1], sub2[1]]; + matchesFound++; + + if(JSON.stringify(value1[1]) === JSON.stringify(value2[1])) { + continue; + } + inequalSubIdentities.push([value1, value2]); + } + + /*for (const [v1, v2] of inequalSubIdentities) { + console.log(v1[1]); + console.log(); + console.log(v2[1]); + await new Promise(resolve => setTimeout(resolve, 300)); + }*/ + console.log(`Accounts with sub-identities on both relays:\t${matchesFound}`); + console.log(`Of them, those with conflicting sub-identities:\t${inequalSubIdentities.length}`); + } catch (error) { + console.error(error); + throw Error('Error during comparison'); + } +}; + +if(process.argv[1] === module.filename) + checkRelayIdentities().catch(() => process.exit(1)); --- /dev/null +++ b/js-packages/tests/util/setCode.ts @@ -0,0 +1,15 @@ +import {readFile} from 'fs/promises'; +import {u8aToHex} from '@polkadot/util'; +import {usingPlaygrounds} from './index.js'; + +const codePath = process.argv[2]; +if(!codePath) throw new Error('missing code path argument'); + +const code = await readFile(codePath); + +await usingPlaygrounds(async (helper, privateKey) => { + const alice = await privateKey('//Alice'); + await helper.getSudo().executeExtrinsicUncheckedWeight(alice, 'api.tx.system.setCode', [u8aToHex(code)]); +}); +// We miss disconnect/unref somewhere. +process.exit(0); --- /dev/null +++ b/js-packages/tests/vesting.test.ts @@ -0,0 +1,151 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from './util/index.js'; + +describe('Vesting', () => { + let donor: IKeyringPair; + let nominal: bigint; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + nominal = helper.balance.getOneTokenNominal(); + }); + }); + + itSub('can perform vestedTransfer and claim tokens', async ({helper}) => { + // arrange + const [sender, recepient] = await helper.arrange.createAccounts([1000n, 1n], donor); + const currentRelayBlock = await helper.chain.getRelayBlockNumber(); + const SCHEDULE_1_PERIOD = 6n; // 6 blocks one period + const SCHEDULE_1_START = currentRelayBlock + 6n; // Block when 1 schedule starts + const SCHEDULE_2_PERIOD = 12n; // 12 blocks one period + const SCHEDULE_2_START = currentRelayBlock + 12n; // Block when 2 schedule starts + const schedule1 = {start: SCHEDULE_1_START, period: SCHEDULE_1_PERIOD, periodCount: 2n, perPeriod: 50n * nominal}; + const schedule2 = {start: SCHEDULE_2_START, period: SCHEDULE_2_PERIOD, periodCount: 2n, perPeriod: 100n * nominal}; + + // act + await helper.balance.vestedTransfer(sender, recepient.address, schedule1); + await helper.balance.vestedTransfer(sender, recepient.address, schedule2); + let schedule = await helper.balance.getVestingSchedules(recepient.address); + + // check senders balance after vesting: + let balanceSender = await helper.balance.getSubstrateFull(sender.address); + expect(balanceSender.free / nominal).to.eq(699n); + expect(balanceSender.frozen).to.eq(0n); + expect(balanceSender.reserved).to.eq(0n); + + // check recepient balance after vesting: + let balanceRecepient = await helper.balance.getSubstrateFull(recepient.address); + expect(balanceRecepient.free).to.eq(301n * nominal); + expect(balanceRecepient.frozen).to.eq(300n * nominal); + expect(balanceRecepient.reserved).to.eq(0n); + + // Schedules list correct: + expect(schedule).to.has.length(2); + expect(schedule[0]).to.deep.eq(schedule1); + expect(schedule[1]).to.deep.eq(schedule2); + + // Wait first part available: + await helper.wait.forRelayBlockNumber(SCHEDULE_1_START + SCHEDULE_1_PERIOD); + await helper.balance.claim(recepient); + + // check recepient balance after claim (50 tokens claimed, 250 left): + balanceRecepient = await helper.balance.getSubstrateFull(recepient.address); + expect(balanceRecepient.free / nominal).to.eq(300n); + expect(balanceRecepient.frozen).to.eq(250n * nominal); + expect(balanceRecepient.reserved).to.eq(0n); + + // Wait first schedule ends and first part od second schedule: + await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD); + await helper.balance.claim(recepient); + + // check recepient balance after second claim (150 tokens claimed, 100 left): + balanceRecepient = await helper.balance.getSubstrateFull(recepient.address); + expect(balanceRecepient.free / nominal).to.eq(300n); + expect(balanceRecepient.frozen).to.eq(100n * nominal); + expect(balanceRecepient.reserved).to.eq(0n); + + // Schedules list contain 1 vesting: + schedule = await helper.balance.getVestingSchedules(recepient.address); + expect(schedule).to.has.length(1); + expect(schedule[0]).to.deep.eq(schedule2); + + // Wait 2 schedule ends: + await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD * 2n); + await helper.balance.claim(recepient); + + // check recepient balance after second claim (100 tokens claimed, 0 left): + balanceRecepient = await helper.balance.getSubstrateFull(recepient.address); + expect(balanceRecepient.free / nominal).to.eq(300n); + expect(balanceRecepient.frozen).to.eq(0n); + expect(balanceRecepient.reserved).to.eq(0n); + + // check sender balance does not changed: + balanceSender = await helper.balance.getSubstrateFull(sender.address); + expect(balanceSender.free / nominal).to.eq(699n); + expect(balanceSender.frozen).to.eq(0n); + expect(balanceSender.reserved).to.eq(0n); + }); + + itSub('cannot send more tokens than have', async ({helper}) => { + const [sender, receiver] = await helper.arrange.createAccounts([1000n, 1n], donor); + const schedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 100n * nominal}; + const manyPeriodsSchedule = {start: 0n, period: 1n, periodCount: 100n, perPeriod: 10n * nominal}; + const oneBigSumSchedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 5000n * nominal}; + + // Sender cannot send vestedTransfer to self or other + await expect(helper.balance.vestedTransfer(sender, sender.address, manyPeriodsSchedule)).to.be.rejectedWith(/^vesting.InsufficientBalanceToLock$/); + await expect(helper.balance.vestedTransfer(sender, receiver.address, manyPeriodsSchedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/); + await expect(helper.balance.vestedTransfer(sender, sender.address, oneBigSumSchedule)).to.be.rejectedWith(/^vesting.InsufficientBalanceToLock$/); + await expect(helper.balance.vestedTransfer(sender, receiver.address, oneBigSumSchedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/); + + const balanceSender = await helper.balance.getSubstrateFull(sender.address); + const balanceReceiver = await helper.balance.getSubstrateFull(receiver.address); + + // Sender's balance has not changed + expect(balanceSender.free / nominal).to.eq(999n); + expect(balanceSender.frozen).to.eq(0n); + expect(balanceSender.reserved).to.eq(0n); + + // Receiver's balance has not changed + expect(balanceReceiver.free).to.be.eq(1n * nominal); + expect(balanceReceiver.frozen).to.be.eq(0n); + expect(balanceReceiver.reserved).to.be.eq(0n); + + // Receiver cannot send vestedTransfer back because of freeze + await expect(helper.balance.vestedTransfer(receiver, sender.address, schedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/); + }); + + itSub('cannot send vestedTransfer with incorrect parameters', async ({helper}) => { + const [sender, receiver] = await helper.arrange.createAccounts([1000n, 1n], donor); + const incorrectperiodSchedule = {start: 0n, period: 0n, periodCount: 10n, perPeriod: 10n * nominal}; + const incorrectPeriodCountSchedule = {start: 0n, period: 1n, periodCount: 0n, perPeriod: 10n * nominal}; + const incorrectPerPeriodSchedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 0n * nominal}; + + await expect(helper.balance.vestedTransfer(sender, sender.address, incorrectperiodSchedule)).to.be.rejectedWith(/vesting.ZeroVestingPeriod/); + await expect(helper.balance.vestedTransfer(sender, receiver.address, incorrectPeriodCountSchedule)).to.be.rejectedWith(/vesting.ZeroVestingPeriod/); + await expect(helper.balance.vestedTransfer(sender, receiver.address, incorrectPerPeriodSchedule)).to.be.rejectedWith(/vesting.AmountLow/); + + const balanceSender = await helper.balance.getSubstrateFull(sender.address); + // Sender's balance has not changed + expect(balanceSender.free / nominal).to.eq(999n); + expect(balanceSender.frozen).to.eq(0n); + expect(balanceSender.reserved).to.eq(0n); + }); +}); --- /dev/null +++ b/js-packages/tests/xcm/lowLevelXcmQuartz.test.ts @@ -0,0 +1,364 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util/index.js'; +import {QUARTZ_CHAIN, QTZ_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, shidenUrl, SAFE_XCM_VERSION, XcmTestHelper, TRANSFER_AMOUNT, SENDER_BUDGET, relayUrl} from './xcm.types.js'; +import {hexToString} from '@polkadot/util'; + +const testHelper = new XcmTestHelper('quartz'); + +describeXCM('[XCMLL] Integration test: Exchanging tokens with Karura', () => { + let alice: IKeyringPair; + let randomAccount: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + [randomAccount] = await helper.arrange.createAccounts([0n], alice); + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingKaruraPlaygrounds(karuraUrl, async (helper) => { + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }; + + const metadata = { + name: 'Quartz', + symbol: 'QTZ', + decimals: 18, + minimalBalance: 1000000000000000000n, + }; + + const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) => + hexToString(v.toJSON()['symbol'])) as string[]; + + if(!assets.includes('QTZ')) { + await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata); + } else { + console.log('QTZ token already registered on Karura assetRegistry pallet'); + } + await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); + }); + + await usingPlaygrounds(async (helper) => { + await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET); + }); + }); + + itSub('Should connect and send QTZ to Karura', async () => { + await testHelper.sendUnqTo('karura', randomAccount); + }); + + itSub('Should connect to Karura and send QTZ back', async () => { + await testHelper.sendUnqBack('karura', alice, randomAccount); + }); + + itSub('Karura can send only up to its balance', async () => { + await testHelper.sendOnlyOwnedBalance('karura', alice); + }); +}); +// These tests are relevant only when +// the the corresponding foreign assets are not registered +describeXCM('[XCMLL] Integration test: Quartz rejects non-native tokens', () => { + let alice: IKeyringPair; + + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + + + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + }); + + itSub('Quartz rejects KAR tokens from Karura', async () => { + await testHelper.rejectNativeTokensFrom('karura', alice); + }); + + itSub('Quartz rejects MOVR tokens from Moonriver', async () => { + await testHelper.rejectNativeTokensFrom('moonriver', alice); + }); + + itSub('Quartz rejects SDN tokens from Shiden', async () => { + await testHelper.rejectNativeTokensFrom('shiden', alice); + }); +}); + +describeXCM('[XCMLL] Integration test: Exchanging QTZ with Moonriver', () => { + // Quartz constants + let alice: IKeyringPair; + let quartzAssetLocation; + + let randomAccountQuartz: IKeyringPair; + let randomAccountMoonriver: IKeyringPair; + + // Moonriver constants + let assetId: string; + + const quartzAssetMetadata = { + name: 'xcQuartz', + symbol: 'xcQTZ', + decimals: 18, + isFrozen: false, + minimalBalance: 1n, + }; + + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice); + + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { + const alithAccount = helper.account.alithAccount(); + const baltatharAccount = helper.account.baltatharAccount(); + const dorothyAccount = helper.account.dorothyAccount(); + + randomAccountMoonriver = helper.account.create(); + + // >>> Sponsoring Dorothy >>> + console.log('Sponsoring Dorothy.......'); + await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n); + console.log('Sponsoring Dorothy.......DONE'); + // <<< Sponsoring Dorothy <<< + + quartzAssetLocation = { + XCM: { + parents: 1, + interior: {X1: {Parachain: QUARTZ_CHAIN}}, + }, + }; + const existentialDeposit = 1n; + const isSufficient = true; + const unitsPerSecond = 1n; + const numAssetsWeightHint = 0; + if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) { + console.log('Quartz asset already registered on Moonriver'); + } else { + const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({ + location: quartzAssetLocation, + metadata: quartzAssetMetadata, + existentialDeposit, + isSufficient, + unitsPerSecond, + numAssetsWeightHint, + }); + + console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal); + + await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal); + } + // >>> Acquire Quartz AssetId Info on Moonriver >>> + console.log('Acquire Quartz AssetId Info on Moonriver.......'); + + assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString(); + + console.log('QTZ asset ID is %s', assetId); + console.log('Acquire Quartz AssetId Info on Moonriver.......DONE'); + // >>> Acquire Quartz AssetId Info on Moonriver >>> + + // >>> Sponsoring random Account >>> + console.log('Sponsoring random Account.......'); + await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n); + console.log('Sponsoring random Account.......DONE'); + // <<< Sponsoring random Account <<< + }); + + await usingPlaygrounds(async (helper) => { + await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT); + }); + }); + + itSub('Should connect and send QTZ to Moonriver', async () => { + await testHelper.sendUnqTo('moonriver', randomAccountQuartz, randomAccountMoonriver); + }); + + itSub('Should connect to Moonriver and send QTZ back', async () => { + await testHelper.sendUnqBack('moonriver', alice, randomAccountQuartz); + }); + + itSub('Moonriver can send only up to its balance', async () => { + await testHelper.sendOnlyOwnedBalance('moonriver', alice); + }); + + itSub('Should not accept reserve transfer of QTZ from Moonriver', async () => { + await testHelper.rejectReserveTransferUNQfrom('moonriver', alice); + }); +}); + +describeXCM('[XCMLL] Integration test: Exchanging tokens with Shiden', () => { + let alice: IKeyringPair; + let randomAccount: IKeyringPair; + + const QTZ_ASSET_ID_ON_SHIDEN = 18_446_744_073_709_551_633n; // The value is taken from the live Shiden + const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n; // The value is taken from the live Shiden + + // Quartz -> Shiden + const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden + const unitsPerSecond = 500_451_000_000_000_000_000n; // The value is taken from the live Shiden + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + randomAccount = helper.arrange.createEmptyAccount(); + await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET); + console.log('sender: ', randomAccount.address); + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingShidenPlaygrounds(shidenUrl, async (helper) => { + if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) { + console.log('1. Create foreign asset and metadata'); + await helper.getSudo().assets.forceCreate( + alice, + QTZ_ASSET_ID_ON_SHIDEN, + alice.address, + QTZ_MINIMAL_BALANCE_ON_SHIDEN, + ); + + await helper.assets.setMetadata( + alice, + QTZ_ASSET_ID_ON_SHIDEN, + 'Quartz', + 'QTZ', + Number(QTZ_DECIMALS), + ); + + console.log('2. Register asset location on Shiden'); + const assetLocation = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }; + + await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]); + + console.log('3. Set QTZ payment for XCM execution on Shiden'); + await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]); + } else { + console.log('QTZ is already registered on Shiden'); + } + console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)'); + await helper.balance.transferToSubstrate(alice, randomAccount.address, shidenInitialBalance); + }); + }); + + itSub('Should connect and send QTZ to Shiden', async () => { + await testHelper.sendUnqTo('shiden', randomAccount); + }); + + itSub('Should connect to Shiden and send QTZ back', async () => { + await testHelper.sendUnqBack('shiden', alice, randomAccount); + }); + + itSub('Shiden can send only up to its balance', async () => { + await testHelper.sendOnlyOwnedBalance('shiden', alice); + }); + + itSub('Should not accept reserve transfer of QTZ from Shiden', async () => { + await testHelper.rejectReserveTransferUNQfrom('shiden', alice); + }); +}); + +describeXCM('[XCMLL] Integration test: The relay can do some root ops', () => { + let sudoer: IKeyringPair; + + before(async function () { + await usingRelayPlaygrounds(relayUrl, async (_, privateKey) => { + sudoer = await privateKey('//Alice'); + }); + }); + + // At the moment there is no reliable way + // to establish the correspondence between the `ExecutedDownward` event + // and the relay's sent message due to `SetTopic` instruction + // containing an unpredictable topic silently added by the relay's messages on the router level. + // This changes the message hash on arrival to our chain. + // + // See: + // * The relay's router: https://github.com/paritytech/polkadot-sdk/blob/f60318f68687e601c47de5ad5ca88e2c3f8139a7/polkadot/runtime/westend/src/xcm_config.rs#L83 + // * The `WithUniqueTopic` helper: https://github.com/paritytech/polkadot-sdk/blob/945ebbbcf66646be13d5b1d1bc26c8b0d3296d9e/polkadot/xcm/xcm-builder/src/routing.rs#L36 + // + // Because of this, we insert time gaps between tests so + // different `ExecutedDownward` events won't interfere with each other. + afterEach(async () => { + await usingPlaygrounds(async (helper) => { + await helper.wait.newBlocks(3); + }); + }); + + itSub('The relay can set storage', async () => { + await testHelper.relayIsPermittedToSetStorage(sudoer, 'plain'); + }); + + itSub('The relay can batch set storage', async () => { + await testHelper.relayIsPermittedToSetStorage(sudoer, 'batch'); + }); + + itSub('The relay can batchAll set storage', async () => { + await testHelper.relayIsPermittedToSetStorage(sudoer, 'batchAll'); + }); + + itSub('The relay can forceBatch set storage', async () => { + await testHelper.relayIsPermittedToSetStorage(sudoer, 'forceBatch'); + }); + + itSub('[negative] The relay cannot set balance', async () => { + await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'plain'); + }); + + itSub('[negative] The relay cannot set balance via batch', async () => { + await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batch'); + }); + + itSub('[negative] The relay cannot set balance via batchAll', async () => { + await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batchAll'); + }); + + itSub('[negative] The relay cannot set balance via forceBatch', async () => { + await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'forceBatch'); + }); + + itSub('[negative] The relay cannot set balance via dispatchAs', async () => { + await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'dispatchAs'); + }); +}); --- /dev/null +++ b/js-packages/tests/xcm/lowLevelXcmUnique.test.ts @@ -0,0 +1,430 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import config from '../config.js'; +import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds} from '../util/index.js'; +import {nToBigInt} from '@polkadot/util'; +import {hexToString} from '@polkadot/util'; +import {ASTAR_DECIMALS, SAFE_XCM_VERSION, SENDER_BUDGET, UNIQUE_CHAIN, UNQ_DECIMALS, XcmTestHelper, acalaUrl, astarUrl, moonbeamUrl, polkadexUrl, relayUrl, uniqueAssetId} from './xcm.types.js'; + +const testHelper = new XcmTestHelper('unique'); + + + + +describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => { + let alice: IKeyringPair; + let randomAccount: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + console.log(config.acalaUrl); + randomAccount = helper.arrange.createEmptyAccount(); + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingAcalaPlaygrounds(acalaUrl, async (helper) => { + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }, + }; + + const metadata = { + name: 'Unique Network', + symbol: 'UNQ', + decimals: 18, + minimalBalance: 1250_000_000_000_000_000n, + }; + const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) => + hexToString(v.toJSON()['symbol'])) as string[]; + + if(!assets.includes('UNQ')) { + await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata); + } else { + console.log('UNQ token already registered on Acala assetRegistry pallet'); + } + await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); + }); + + await usingPlaygrounds(async (helper) => { + await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET); + }); + }); + + itSub('Should connect and send UNQ to Acala', async () => { + await testHelper.sendUnqTo('acala', randomAccount); + }); + + itSub('Should connect to Acala and send UNQ back', async () => { + await testHelper.sendUnqBack('acala', alice, randomAccount); + }); + + itSub('Acala can send only up to its balance', async () => { + await testHelper.sendOnlyOwnedBalance('acala', alice); + }); + + itSub('Should not accept reserve transfer of UNQ from Acala', async () => { + await testHelper.rejectReserveTransferUNQfrom('acala', alice); + }); +}); + +describeXCM('[XCMLL] Integration test: Exchanging tokens with Polkadex', () => { + let alice: IKeyringPair; + let randomAccount: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + randomAccount = helper.arrange.createEmptyAccount(); + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { + const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', [])) + .toJSON() as []) + .map(nToBigInt).length != 0; + /* + Check whether the Unique token has been added + to the whitelist, since an error will occur + if it is added again. Needed for debugging + when this test is run multiple times. + */ + if(isWhitelisted) { + console.log('UNQ token is already whitelisted on Polkadex'); + } else { + await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId); + } + + await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); + }); + + await usingPlaygrounds(async (helper) => { + await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET); + }); + }); + + itSub('Should connect and send UNQ to Polkadex', async () => { + await testHelper.sendUnqTo('polkadex', randomAccount); + }); + + + itSub('Should connect to Polkadex and send UNQ back', async () => { + await testHelper.sendUnqBack('polkadex', alice, randomAccount); + }); + + itSub('Polkadex can send only up to its balance', async () => { + await testHelper.sendOnlyOwnedBalance('polkadex', alice); + }); + + itSub('Should not accept reserve transfer of UNQ from Polkadex', async () => { + await testHelper.rejectReserveTransferUNQfrom('polkadex', alice); + }); +}); + +// These tests are relevant only when +// the the corresponding foreign assets are not registered +describeXCM('[XCMLL] Integration test: Unique rejects non-native tokens', () => { + let alice: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + }); + + itSub('Unique rejects ACA tokens from Acala', async () => { + await testHelper.rejectNativeTokensFrom('acala', alice); + }); + + itSub('Unique rejects GLMR tokens from Moonbeam', async () => { + await testHelper.rejectNativeTokensFrom('moonbeam', alice); + }); + + itSub('Unique rejects ASTR tokens from Astar', async () => { + await testHelper.rejectNativeTokensFrom('astar', alice); + }); + + itSub('Unique rejects PDX tokens from Polkadex', async () => { + await testHelper.rejectNativeTokensFrom('polkadex', alice); + }); +}); + +describeXCM('[XCMLL] Integration test: Exchanging UNQ with Moonbeam', () => { + // Unique constants + let alice: IKeyringPair; + let uniqueAssetLocation; + + let randomAccountUnique: IKeyringPair; + let randomAccountMoonbeam: IKeyringPair; + + // Moonbeam constants + let assetId: string; + + const uniqueAssetMetadata = { + name: 'xcUnique', + symbol: 'xcUNQ', + decimals: 18, + isFrozen: false, + minimalBalance: 1n, + }; + + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice); + + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { + const alithAccount = helper.account.alithAccount(); + const baltatharAccount = helper.account.baltatharAccount(); + const dorothyAccount = helper.account.dorothyAccount(); + + randomAccountMoonbeam = helper.account.create(); + + // >>> Sponsoring Dorothy >>> + console.log('Sponsoring Dorothy.......'); + await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n); + console.log('Sponsoring Dorothy.......DONE'); + // <<< Sponsoring Dorothy <<< + uniqueAssetLocation = { + XCM: { + parents: 1, + interior: {X1: {Parachain: UNIQUE_CHAIN}}, + }, + }; + const existentialDeposit = 1n; + const isSufficient = true; + const unitsPerSecond = 1n; + const numAssetsWeightHint = 0; + + if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) { + console.log('Unique asset already registered on Moonbeam'); + } else { + const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({ + location: uniqueAssetLocation, + metadata: uniqueAssetMetadata, + existentialDeposit, + isSufficient, + unitsPerSecond, + numAssetsWeightHint, + }); + + console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal); + + await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal); + } + + // >>> Acquire Unique AssetId Info on Moonbeam >>> + console.log('Acquire Unique AssetId Info on Moonbeam.......'); + + assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString(); + + console.log('UNQ asset ID is %s', assetId); + console.log('Acquire Unique AssetId Info on Moonbeam.......DONE'); + + // >>> Sponsoring random Account >>> + console.log('Sponsoring random Account.......'); + await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n); + console.log('Sponsoring random Account.......DONE'); + // <<< Sponsoring random Account <<< + }); + + await usingPlaygrounds(async (helper) => { + await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, SENDER_BUDGET); + }); + }); + + itSub('Should connect and send UNQ to Moonbeam', async () => { + await testHelper.sendUnqTo('moonbeam', randomAccountUnique, randomAccountMoonbeam); + }); + + itSub('Should connect to Moonbeam and send UNQ back', async () => { + await testHelper.sendUnqBack('moonbeam', alice, randomAccountUnique); + }); + + itSub('Moonbeam can send only up to its balance', async () => { + await testHelper.sendOnlyOwnedBalance('moonbeam', alice); + }); + + itSub('Should not accept reserve transfer of UNQ from Moonbeam', async () => { + await testHelper.rejectReserveTransferUNQfrom('moonbeam', alice); + }); +}); + +describeXCM('[XCMLL] Integration test: Exchanging tokens with Astar', () => { + let alice: IKeyringPair; + let randomAccount: IKeyringPair; + + const UNQ_ASSET_ID_ON_ASTAR = 18_446_744_073_709_551_631n; // The value is taken from the live Astar + const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n; // The value is taken from the live Astar + + // Unique -> Astar + const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar. + const unitsPerSecond = 9_451_000_000_000_000_000n; // The value is taken from the live Astar + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + randomAccount = helper.arrange.createEmptyAccount(); + await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET); + console.log('randomAccount', randomAccount.address); + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingAstarPlaygrounds(astarUrl, async (helper) => { + if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) { + console.log('1. Create foreign asset and metadata'); + await helper.getSudo().assets.forceCreate( + alice, + UNQ_ASSET_ID_ON_ASTAR, + alice.address, + UNQ_MINIMAL_BALANCE_ON_ASTAR, + ); + + await helper.assets.setMetadata( + alice, + UNQ_ASSET_ID_ON_ASTAR, + 'Unique Network', + 'UNQ', + Number(UNQ_DECIMALS), + ); + + console.log('2. Register asset location on Astar'); + const assetLocation = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }, + }; + + await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]); + + console.log('3. Set UNQ payment for XCM execution on Astar'); + await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]); + } else { + console.log('UNQ is already registered on Astar'); + } + console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)'); + await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance); + }); + }); + + itSub('Should connect and send UNQ to Astar', async () => { + await testHelper.sendUnqTo('astar', randomAccount); + }); + + itSub('Should connect to Astar and send UNQ back', async () => { + await testHelper.sendUnqBack('astar', alice, randomAccount); + }); + + itSub('Astar can send only up to its balance', async () => { + await testHelper.sendOnlyOwnedBalance('astar', alice); + }); + + itSub('Should not accept reserve transfer of UNQ from Astar', async () => { + await testHelper.rejectReserveTransferUNQfrom('astar', alice); + }); +}); + +describeXCM('[XCMLL] Integration test: The relay can do some root ops', () => { + let sudoer: IKeyringPair; + + before(async function () { + await usingRelayPlaygrounds(relayUrl, async (_, privateKey) => { + sudoer = await privateKey('//Alice'); + }); + }); + + // At the moment there is no reliable way + // to establish the correspondence between the `ExecutedDownward` event + // and the relay's sent message due to `SetTopic` instruction + // containing an unpredictable topic silently added by the relay's messages on the router level. + // This changes the message hash on arrival to our chain. + // + // See: + // * The relay's router: https://github.com/paritytech/polkadot-sdk/blob/f60318f68687e601c47de5ad5ca88e2c3f8139a7/polkadot/runtime/westend/src/xcm_config.rs#L83 + // * The `WithUniqueTopic` helper: https://github.com/paritytech/polkadot-sdk/blob/945ebbbcf66646be13d5b1d1bc26c8b0d3296d9e/polkadot/xcm/xcm-builder/src/routing.rs#L36 + // + // Because of this, we insert time gaps between tests so + // different `ExecutedDownward` events won't interfere with each other. + afterEach(async () => { + await usingPlaygrounds(async (helper) => { + await helper.wait.newBlocks(3); + }); + }); + + itSub('The relay can set storage', async () => { + await testHelper.relayIsPermittedToSetStorage(sudoer, 'plain'); + }); + + itSub('The relay can batch set storage', async () => { + await testHelper.relayIsPermittedToSetStorage(sudoer, 'batch'); + }); + + itSub('The relay can batchAll set storage', async () => { + await testHelper.relayIsPermittedToSetStorage(sudoer, 'batchAll'); + }); + + itSub('The relay can forceBatch set storage', async () => { + await testHelper.relayIsPermittedToSetStorage(sudoer, 'forceBatch'); + }); + + itSub('[negative] The relay cannot set balance', async () => { + await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'plain'); + }); + + itSub('[negative] The relay cannot set balance via batch', async () => { + await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batch'); + }); + + itSub('[negative] The relay cannot set balance via batchAll', async () => { + await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batchAll'); + }); + + itSub('[negative] The relay cannot set balance via forceBatch', async () => { + await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'forceBatch'); + }); + + itSub('[negative] The relay cannot set balance via dispatchAs', async () => { + await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'dispatchAs'); + }); +}); --- /dev/null +++ b/js-packages/tests/xcm/xcm.types.ts @@ -0,0 +1,621 @@ +import type {IKeyringPair} from '@polkadot/types/types'; +import {hexToString} from '@polkadot/util'; +import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '../util/index.js'; +import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js'; +import config from '../config.js'; + +export const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037); +export const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000); +export const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000); +export const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004); +export const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006); +export const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040); + +export const QUARTZ_CHAIN = +(process.env.RELAY_QUARTZ_ID || 2095); +export const STATEMINE_CHAIN = +(process.env.RELAY_STATEMINE_ID || 1000); +export const KARURA_CHAIN = +(process.env.RELAY_KARURA_ID || 2000); +export const MOONRIVER_CHAIN = +(process.env.RELAY_MOONRIVER_ID || 2023); +export const SHIDEN_CHAIN = +(process.env.RELAY_SHIDEN_ID || 2007); + +export const relayUrl = config.relayUrl; +export const statemintUrl = config.statemintUrl; +export const statemineUrl = config.statemineUrl; + +export const acalaUrl = config.acalaUrl; +export const moonbeamUrl = config.moonbeamUrl; +export const astarUrl = config.astarUrl; +export const polkadexUrl = config.polkadexUrl; + +export const karuraUrl = config.karuraUrl; +export const moonriverUrl = config.moonriverUrl; +export const shidenUrl = config.shidenUrl; + +export const SAFE_XCM_VERSION = 3; + + +export const RELAY_DECIMALS = 12; +export const STATEMINE_DECIMALS = 12; +export const KARURA_DECIMALS = 12; +export const SHIDEN_DECIMALS = 18n; +export const QTZ_DECIMALS = 18n; + +export const ASTAR_DECIMALS = 18n; +export const UNQ_DECIMALS = 18n; + +export const maxWaitBlocks = 6; + +export const uniqueMultilocation = { + parents: 1, + interior: { + X1: { + Parachain: UNIQUE_CHAIN, + }, + }, +}; +export const uniqueVersionedMultilocation = { + V3: uniqueMultilocation, +}; + +export const uniqueAssetId = { + Concrete: uniqueMultilocation, +}; + +export const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => { + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash + && event.outcome.isFailedToTransactAsset); +}; +export const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => { + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash + && event.outcome.isUntrustedReserveLocation); +}; + +export const expectDownwardXcmNoPermission = async (helper: DevUniqueHelper) => { + // The correct messageHash for downward messages can't be reliably obtained + await helper.wait.expectEvent(maxWaitBlocks, Event.DmpQueue.ExecutedDownward, event => event.outcome.asIncomplete[1].isNoPermission); +}; + +export const expectDownwardXcmComplete = async (helper: DevUniqueHelper) => { + // The correct messageHash for downward messages can't be reliably obtained + await helper.wait.expectEvent(maxWaitBlocks, Event.DmpQueue.ExecutedDownward, event => event.outcome.isComplete); +}; + +export const NETWORKS = { + acala: usingAcalaPlaygrounds, + astar: usingAstarPlaygrounds, + polkadex: usingPolkadexPlaygrounds, + moonbeam: usingMoonbeamPlaygrounds, + moonriver: usingMoonriverPlaygrounds, + karura: usingKaruraPlaygrounds, + shiden: usingShidenPlaygrounds, +} as const; +type NetworkNames = keyof typeof NETWORKS; + +type NativeRuntime = 'opal' | 'quartz' | 'unique'; + +export function mapToChainId(networkName: keyof typeof NETWORKS): number { + switch (networkName) { + case 'acala': + return ACALA_CHAIN; + case 'astar': + return ASTAR_CHAIN; + case 'moonbeam': + return MOONBEAM_CHAIN; + case 'polkadex': + return POLKADEX_CHAIN; + case 'moonriver': + return MOONRIVER_CHAIN; + case 'karura': + return KARURA_CHAIN; + case 'shiden': + return SHIDEN_CHAIN; + } +} + +export function mapToChainUrl(networkName: NetworkNames): string { + switch (networkName) { + case 'acala': + return acalaUrl; + case 'astar': + return astarUrl; + case 'moonbeam': + return moonbeamUrl; + case 'polkadex': + return polkadexUrl; + case 'moonriver': + return moonriverUrl; + case 'karura': + return karuraUrl; + case 'shiden': + return shidenUrl; + } +} + +export function getDevPlayground(name: NetworkNames) { + return NETWORKS[name]; +} + +export const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n; +export const SENDER_BUDGET = 2n * TRANSFER_AMOUNT; +export const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n; +export const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT; +export const TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT = 100_000_000_000n; + +export class XcmTestHelper { + private _balanceUniqueTokenInit: bigint = 0n; + private _balanceUniqueTokenMiddle: bigint = 0n; + private _balanceUniqueTokenFinal: bigint = 0n; + private _unqFees: bigint = 0n; + private _nativeRuntime: NativeRuntime; + + constructor(runtime: NativeRuntime) { + this._nativeRuntime = runtime; + } + + private _getNativeId() { + switch (this._nativeRuntime) { + case 'opal': + // To-Do + return 1001; + case 'quartz': + return QUARTZ_CHAIN; + case 'unique': + return UNIQUE_CHAIN; + } + } + + private _isAddress20FormatFor(network: NetworkNames) { + switch (network) { + case 'moonbeam': + case 'moonriver': + return true; + default: + return false; + } + } + + private _runtimeVersionedMultilocation() { + return { + V3: { + parents: 1, + interior: { + X1: { + Parachain: this._getNativeId(), + }, + }, + }, + }; + } + + private _uniqueChainMultilocationForRelay() { + return { + V3: { + parents: 0, + interior: { + X1: {Parachain: this._getNativeId()}, + }, + }, + }; + } + + async sendUnqTo( + networkName: keyof typeof NETWORKS, + randomAccount: IKeyringPair, + randomAccountOnTargetChain = randomAccount, + ) { + const networkUrl = mapToChainUrl(networkName); + const targetPlayground = getDevPlayground(networkName); + await usingPlaygrounds(async (helper) => { + this._balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address); + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: mapToChainId(networkName), + }, + }, + }, + }; + + const beneficiary = { + V2: { + parents: 0, + interior: { + X1: ( + this._isAddress20FormatFor(networkName) ? + { + AccountKey20: { + network: 'Any', + key: randomAccountOnTargetChain.address, + }, + } + : + { + AccountId32: { + network: 'Any', + id: randomAccountOnTargetChain.addressRaw, + }, + } + ), + }, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + ], + }; + const feeAssetItem = 0; + + await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + this._balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); + + this._unqFees = this._balanceUniqueTokenInit - this._balanceUniqueTokenMiddle - TRANSFER_AMOUNT; + console.log('[%s -> %s] transaction fees: %s', this._nativeRuntime, networkName, helper.util.bigIntToDecimals(this._unqFees)); + expect(this._unqFees > 0n, 'Negative fees, looks like nothing was transferred').to.be.true; + + await targetPlayground(networkUrl, async (helper) => { + /* + Since only the parachain part of the Polkadex + infrastructure is launched (without their + solochain validators), processing incoming + assets will lead to an error. + This error indicates that the Polkadex chain + received a message from the Unique network, + since the hash is being checked to ensure + it matches what was sent. + */ + if(networkName == 'polkadex') { + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash); + } else { + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == messageSent.messageHash); + } + }); + + }); + } + + async sendUnqBack( + networkName: keyof typeof NETWORKS, + sudoer: IKeyringPair, + randomAccountOnUnq: IKeyringPair, + ) { + const networkUrl = mapToChainUrl(networkName); + + const targetPlayground = getDevPlayground(networkName); + await usingPlaygrounds(async (helper) => { + + const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + randomAccountOnUnq.addressRaw, + { + Concrete: { + parents: 1, + interior: { + X1: {Parachain: this._getNativeId()}, + }, + }, + }, + SENDBACK_AMOUNT, + ); + + let xcmProgramSent: any; + + + await targetPlayground(networkUrl, async (helper) => { + if('getSudo' in helper) { + await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), xcmProgram); + xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + } else if('fastDemocracy' in helper) { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), xcmProgram]); + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall); + xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + } + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash); + + this._balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountOnUnq.address); + + expect(this._balanceUniqueTokenFinal).to.be.equal(this._balanceUniqueTokenInit - this._unqFees - STAYED_ON_TARGET_CHAIN); + + }); + } + + async sendOnlyOwnedBalance( + networkName: keyof typeof NETWORKS, + sudoer: IKeyringPair, + ) { + const networkUrl = mapToChainUrl(networkName); + const targetPlayground = getDevPlayground(networkName); + + const targetChainBalance = 10000n * (10n ** UNQ_DECIMALS); + + await usingPlaygrounds(async (helper) => { + const targetChainSovereignAccount = helper.address.paraSiblingSovereignAccount(mapToChainId(networkName)); + await helper.getSudo().balance.setBalanceSubstrate(sudoer, targetChainSovereignAccount, targetChainBalance); + const moreThanTargetChainHas = 2n * targetChainBalance; + + const targetAccount = helper.arrange.createEmptyAccount(); + + const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + moreThanTargetChainHas, + ); + + let maliciousXcmProgramSent: any; + + + await targetPlayground(networkUrl, async (helper) => { + if('getSudo' in helper) { + await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgram); + maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + } else if('fastDemocracy' in helper) { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgram]); + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall); + maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + } + }); + + await expectFailedToTransact(helper, maliciousXcmProgramSent); + + const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(0n); + }); + } + + async rejectReserveTransferUNQfrom(networkName: keyof typeof NETWORKS, sudoer: IKeyringPair) { + const networkUrl = mapToChainUrl(networkName); + const targetPlayground = getDevPlayground(networkName); + + await usingPlaygrounds(async (helper) => { + const testAmount = 10_000n * (10n ** UNQ_DECIMALS); + const targetAccount = helper.arrange.createEmptyAccount(); + + const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 1, + interior: { + X1: { + Parachain: this._getNativeId(), + }, + }, + }, + }, + testAmount, + ); + + const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + testAmount, + ); + + let maliciousXcmProgramFullIdSent: any; + let maliciousXcmProgramHereIdSent: any; + const maxWaitBlocks = 3; + + // Try to trick Unique using full UNQ identification + await targetPlayground(networkUrl, async (helper) => { + if('getSudo' in helper) { + await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId); + maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + } + // Moonbeam case + else if('fastDemocracy' in helper) { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]); + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal(`${networkName} try to act like a reserve location for UNQ using path asset identification`,batchCall); + + maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + } + }); + + + await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent); + + let accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + + // Try to trick Unique using shortened UNQ identification + await targetPlayground(networkUrl, async (helper) => { + if('getSudo' in helper) { + await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId); + maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + } + else if('fastDemocracy' in helper) { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId]); + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal(`${networkName} try to act like a reserve location for UNQ using "here" asset identification`, batchCall); + + maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + } + }); + + await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent); + + accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + }); + } + + async rejectNativeTokensFrom(networkName: keyof typeof NETWORKS, sudoerOnTargetChain: IKeyringPair) { + const networkUrl = mapToChainUrl(networkName); + const targetPlayground = getDevPlayground(networkName); + let messageSent: any; + + await usingPlaygrounds(async (helper) => { + const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( + helper.arrange.createEmptyAccount().addressRaw, + { + Concrete: { + parents: 1, + interior: { + X1: { + Parachain: mapToChainId(networkName), + }, + }, + }, + }, + TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT, + ); + await targetPlayground(networkUrl, async (helper) => { + if('getSudo' in helper) { + await helper.getSudo().xcm.send(sudoerOnTargetChain, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId); + messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + } else if('fastDemocracy' in helper) { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]); + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal(`${networkName} sending native tokens to the Unique via fast democracy`, batchCall); + + messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + } + }); + await expectFailedToTransact(helper, messageSent); + }); + } + + private async _relayXcmTransactSetStorage(variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch') { + // eslint-disable-next-line require-await + return await usingPlaygrounds(async (helper) => { + const relayForceKV = () => { + const random = Math.random(); + const key = `relay-forced-key (instance: ${random})`; + const val = `relay-forced-value (instance: ${random})`; + const call = helper.constructApiCall('api.tx.system.setStorage', [[[key, val]]]).method.toHex(); + + return { + call, + key, + val, + }; + }; + + if(variant == 'plain') { + const kv = relayForceKV(); + return { + program: helper.arrange.makeUnpaidSudoTransactProgram({ + weightMultiplier: 1, + call: kv.call, + }), + kvs: [kv], + }; + } else { + const kv0 = relayForceKV(); + const kv1 = relayForceKV(); + + const batchCall = helper.constructApiCall(`api.tx.utility.${variant}`, [[kv0.call, kv1.call]]).method.toHex(); + return { + program: helper.arrange.makeUnpaidSudoTransactProgram({ + weightMultiplier: 2, + call: batchCall, + }), + kvs: [kv0, kv1], + }; + } + }); + } + + async relayIsPermittedToSetStorage(relaySudoer: IKeyringPair, variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch') { + const {program, kvs} = await this._relayXcmTransactSetStorage(variant); + + await usingRelayPlaygrounds(relayUrl, async (helper) => { + await helper.getSudo().executeExtrinsic(relaySudoer, 'api.tx.xcmPallet.send', [ + this._uniqueChainMultilocationForRelay(), + program, + ]); + }); + + await usingPlaygrounds(async (helper) => { + await expectDownwardXcmComplete(helper); + + for(const kv of kvs) { + const forcedValue = await helper.callRpc('api.rpc.state.getStorage', [kv.key]); + expect(hexToString(forcedValue.toHex())).to.be.equal(kv.val); + } + }); + } + + private async _relayXcmTransactSetBalance(variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch' | 'dispatchAs') { + // eslint-disable-next-line require-await + return await usingPlaygrounds(async (helper) => { + const emptyAccount = helper.arrange.createEmptyAccount().address; + + const forceSetBalanceCall = helper.constructApiCall('api.tx.balances.forceSetBalance', [emptyAccount, 10_000n]).method.toHex(); + + let call; + + if(variant == 'plain') { + call = forceSetBalanceCall; + + } else if(variant == 'dispatchAs') { + call = helper.constructApiCall('api.tx.utility.dispatchAs', [ + { + system: 'Root', + }, + forceSetBalanceCall, + ]).method.toHex(); + } else { + call = helper.constructApiCall(`api.tx.utility.${variant}`, [[forceSetBalanceCall]]).method.toHex(); + } + + return { + program: helper.arrange.makeUnpaidSudoTransactProgram({ + weightMultiplier: 1, + call, + }), + emptyAccount, + }; + }); + } + + async relayIsNotPermittedToSetBalance( + relaySudoer: IKeyringPair, + variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch' | 'dispatchAs', + ) { + const {program, emptyAccount} = await this._relayXcmTransactSetBalance(variant); + + await usingRelayPlaygrounds(relayUrl, async (helper) => { + await helper.getSudo().executeExtrinsic(relaySudoer, 'api.tx.xcmPallet.send', [ + this._uniqueChainMultilocationForRelay(), + program, + ]); + }); + + await usingPlaygrounds(async (helper) => { + await expectDownwardXcmNoPermission(helper); + expect(await helper.balance.getSubstrate(emptyAccount)).to.be.equal(0n); + }); + } +} --- /dev/null +++ b/js-packages/tests/xcm/xcmOpal.test.ts @@ -0,0 +1,409 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import config from '../config.js'; +import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/index.js'; + +const STATEMINE_CHAIN = +(process.env.RELAY_WESTMINT_ID || 1000); +const UNIQUE_CHAIN = +(process.env.RELAY_OPAL_ID || 2095); + +const relayUrl = config.relayUrl; +const westmintUrl = config.westmintUrl; + +const STATEMINE_PALLET_INSTANCE = 50; +const ASSET_ID = 100; +const ASSET_METADATA_DECIMALS = 18; +const ASSET_METADATA_NAME = 'USDT'; +const ASSET_METADATA_DESCRIPTION = 'USDT'; +const ASSET_METADATA_MINIMAL_BALANCE = 1n; + +const RELAY_DECIMALS = 12; +const WESTMINT_DECIMALS = 12; + +const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n; + +// 10,000.00 (ten thousands) USDT +const ASSET_AMOUNT = 1_000_000_000_000_000_000_000n; + +describeXCM('[XCM] Integration test: Exchanging USDT with Westmint', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + let balanceStmnBefore: bigint; + let balanceStmnAfter: bigint; + + let balanceOpalBefore: bigint; + let balanceOpalAfter: bigint; + let balanceOpalFinal: bigint; + + let balanceBobBefore: bigint; + let balanceBobAfter: bigint; + let balanceBobFinal: bigint; + + let balanceBobRelayTokenBefore: bigint; + let balanceBobRelayTokenAfter: bigint; + + + before(async () => { + await usingPlaygrounds(async (_helper, privateKey) => { + alice = await privateKey('//Alice'); + bob = await privateKey('//Bob'); // funds donor + }); + + await usingWestmintPlaygrounds(westmintUrl, async (helper) => { + // 350.00 (three hundred fifty) DOT + const fundingAmount = 3_500_000_000_000n; + + await helper.assets.create(alice, ASSET_ID, alice.address, ASSET_METADATA_MINIMAL_BALANCE); + await helper.assets.setMetadata(alice, ASSET_ID, ASSET_METADATA_NAME, ASSET_METADATA_DESCRIPTION, ASSET_METADATA_DECIMALS); + await helper.assets.mint(alice, ASSET_ID, alice.address, ASSET_AMOUNT); + + // funding parachain sovereing account (Parachain: 2095) + const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN); + await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, fundingAmount); + }); + + + await usingPlaygrounds(async (helper) => { + const location = { + V2: { + parents: 1, + interior: {X3: [ + { + Parachain: STATEMINE_CHAIN, + }, + { + PalletInstance: STATEMINE_PALLET_INSTANCE, + }, + { + GeneralIndex: ASSET_ID, + }, + ]}, + }, + }; + + const metadata = + { + name: ASSET_ID, + symbol: ASSET_METADATA_NAME, + decimals: ASSET_METADATA_DECIMALS, + minimalBalance: ASSET_METADATA_MINIMAL_BALANCE, + }; + await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata); + balanceOpalBefore = await helper.balance.getSubstrate(alice.address); + }); + + + // Providing the relay currency to the unique sender account + await usingRelayPlaygrounds(relayUrl, async (helper) => { + const destination = { + V2: { + parents: 0, + interior: {X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }}; + + const beneficiary = { + V2: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }}, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: 50_000_000_000_000_000n, + }, + }, + ], + }; + + const feeAssetItem = 0; + + await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + }); + + }); + + itSub('Should connect and send USDT from Westmint to Opal', async ({helper}) => { + await usingWestmintPlaygrounds(westmintUrl, async (helper) => { + const dest = { + V2: { + parents: 1, + interior: {X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }}; + + const beneficiary = { + V2: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }}, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: { + X2: [ + { + PalletInstance: STATEMINE_PALLET_INSTANCE, + }, + { + GeneralIndex: ASSET_ID, + }, + ]}, + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + ], + }; + + const feeAssetItem = 0; + + balanceStmnBefore = await helper.balance.getSubstrate(alice.address); + await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited'); + + balanceStmnAfter = await helper.balance.getSubstrate(alice.address); + + // common good parachain take commission in it native token + console.log( + '[Westmint -> Opal] transaction fees on Westmint: %s WND', + helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS), + ); + expect(balanceStmnBefore > balanceStmnAfter).to.be.true; + + }); + + + // ensure that asset has been delivered + await helper.wait.newBlocks(3); + + // expext collection id will be with id 1 + const free = await helper.ft.getBalance(1, {Substrate: alice.address}); + + balanceOpalAfter = await helper.balance.getSubstrate(alice.address); + + console.log( + '[Westmint -> Opal] transaction fees on Opal: %s USDT', + helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, ASSET_METADATA_DECIMALS), + ); + console.log( + '[Westmint -> Opal] transaction fees on Opal: %s OPL', + helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore), + ); + + // commission has not paid in USDT token + expect(free == TRANSFER_AMOUNT).to.be.true; + // ... and parachain native token + expect(balanceOpalAfter == balanceOpalBefore).to.be.true; + }); + + itSub('Should connect and send USDT from Unique to Statemine back', async ({helper}) => { + const destination = { + V2: { + parents: 1, + interior: {X2: [ + { + Parachain: STATEMINE_CHAIN, + }, + { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }, + ]}, + }, + }; + + const currencies: [any, bigint][] = [ + [ + { + ForeignAssetId: 0, + }, + //10_000_000_000_000_000n, + TRANSFER_AMOUNT, + ], + [ + { + NativeAssetId: 'Parent', + }, + 400_000_000_000_000n, + ], + ]; + + const feeItem = 1; + + await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited'); + + // the commission has been paid in parachain native token + balanceOpalFinal = await helper.balance.getSubstrate(alice.address); + expect(balanceOpalAfter > balanceOpalFinal).to.be.true; + + await usingWestmintPlaygrounds(westmintUrl, async (helper) => { + await helper.wait.newBlocks(3); + + // The USDT token never paid fees. Its amount not changed from begin value. + // Also check that xcm transfer has been succeeded + expect((await helper.assets.account(ASSET_ID, alice.address))! == ASSET_AMOUNT).to.be.true; + }); + }); + + itSub('Should connect and send Relay token to Unique', async ({helper}) => { + const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n; + + balanceBobBefore = await helper.balance.getSubstrate(bob.address); + balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); + + // Providing the relay currency to the unique sender account + await usingRelayPlaygrounds(relayUrl, async (helper) => { + const destination = { + V2: { + parents: 0, + interior: {X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }}; + + const beneficiary = { + V2: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: bob.addressRaw, + }, + }}, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT_RELAY, + }, + }, + ], + }; + + const feeAssetItem = 0; + + await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + }); + + await helper.wait.newBlocks(3); + + balanceBobAfter = await helper.balance.getSubstrate(bob.address); + balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); + + const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; + console.log( + 'Relay (Westend) to Opal transaction fees: %s OPL', + helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore), + ); + console.log( + 'Relay (Westend) to Opal transaction fees: %s WND', + helper.util.bigIntToDecimals(wndFee, WESTMINT_DECIMALS), + ); + expect(balanceBobBefore == balanceBobAfter).to.be.true; + expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true; + }); + + itSub('Should connect and send Relay token back', async ({helper}) => { + let relayTokenBalanceBefore: bigint; + let relayTokenBalanceAfter: bigint; + await usingRelayPlaygrounds(relayUrl, async (helper) => { + relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address); + }); + + const destination = { + V2: { + parents: 1, + interior: { + X1:{ + AccountId32: { + network: 'Any', + id: bob.addressRaw, + }, + }, + }, + }, + }; + + const currencies: any = [ + [ + { + NativeAssetId: 'Parent', + }, + 50_000_000_000_000_000n, + ], + ]; + + const feeItem = 0; + + await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited'); + + balanceBobFinal = await helper.balance.getSubstrate(bob.address); + console.log('[Opal -> Relay (Westend)] transaction fees: %s OPL', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal)); + + await usingRelayPlaygrounds(relayUrl, async (helper) => { + await helper.wait.newBlocks(10); + relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address); + + const diff = relayTokenBalanceAfter - relayTokenBalanceBefore; + console.log('[Opal -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS)); + expect(diff > 0, 'Relay tokens was not delivered back').to.be.true; + }); + }); +}); --- /dev/null +++ b/js-packages/tests/xcm/xcmQuartz.test.ts @@ -0,0 +1,1636 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util/index.js'; +import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js'; +import {STATEMINE_CHAIN, QUARTZ_CHAIN, KARURA_CHAIN, MOONRIVER_CHAIN, SHIDEN_CHAIN, STATEMINE_DECIMALS, KARURA_DECIMALS, QTZ_DECIMALS, RELAY_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, relayUrl, shidenUrl, statemineUrl} from './xcm.types.js'; +import {hexToString} from '@polkadot/util'; + + + +const STATEMINE_PALLET_INSTANCE = 50; + +const TRANSFER_AMOUNT = 2000000000000000000000000n; + +const FUNDING_AMOUNT = 3_500_000_0000_000_000n; + +const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n; + +const USDT_ASSET_ID = 100; +const USDT_ASSET_METADATA_DECIMALS = 18; +const USDT_ASSET_METADATA_NAME = 'USDT'; +const USDT_ASSET_METADATA_DESCRIPTION = 'USDT'; +const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n; +const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n; + +const SAFE_XCM_VERSION = 2; + +describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + let balanceStmnBefore: bigint; + let balanceStmnAfter: bigint; + + let balanceQuartzBefore: bigint; + let balanceQuartzAfter: bigint; + let balanceQuartzFinal: bigint; + + let balanceBobBefore: bigint; + let balanceBobAfter: bigint; + let balanceBobFinal: bigint; + + let balanceBobRelayTokenBefore: bigint; + let balanceBobRelayTokenAfter: bigint; + + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + bob = await privateKey('//Bob'); // sovereign account on Statemine(t) funds donor + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingRelayPlaygrounds(relayUrl, async (helper) => { + // Fund accounts on Statemine(t) + await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, alice.addressRaw, FUNDING_AMOUNT); + await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, bob.addressRaw, FUNDING_AMOUNT); + }); + + await usingStateminePlaygrounds(statemineUrl, async (helper) => { + const sovereignFundingAmount = 3_500_000_000n; + + await helper.assets.create( + alice, + USDT_ASSET_ID, + alice.address, + USDT_ASSET_METADATA_MINIMAL_BALANCE, + ); + await helper.assets.setMetadata( + alice, + USDT_ASSET_ID, + USDT_ASSET_METADATA_NAME, + USDT_ASSET_METADATA_DESCRIPTION, + USDT_ASSET_METADATA_DECIMALS, + ); + await helper.assets.mint( + alice, + USDT_ASSET_ID, + alice.address, + USDT_ASSET_AMOUNT, + ); + + // funding parachain sovereing account on Statemine(t). + // The sovereign account should be created before any action + // (the assets pallet on Statemine(t) check if the sovereign account exists) + const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(QUARTZ_CHAIN); + await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount); + }); + + + await usingPlaygrounds(async (helper) => { + const location = { + V2: { + parents: 1, + interior: {X3: [ + { + Parachain: STATEMINE_CHAIN, + }, + { + PalletInstance: STATEMINE_PALLET_INSTANCE, + }, + { + GeneralIndex: USDT_ASSET_ID, + }, + ]}, + }, + }; + + const metadata = + { + name: USDT_ASSET_ID, + symbol: USDT_ASSET_METADATA_NAME, + decimals: USDT_ASSET_METADATA_DECIMALS, + minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE, + }; + await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata); + balanceQuartzBefore = await helper.balance.getSubstrate(alice.address); + }); + + + // Providing the relay currency to the quartz sender account + // (fee for USDT XCM are paid in relay tokens) + await usingRelayPlaygrounds(relayUrl, async (helper) => { + const destination = { + V2: { + parents: 0, + interior: {X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }}; + + const beneficiary = { + V2: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }}, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT_RELAY, + }, + }, + ], + }; + + const feeAssetItem = 0; + + await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + }); + + }); + + itSub('Should connect and send USDT from Statemine to Quartz', async ({helper}) => { + await usingStateminePlaygrounds(statemineUrl, async (helper) => { + const dest = { + V2: { + parents: 1, + interior: {X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }}; + + const beneficiary = { + V2: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }}, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: { + X2: [ + { + PalletInstance: STATEMINE_PALLET_INSTANCE, + }, + { + GeneralIndex: USDT_ASSET_ID, + }, + ]}, + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + ], + }; + + const feeAssetItem = 0; + + balanceStmnBefore = await helper.balance.getSubstrate(alice.address); + await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited'); + + balanceStmnAfter = await helper.balance.getSubstrate(alice.address); + + // common good parachain take commission in it native token + console.log( + '[Statemine -> Quartz] transaction fees on Statemine: %s WND', + helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINE_DECIMALS), + ); + expect(balanceStmnBefore > balanceStmnAfter).to.be.true; + + }); + + + // ensure that asset has been delivered + await helper.wait.newBlocks(3); + + // expext collection id will be with id 1 + const free = await helper.ft.getBalance(1, {Substrate: alice.address}); + + balanceQuartzAfter = await helper.balance.getSubstrate(alice.address); + + console.log( + '[Statemine -> Quartz] transaction fees on Quartz: %s USDT', + helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS), + ); + console.log( + '[Statemine -> Quartz] transaction fees on Quartz: %s QTZ', + helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzBefore), + ); + // commission has not paid in USDT token + expect(free).to.be.equal(TRANSFER_AMOUNT); + // ... and parachain native token + expect(balanceQuartzAfter == balanceQuartzBefore).to.be.true; + }); + + itSub('Should connect and send USDT from Quartz to Statemine back', async ({helper}) => { + const destination = { + V2: { + parents: 1, + interior: {X2: [ + { + Parachain: STATEMINE_CHAIN, + }, + { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }, + ]}, + }, + }; + + const relayFee = 400_000_000_000_000n; + const currencies: [any, bigint][] = [ + [ + { + ForeignAssetId: 0, + }, + TRANSFER_AMOUNT, + ], + [ + { + NativeAssetId: 'Parent', + }, + relayFee, + ], + ]; + + const feeItem = 1; + + await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited'); + + // the commission has been paid in parachain native token + balanceQuartzFinal = await helper.balance.getSubstrate(alice.address); + console.log('[Quartz -> Statemine] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzFinal)); + expect(balanceQuartzAfter > balanceQuartzFinal).to.be.true; + + await usingStateminePlaygrounds(statemineUrl, async (helper) => { + await helper.wait.newBlocks(3); + + // The USDT token never paid fees. Its amount not changed from begin value. + // Also check that xcm transfer has been succeeded + expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true; + }); + }); + + itSub('Should connect and send Relay token to Quartz', async ({helper}) => { + balanceBobBefore = await helper.balance.getSubstrate(bob.address); + balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); + + await usingRelayPlaygrounds(relayUrl, async (helper) => { + const destination = { + V2: { + parents: 0, + interior: {X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }}; + + const beneficiary = { + V2: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: bob.addressRaw, + }, + }}, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT_RELAY, + }, + }, + ], + }; + + const feeAssetItem = 0; + + await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + }); + + await helper.wait.newBlocks(3); + + balanceBobAfter = await helper.balance.getSubstrate(bob.address); + balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); + + const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; + const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore; + console.log( + '[Relay (Westend) -> Quartz] transaction fees: %s QTZ', + helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore), + ); + console.log( + '[Relay (Westend) -> Quartz] transaction fees: %s WND', + helper.util.bigIntToDecimals(wndFeeOnQuartz, STATEMINE_DECIMALS), + ); + console.log('[Relay (Westend) -> Quartz] actually delivered: %s WND', wndDiffOnQuartz); + expect(wndFeeOnQuartz == 0n, 'No incoming WND fees should be taken').to.be.true; + expect(balanceBobBefore == balanceBobAfter, 'No incoming QTZ fees should be taken').to.be.true; + }); + + itSub('Should connect and send Relay token back', async ({helper}) => { + let relayTokenBalanceBefore: bigint; + let relayTokenBalanceAfter: bigint; + await usingRelayPlaygrounds(relayUrl, async (helper) => { + relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address); + }); + + const destination = { + V2: { + parents: 1, + interior: { + X1:{ + AccountId32: { + network: 'Any', + id: bob.addressRaw, + }, + }, + }, + }, + }; + + const currencies: any = [ + [ + { + NativeAssetId: 'Parent', + }, + TRANSFER_AMOUNT_RELAY, + ], + ]; + + const feeItem = 0; + + await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited'); + + balanceBobFinal = await helper.balance.getSubstrate(bob.address); + console.log('[Quartz -> Relay (Westend)] transaction fees: %s QTZ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal)); + + await usingRelayPlaygrounds(relayUrl, async (helper) => { + await helper.wait.newBlocks(10); + relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address); + + const diff = relayTokenBalanceAfter - relayTokenBalanceBefore; + console.log('[Quartz -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS)); + expect(diff > 0, 'Relay tokens was not delivered back').to.be.true; + }); + }); +}); + +describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => { + let alice: IKeyringPair; + let randomAccount: IKeyringPair; + + let balanceQuartzTokenInit: bigint; + let balanceQuartzTokenMiddle: bigint; + let balanceQuartzTokenFinal: bigint; + let balanceKaruraTokenInit: bigint; + let balanceKaruraTokenMiddle: bigint; + let balanceKaruraTokenFinal: bigint; + let balanceQuartzForeignTokenInit: bigint; + let balanceQuartzForeignTokenMiddle: bigint; + let balanceQuartzForeignTokenFinal: bigint; + + // computed by a test transfer from prod Quartz to prod Karura. + // 2 QTZ sent https://quartz.subscan.io/xcm_message/kusama-f60d821b049f8835a3005ce7102285006f5b61e9 + // 1.919176000000000000 QTZ received (you can check Karura's chain state in the corresponding block) + const expectedKaruraIncomeFee = 2000000000000000000n - 1919176000000000000n; + const karuraEps = 8n * 10n ** 16n; + + let karuraBackwardTransferAmount: bigint; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + [randomAccount] = await helper.arrange.createAccounts([0n], alice); + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingKaruraPlaygrounds(karuraUrl, async (helper) => { + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }; + + const metadata = { + name: 'Quartz', + symbol: 'QTZ', + decimals: 18, + minimalBalance: 1000000000000000000n, + }; + + const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) => + hexToString(v.toJSON()['symbol'])) as string[]; + + if(!assets.includes('QTZ')) { + await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata); + } else { + console.log('QTZ token already registered on Karura assetRegistry pallet'); + } + await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); + balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address); + balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0}); + }); + + await usingPlaygrounds(async (helper) => { + await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT); + balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address); + }); + }); + + itSub('Should connect and send QTZ to Karura', async ({helper}) => { + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: KARURA_CHAIN, + }, + }, + }, + }; + + const beneficiary = { + V2: { + parents: 0, + interior: { + X1: { + AccountId32: { + network: 'Any', + id: randomAccount.addressRaw, + }, + }, + }, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + ], + }; + + const feeAssetItem = 0; + + await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); + + const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT; + expect(qtzFees > 0n, 'Negative fees QTZ, looks like nothing was transferred').to.be.true; + console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees)); + + await usingKaruraPlaygrounds(karuraUrl, async (helper) => { + await helper.wait.newBlocks(3); + + balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0}); + balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); + + const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle; + const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit; + karuraBackwardTransferAmount = qtzIncomeTransfer; + + const karUnqFees = TRANSFER_AMOUNT - qtzIncomeTransfer; + + console.log( + '[Quartz -> Karura] transaction fees on Karura: %s KAR', + helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS), + ); + console.log( + '[Quartz -> Karura] transaction fees on Karura: %s QTZ', + helper.util.bigIntToDecimals(karUnqFees), + ); + console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer)); + expect(karFees == 0n).to.be.true; + + const bigintAbs = (n: bigint) => (n < 0n) ? -n : n; + + expect( + bigintAbs(karUnqFees - expectedKaruraIncomeFee) < karuraEps, + 'Karura took different income fee, check the Karura foreign asset config', + ).to.be.true; + }); + }); + + itSub('Should connect to Karura and send QTZ back', async ({helper}) => { + await usingKaruraPlaygrounds(karuraUrl, async (helper) => { + const destination = { + V2: { + parents: 1, + interior: { + X2: [ + {Parachain: QUARTZ_CHAIN}, + { + AccountId32: { + network: 'Any', + id: randomAccount.addressRaw, + }, + }, + ], + }, + }, + }; + + const id = { + ForeignAsset: 0, + }; + + await helper.xTokens.transfer(randomAccount, id, karuraBackwardTransferAmount, destination, 'Unlimited'); + balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address); + balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id); + + const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal; + const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal; + + console.log( + '[Karura -> Quartz] transaction fees on Karura: %s KAR', + helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS), + ); + console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer)); + + expect(karFees > 0, 'Negative fees KAR, looks like nothing was transferred').to.be.true; + expect(qtzOutcomeTransfer == karuraBackwardTransferAmount).to.be.true; + }); + + await helper.wait.newBlocks(3); + + balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address); + const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle; + expect(actuallyDelivered > 0).to.be.true; + + console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered)); + + const qtzFees = karuraBackwardTransferAmount - actuallyDelivered; + console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees)); + expect(qtzFees == 0n).to.be.true; + }); + + itSub('Karura can send only up to its balance', async ({helper}) => { + // set Karura's sovereign account's balance + const karuraBalance = 10000n * (10n ** QTZ_DECIMALS); + const karuraSovereignAccount = helper.address.paraSiblingSovereignAccount(KARURA_CHAIN); + await helper.getSudo().balance.setBalanceSubstrate(alice, karuraSovereignAccount, karuraBalance); + + const moreThanKaruraHas = karuraBalance * 2n; + + let targetAccountBalance = 0n; + const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); + + const quartzMultilocation = { + V2: { + parents: 1, + interior: { + X1: {Parachain: QUARTZ_CHAIN}, + }, + }, + }; + + const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + moreThanKaruraHas, + ); + + let maliciousXcmProgramSent: any; + const maxWaitBlocks = 5; + + // Try to trick Quartz + await usingKaruraPlaygrounds(karuraUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram); + + maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash + && event.outcome.isFailedToTransactAsset); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(0n); + + // But Karura still can send the correct amount + const validTransferAmount = karuraBalance / 2n; + const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + validTransferAmount, + ); + + await usingKaruraPlaygrounds(karuraUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram); + }); + + await helper.wait.newBlocks(maxWaitBlocks); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(validTransferAmount); + }); + + itSub('Should not accept reserve transfer of QTZ from Karura', async ({helper}) => { + const testAmount = 10_000n * (10n ** QTZ_DECIMALS); + const [targetAccount] = await helper.arrange.createAccounts([0n], alice); + + const quartzMultilocation = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }; + + const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 1, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }, + testAmount, + ); + + const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + testAmount, + ); + + let maliciousXcmProgramFullIdSent: any; + let maliciousXcmProgramHereIdSent: any; + const maxWaitBlocks = 3; + + // Try to trick Quartz using full QTZ identification + await usingKaruraPlaygrounds(karuraUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId); + + maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + let accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + + // Try to trick Quartz using shortened QTZ identification + await usingKaruraPlaygrounds(karuraUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId); + + maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + }); +}); + +// These tests are relevant only when +// the the corresponding foreign assets are not registered +describeXCM('[XCM] Integration test: Quartz rejects non-native tokens', () => { + let alice: IKeyringPair; + let alith: IKeyringPair; + + const testAmount = 100_000_000_000n; + let quartzParachainJunction; + let quartzAccountJunction; + + let quartzParachainMultilocation: any; + let quartzAccountMultilocation: any; + let quartzCombinedMultilocation: any; + + let messageSent: any; + + const maxWaitBlocks = 3; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + + quartzParachainJunction = {Parachain: QUARTZ_CHAIN}; + quartzAccountJunction = { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }; + + quartzParachainMultilocation = { + V2: { + parents: 1, + interior: { + X1: quartzParachainJunction, + }, + }, + }; + + quartzAccountMultilocation = { + V2: { + parents: 0, + interior: { + X1: quartzAccountJunction, + }, + }, + }; + + quartzCombinedMultilocation = { + V2: { + parents: 1, + interior: { + X2: [quartzParachainJunction, quartzAccountJunction], + }, + }, + }; + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + // eslint-disable-next-line require-await + await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { + alith = helper.account.alithAccount(); + }); + }); + + const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => { + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash + && event.outcome.isFailedToTransactAsset); + }; + + itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => { + await usingKaruraPlaygrounds(karuraUrl, async (helper) => { + const id = { + Token: 'KAR', + }; + const destination = quartzCombinedMultilocation; + await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited'); + + messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await expectFailedToTransact(helper, messageSent); + }); + + itSub('Quartz rejects MOVR tokens from Moonriver', async ({helper}) => { + await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { + const id = 'SelfReserve'; + const destination = quartzCombinedMultilocation; + await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited'); + + messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await expectFailedToTransact(helper, messageSent); + }); + + itSub('Quartz rejects SDN tokens from Shiden', async ({helper}) => { + await usingShidenPlaygrounds(shidenUrl, async (helper) => { + const destinationParachain = quartzParachainMultilocation; + const beneficiary = quartzAccountMultilocation; + const assets = { + V2: [{ + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: testAmount, + }, + }], + }; + const feeAssetItem = 0; + + await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [ + destinationParachain, + beneficiary, + assets, + feeAssetItem, + ]); + + messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await expectFailedToTransact(helper, messageSent); + }); +}); + +describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => { + // Quartz constants + let alice: IKeyringPair; + let quartzAssetLocation; + + let randomAccountQuartz: IKeyringPair; + let randomAccountMoonriver: IKeyringPair; + + // Moonriver constants + let assetId: string; + + const quartzAssetMetadata = { + name: 'xcQuartz', + symbol: 'xcQTZ', + decimals: 18, + isFrozen: false, + minimalBalance: 1n, + }; + + let balanceQuartzTokenInit: bigint; + let balanceQuartzTokenMiddle: bigint; + let balanceQuartzTokenFinal: bigint; + let balanceForeignQtzTokenInit: bigint; + let balanceForeignQtzTokenMiddle: bigint; + let balanceForeignQtzTokenFinal: bigint; + let balanceMovrTokenInit: bigint; + let balanceMovrTokenMiddle: bigint; + let balanceMovrTokenFinal: bigint; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice); + + balanceForeignQtzTokenInit = 0n; + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { + const alithAccount = helper.account.alithAccount(); + const baltatharAccount = helper.account.baltatharAccount(); + const dorothyAccount = helper.account.dorothyAccount(); + + randomAccountMoonriver = helper.account.create(); + + // >>> Sponsoring Dorothy >>> + console.log('Sponsoring Dorothy.......'); + await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n); + console.log('Sponsoring Dorothy.......DONE'); + // <<< Sponsoring Dorothy <<< + + quartzAssetLocation = { + XCM: { + parents: 1, + interior: {X1: {Parachain: QUARTZ_CHAIN}}, + }, + }; + const existentialDeposit = 1n; + const isSufficient = true; + const unitsPerSecond = 1n; + const numAssetsWeightHint = 0; + + if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) { + console.log('Quartz asset already registered on Moonriver'); + } else { + const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({ + location: quartzAssetLocation, + metadata: quartzAssetMetadata, + existentialDeposit, + isSufficient, + unitsPerSecond, + numAssetsWeightHint, + }); + + console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal); + + await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal); + } + // >>> Acquire Quartz AssetId Info on Moonriver >>> + console.log('Acquire Quartz AssetId Info on Moonriver.......'); + + assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString(); + + console.log('QTZ asset ID is %s', assetId); + console.log('Acquire Quartz AssetId Info on Moonriver.......DONE'); + // >>> Acquire Quartz AssetId Info on Moonriver >>> + + // >>> Sponsoring random Account >>> + console.log('Sponsoring random Account.......'); + await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n); + console.log('Sponsoring random Account.......DONE'); + // <<< Sponsoring random Account <<< + + balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address); + }); + + await usingPlaygrounds(async (helper) => { + await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT); + balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address); + }); + }); + + itSub('Should connect and send QTZ to Moonriver', async ({helper}) => { + const currencyId = { + NativeAssetId: 'Here', + }; + const dest = { + V2: { + parents: 1, + interior: { + X2: [ + {Parachain: MOONRIVER_CHAIN}, + {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}}, + ], + }, + }, + }; + const amount = TRANSFER_AMOUNT; + + await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, 'Unlimited'); + + balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address); + expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true; + + const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT; + console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees)); + expect(transactionFees > 0, 'Negative fees QTZ, looks like nothing was transferred').to.be.true; + + await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { + await helper.wait.newBlocks(3); + + balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address); + + const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle; + console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',helper.util.bigIntToDecimals(movrFees)); + expect(movrFees == 0n).to.be.true; + + balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; // BigInt(qtzRandomAccountAsset['balance']); + const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit; + console.log('[Quartz -> Moonriver] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer)); + expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true; + }); + }); + + itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => { + await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { + const asset = { + V2: { + id: { + Concrete: { + parents: 1, + interior: { + X1: {Parachain: QUARTZ_CHAIN}, + }, + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + }; + const destination = { + V2: { + parents: 1, + interior: { + X2: [ + {Parachain: QUARTZ_CHAIN}, + {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}}, + ], + }, + }, + }; + + await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, 'Unlimited'); + + balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address); + + const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal; + console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees)); + expect(movrFees > 0, 'Negative fees MOVR, looks like nothing was transferred').to.be.true; + + const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address); + + expect(qtzRandomAccountAsset).to.be.null; + + balanceForeignQtzTokenFinal = 0n; + + const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal; + console.log('[Quartz -> Moonriver] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer)); + expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true; + }); + + await helper.wait.newBlocks(3); + + balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address); + const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle; + expect(actuallyDelivered > 0).to.be.true; + + console.log('[Moonriver -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered)); + + const qtzFees = TRANSFER_AMOUNT - actuallyDelivered; + console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees)); + expect(qtzFees == 0n).to.be.true; + }); + + itSub('Moonriver can send only up to its balance', async ({helper}) => { + // set Moonriver's sovereign account's balance + const moonriverBalance = 10000n * (10n ** QTZ_DECIMALS); + const moonriverSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONRIVER_CHAIN); + await helper.getSudo().balance.setBalanceSubstrate(alice, moonriverSovereignAccount, moonriverBalance); + + const moreThanMoonriverHas = moonriverBalance * 2n; + + let targetAccountBalance = 0n; + const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); + + const quartzMultilocation = { + V2: { + parents: 1, + interior: { + X1: {Parachain: QUARTZ_CHAIN}, + }, + }, + }; + + const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + moreThanMoonriverHas, + ); + + let maliciousXcmProgramSent: any; + const maxWaitBlocks = 3; + + // Try to trick Quartz + await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgram]); + + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal('try to spend more QTZ than Moonriver has', batchCall); + + maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash + && event.outcome.isFailedToTransactAsset); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(0n); + + // But Moonriver still can send the correct amount + const validTransferAmount = moonriverBalance / 2n; + const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + validTransferAmount, + ); + + await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, validXcmProgram]); + + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal('Spend the correct amount of QTZ', batchCall); + }); + + await helper.wait.newBlocks(maxWaitBlocks); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(validTransferAmount); + }); + + itSub('Should not accept reserve transfer of QTZ from Moonriver', async ({helper}) => { + const testAmount = 10_000n * (10n ** QTZ_DECIMALS); + const [targetAccount] = await helper.arrange.createAccounts([0n], alice); + + const quartzMultilocation = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }; + + const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }, + testAmount, + ); + + const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + testAmount, + ); + + let maliciousXcmProgramFullIdSent: any; + let maliciousXcmProgramHereIdSent: any; + const maxWaitBlocks = 3; + + // Try to trick Quartz using full QTZ identification + await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramFullId]); + + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using path asset identification', batchCall); + + maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + let accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + + // Try to trick Quartz using shortened QTZ identification + await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramHereId]); + + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using "here" asset identification', batchCall); + + maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + }); +}); + +describeXCM('[XCM] Integration test: Exchanging tokens with Shiden', () => { + let alice: IKeyringPair; + let sender: IKeyringPair; + + const QTZ_ASSET_ID_ON_SHIDEN = 18_446_744_073_709_551_633n; // The value is taken from the live Shiden + const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n; // The value is taken from the live Shiden + + // Quartz -> Shiden + const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden + const unitsPerSecond = 500_451_000_000_000_000_000n; // The value is taken from the live Shiden + const qtzToShidenTransferred = 10n * (10n ** QTZ_DECIMALS); // 10 QTZ + const qtzToShidenArrived = 7_998_196_000_000_000_000n; // 7.99 ... QTZ, Shiden takes a commision in foreign tokens + + // Shiden -> Quartz + const qtzFromShidenTransfered = 5n * (10n ** QTZ_DECIMALS); // 5 QTZ + const qtzOnShidenLeft = qtzToShidenArrived - qtzFromShidenTransfered; // 2.99 ... QTZ + + let balanceAfterQuartzToShidenXCM: bigint; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + [sender] = await helper.arrange.createAccounts([100n], alice); + console.log('sender', sender.address); + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingShidenPlaygrounds(shidenUrl, async (helper) => { + if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) { + console.log('1. Create foreign asset and metadata'); + await helper.getSudo().assets.forceCreate( + alice, + QTZ_ASSET_ID_ON_SHIDEN, + alice.address, + QTZ_MINIMAL_BALANCE_ON_SHIDEN, + ); + + await helper.assets.setMetadata( + alice, + QTZ_ASSET_ID_ON_SHIDEN, + 'Quartz', + 'QTZ', + Number(QTZ_DECIMALS), + ); + + console.log('2. Register asset location on Shiden'); + const assetLocation = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }; + + await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]); + + console.log('3. Set QTZ payment for XCM execution on Shiden'); + await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]); + } else { + console.log('QTZ is already registered on Shiden'); + } + console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)'); + await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance); + }); + }); + + itSub('Should connect and send QTZ to Shiden', async ({helper}) => { + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: SHIDEN_CHAIN, + }, + }, + }, + }; + + const beneficiary = { + V2: { + parents: 0, + interior: { + X1: { + AccountId32: { + network: 'Any', + id: sender.addressRaw, + }, + }, + }, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: qtzToShidenTransferred, + }, + }, + ], + }; + + // Initial balance is 100 QTZ + const balanceBefore = await helper.balance.getSubstrate(sender.address); + console.log(`Initial balance is: ${balanceBefore}`); + + const feeAssetItem = 0; + await helper.xcm.limitedReserveTransferAssets(sender, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + + // Balance after reserve transfer is less than 90 + balanceAfterQuartzToShidenXCM = await helper.balance.getSubstrate(sender.address); + console.log(`QTZ Balance on Quartz after XCM is: ${balanceAfterQuartzToShidenXCM}`); + console.log(`Quartz's QTZ commission is: ${balanceBefore - balanceAfterQuartzToShidenXCM}`); + expect(balanceBefore - balanceAfterQuartzToShidenXCM > 0).to.be.true; + + await usingShidenPlaygrounds(shidenUrl, async (helper) => { + await helper.wait.newBlocks(3); + const xcQTZbalance = await helper.assets.account(QTZ_ASSET_ID_ON_SHIDEN, sender.address); + const shidenBalance = await helper.balance.getSubstrate(sender.address); + + console.log(`xcQTZ balance on Shiden after XCM is: ${xcQTZbalance}`); + console.log(`Shiden's QTZ commission is: ${qtzToShidenTransferred - xcQTZbalance!}`); + + expect(xcQTZbalance).to.eq(qtzToShidenArrived); + // SHD balance does not changed: + expect(shidenBalance).to.eq(shidenInitialBalance); + }); + }); + + itSub('Should connect to Shiden and send QTZ back', async ({helper}) => { + await usingShidenPlaygrounds(shidenUrl, async (helper) => { + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }; + + const beneficiary = { + V2: { + parents: 0, + interior: { + X1: { + AccountId32: { + network: 'Any', + id: sender.addressRaw, + }, + }, + }, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 1, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }, + fun: { + Fungible: qtzFromShidenTransfered, + }, + }, + ], + }; + + // Initial balance is 1 SDN + const balanceSDNbefore = await helper.balance.getSubstrate(sender.address); + console.log(`SDN balance is: ${balanceSDNbefore}, it does not changed`); + expect(balanceSDNbefore).to.eq(shidenInitialBalance); + + const feeAssetItem = 0; + // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw + await helper.executeExtrinsic(sender, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]); + + // Balance after reserve transfer is less than 1 SDN + const xcQTZbalance = await helper.assets.account(QTZ_ASSET_ID_ON_SHIDEN, sender.address); + const balanceSDN = await helper.balance.getSubstrate(sender.address); + console.log(`xcQTZ balance on Shiden after XCM is: ${xcQTZbalance}`); + + // Assert: xcQTZ balance correctly decreased + expect(xcQTZbalance).to.eq(qtzOnShidenLeft); + // Assert: SDN balance is 0.996... + expect(balanceSDN / (10n ** (SHIDEN_DECIMALS - 3n))).to.eq(996n); + }); + + await helper.wait.newBlocks(3); + const balanceQTZ = await helper.balance.getSubstrate(sender.address); + console.log(`QTZ Balance on Quartz after XCM is: ${balanceQTZ}`); + expect(balanceQTZ).to.eq(balanceAfterQuartzToShidenXCM + qtzFromShidenTransfered); + }); + + itSub('Shiden can send only up to its balance', async ({helper}) => { + // set Shiden's sovereign account's balance + const shidenBalance = 10000n * (10n ** QTZ_DECIMALS); + const shidenSovereignAccount = helper.address.paraSiblingSovereignAccount(SHIDEN_CHAIN); + await helper.getSudo().balance.setBalanceSubstrate(alice, shidenSovereignAccount, shidenBalance); + + const moreThanShidenHas = shidenBalance * 2n; + + let targetAccountBalance = 0n; + const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); + + const quartzMultilocation = { + V2: { + parents: 1, + interior: { + X1: {Parachain: QUARTZ_CHAIN}, + }, + }, + }; + + const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + moreThanShidenHas, + ); + + let maliciousXcmProgramSent: any; + const maxWaitBlocks = 3; + + // Try to trick Quartz + await usingShidenPlaygrounds(shidenUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram); + + maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash + && event.outcome.isFailedToTransactAsset); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(0n); + + // But Shiden still can send the correct amount + const validTransferAmount = shidenBalance / 2n; + const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + validTransferAmount, + ); + + await usingShidenPlaygrounds(shidenUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram); + }); + + await helper.wait.newBlocks(maxWaitBlocks); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(validTransferAmount); + }); + + itSub('Should not accept reserve transfer of QTZ from Shiden', async ({helper}) => { + const testAmount = 10_000n * (10n ** QTZ_DECIMALS); + const [targetAccount] = await helper.arrange.createAccounts([0n], alice); + + const quartzMultilocation = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }; + + const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 1, + interior: { + X1: { + Parachain: QUARTZ_CHAIN, + }, + }, + }, + }, + testAmount, + ); + + const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + testAmount, + ); + + let maliciousXcmProgramFullIdSent: any; + let maliciousXcmProgramHereIdSent: any; + const maxWaitBlocks = 3; + + // Try to trick Quartz using full QTZ identification + await usingShidenPlaygrounds(shidenUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId); + + maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + let accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + + // Try to trick Quartz using shortened QTZ identification + await usingShidenPlaygrounds(shidenUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId); + + maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + }); +}); --- /dev/null +++ b/js-packages/tests/xcm/xcmUnique.test.ts @@ -0,0 +1,1834 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import type {IKeyringPair} from '@polkadot/types/types'; +import config from '../config.js'; +import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util/index.js'; +import {Event} from '@unique/playgrounds/unique.dev.js'; +import {hexToString, nToBigInt} from '@polkadot/util'; +import {ACALA_CHAIN, ASTAR_CHAIN, MOONBEAM_CHAIN, POLKADEX_CHAIN, SAFE_XCM_VERSION, STATEMINT_CHAIN, UNIQUE_CHAIN, expectFailedToTransact, expectUntrustedReserveLocationFail, uniqueAssetId, uniqueVersionedMultilocation} from './xcm.types.js'; + + +const STATEMINT_PALLET_INSTANCE = 50; + +const relayUrl = config.relayUrl; +const statemintUrl = config.statemintUrl; +const acalaUrl = config.acalaUrl; +const moonbeamUrl = config.moonbeamUrl; +const astarUrl = config.astarUrl; +const polkadexUrl = config.polkadexUrl; + +const RELAY_DECIMALS = 12; +const STATEMINT_DECIMALS = 12; +const ACALA_DECIMALS = 12; +const ASTAR_DECIMALS = 18n; +const UNQ_DECIMALS = 18n; + +const TRANSFER_AMOUNT = 2000000000000000000000000n; + +const FUNDING_AMOUNT = 3_500_000_0000_000_000n; + +const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n; + +const USDT_ASSET_ID = 100; +const USDT_ASSET_METADATA_DECIMALS = 18; +const USDT_ASSET_METADATA_NAME = 'USDT'; +const USDT_ASSET_METADATA_DESCRIPTION = 'USDT'; +const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n; +const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n; + +describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + let balanceStmnBefore: bigint; + let balanceStmnAfter: bigint; + + let balanceUniqueBefore: bigint; + let balanceUniqueAfter: bigint; + let balanceUniqueFinal: bigint; + + let balanceBobBefore: bigint; + let balanceBobAfter: bigint; + let balanceBobFinal: bigint; + + let balanceBobRelayTokenBefore: bigint; + let balanceBobRelayTokenAfter: bigint; + + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingRelayPlaygrounds(relayUrl, async (helper) => { + // Fund accounts on Statemint + await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT); + await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT); + }); + + await usingStatemintPlaygrounds(statemintUrl, async (helper) => { + const sovereignFundingAmount = 3_500_000_000n; + + await helper.assets.create( + alice, + USDT_ASSET_ID, + alice.address, + USDT_ASSET_METADATA_MINIMAL_BALANCE, + ); + await helper.assets.setMetadata( + alice, + USDT_ASSET_ID, + USDT_ASSET_METADATA_NAME, + USDT_ASSET_METADATA_DESCRIPTION, + USDT_ASSET_METADATA_DECIMALS, + ); + await helper.assets.mint( + alice, + USDT_ASSET_ID, + alice.address, + USDT_ASSET_AMOUNT, + ); + + // funding parachain sovereing account on Statemint. + // The sovereign account should be created before any action + // (the assets pallet on Statemint check if the sovereign account exists) + const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN); + await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount); + }); + + + await usingPlaygrounds(async (helper) => { + const location = { + V2: { + parents: 1, + interior: {X3: [ + { + Parachain: STATEMINT_CHAIN, + }, + { + PalletInstance: STATEMINT_PALLET_INSTANCE, + }, + { + GeneralIndex: USDT_ASSET_ID, + }, + ]}, + }, + }; + + const metadata = + { + name: USDT_ASSET_ID, + symbol: USDT_ASSET_METADATA_NAME, + decimals: USDT_ASSET_METADATA_DECIMALS, + minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE, + }; + await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata); + balanceUniqueBefore = await helper.balance.getSubstrate(alice.address); + }); + + + // Providing the relay currency to the unique sender account + // (fee for USDT XCM are paid in relay tokens) + await usingRelayPlaygrounds(relayUrl, async (helper) => { + const destination = { + V2: { + parents: 0, + interior: {X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }}; + + const beneficiary = { + V2: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }}, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT_RELAY, + }, + }, + ], + }; + + const feeAssetItem = 0; + + await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + }); + + }); + + itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => { + await usingStatemintPlaygrounds(statemintUrl, async (helper) => { + const dest = { + V2: { + parents: 1, + interior: {X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }}; + + const beneficiary = { + V2: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }}, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: { + X2: [ + { + PalletInstance: STATEMINT_PALLET_INSTANCE, + }, + { + GeneralIndex: USDT_ASSET_ID, + }, + ]}, + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + ], + }; + + const feeAssetItem = 0; + + balanceStmnBefore = await helper.balance.getSubstrate(alice.address); + await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited'); + + balanceStmnAfter = await helper.balance.getSubstrate(alice.address); + + // common good parachain take commission in it native token + console.log( + '[Statemint -> Unique] transaction fees on Statemint: %s WND', + helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS), + ); + expect(balanceStmnBefore > balanceStmnAfter).to.be.true; + + }); + + + // ensure that asset has been delivered + await helper.wait.newBlocks(3); + + // expext collection id will be with id 1 + const free = await helper.ft.getBalance(1, {Substrate: alice.address}); + + balanceUniqueAfter = await helper.balance.getSubstrate(alice.address); + + console.log( + '[Statemint -> Unique] transaction fees on Unique: %s USDT', + helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS), + ); + console.log( + '[Statemint -> Unique] transaction fees on Unique: %s UNQ', + helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore), + ); + // commission has not paid in USDT token + expect(free).to.be.equal(TRANSFER_AMOUNT); + // ... and parachain native token + expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true; + }); + + itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => { + const destination = { + V2: { + parents: 1, + interior: {X2: [ + { + Parachain: STATEMINT_CHAIN, + }, + { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }, + ]}, + }, + }; + + const relayFee = 400_000_000_000_000n; + const currencies: [any, bigint][] = [ + [ + { + ForeignAssetId: 0, + }, + TRANSFER_AMOUNT, + ], + [ + { + NativeAssetId: 'Parent', + }, + relayFee, + ], + ]; + + const feeItem = 1; + + await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited'); + + // the commission has been paid in parachain native token + balanceUniqueFinal = await helper.balance.getSubstrate(alice.address); + console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal)); + expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true; + + await usingStatemintPlaygrounds(statemintUrl, async (helper) => { + await helper.wait.newBlocks(3); + + // The USDT token never paid fees. Its amount not changed from begin value. + // Also check that xcm transfer has been succeeded + expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true; + }); + }); + + itSub('Should connect and send Relay token to Unique', async ({helper}) => { + balanceBobBefore = await helper.balance.getSubstrate(bob.address); + balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); + + await usingRelayPlaygrounds(relayUrl, async (helper) => { + const destination = { + V2: { + parents: 0, + interior: {X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }}; + + const beneficiary = { + V2: { + parents: 0, + interior: {X1: { + AccountId32: { + network: 'Any', + id: bob.addressRaw, + }, + }}, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT_RELAY, + }, + }, + ], + }; + + const feeAssetItem = 0; + + await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + }); + + await helper.wait.newBlocks(3); + + balanceBobAfter = await helper.balance.getSubstrate(bob.address); + balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'}); + + const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; + const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore; + console.log( + '[Relay (Westend) -> Unique] transaction fees: %s UNQ', + helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore), + ); + console.log( + '[Relay (Westend) -> Unique] transaction fees: %s WND', + helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS), + ); + console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique); + expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true; + expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true; + }); + + itSub('Should connect and send Relay token back', async ({helper}) => { + let relayTokenBalanceBefore: bigint; + let relayTokenBalanceAfter: bigint; + await usingRelayPlaygrounds(relayUrl, async (helper) => { + relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address); + }); + + const destination = { + V2: { + parents: 1, + interior: { + X1:{ + AccountId32: { + network: 'Any', + id: bob.addressRaw, + }, + }, + }, + }, + }; + + const currencies: any = [ + [ + { + NativeAssetId: 'Parent', + }, + TRANSFER_AMOUNT_RELAY, + ], + ]; + + const feeItem = 0; + + await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited'); + + balanceBobFinal = await helper.balance.getSubstrate(bob.address); + console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal)); + + await usingRelayPlaygrounds(relayUrl, async (helper) => { + await helper.wait.newBlocks(10); + relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address); + + const diff = relayTokenBalanceAfter - relayTokenBalanceBefore; + console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS)); + expect(diff > 0, 'Relay tokens was not delivered back').to.be.true; + }); + }); +}); + +describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => { + let alice: IKeyringPair; + let randomAccount: IKeyringPair; + + let balanceUniqueTokenInit: bigint; + let balanceUniqueTokenMiddle: bigint; + let balanceUniqueTokenFinal: bigint; + let balanceAcalaTokenInit: bigint; + let balanceAcalaTokenMiddle: bigint; + let balanceAcalaTokenFinal: bigint; + let balanceUniqueForeignTokenInit: bigint; + let balanceUniqueForeignTokenMiddle: bigint; + let balanceUniqueForeignTokenFinal: bigint; + + // computed by a test transfer from prod Unique to prod Acala. + // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48 + // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block) + const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n; + const acalaEps = 8n * 10n ** 16n; + + let acalaBackwardTransferAmount: bigint; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + [randomAccount] = await helper.arrange.createAccounts([0n], alice); + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingAcalaPlaygrounds(acalaUrl, async (helper) => { + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }, + }; + + const metadata = { + name: 'Unique Network', + symbol: 'UNQ', + decimals: 18, + minimalBalance: 1250000000000000000n, + }; + const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) => + hexToString(v.toJSON()['symbol'])) as string[]; + + if(!assets.includes('UNQ')) { + await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata); + } else { + console.log('UNQ token already registered on Acala assetRegistry pallet'); + } + await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); + balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address); + balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0}); + }); + + await usingPlaygrounds(async (helper) => { + await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT); + balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address); + }); + }); + + itSub('Should connect and send UNQ to Acala', async ({helper}) => { + + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: ACALA_CHAIN, + }, + }, + }, + }; + + const beneficiary = { + V2: { + parents: 0, + interior: { + X1: { + AccountId32: { + network: 'Any', + id: randomAccount.addressRaw, + }, + }, + }, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + ], + }; + + const feeAssetItem = 0; + + await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); + + const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT; + console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees)); + expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true; + + await usingAcalaPlaygrounds(acalaUrl, async (helper) => { + await helper.wait.newBlocks(3); + + balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0}); + balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); + + const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle; + const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit; + acalaBackwardTransferAmount = unqIncomeTransfer; + + const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer; + + console.log( + '[Unique -> Acala] transaction fees on Acala: %s ACA', + helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS), + ); + console.log( + '[Unique -> Acala] transaction fees on Acala: %s UNQ', + helper.util.bigIntToDecimals(acaUnqFees), + ); + console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer)); + expect(acaFees == 0n).to.be.true; + + const bigintAbs = (n: bigint) => (n < 0n) ? -n : n; + + expect( + bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps, + 'Acala took different income fee, check the Acala foreign asset config', + ).to.be.true; + }); + }); + + itSub('Should connect to Acala and send UNQ back', async ({helper}) => { + await usingAcalaPlaygrounds(acalaUrl, async (helper) => { + const destination = { + V2: { + parents: 1, + interior: { + X2: [ + {Parachain: UNIQUE_CHAIN}, + { + AccountId32: { + network: 'Any', + id: randomAccount.addressRaw, + }, + }, + ], + }, + }, + }; + + const id = { + ForeignAsset: 0, + }; + + await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited'); + balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address); + balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id); + + const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal; + const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal; + + console.log( + '[Acala -> Unique] transaction fees on Acala: %s ACA', + helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS), + ); + console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer)); + + expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true; + expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true; + }); + + await helper.wait.newBlocks(3); + + balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address); + const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle; + expect(actuallyDelivered > 0).to.be.true; + + console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered)); + + const unqFees = acalaBackwardTransferAmount - actuallyDelivered; + console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees)); + expect(unqFees == 0n).to.be.true; + }); + + itSub('Acala can send only up to its balance', async ({helper}) => { + // set Acala's sovereign account's balance + const acalaBalance = 10000n * (10n ** UNQ_DECIMALS); + const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN); + await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance); + + const moreThanAcalaHas = acalaBalance * 2n; + + let targetAccountBalance = 0n; + const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); + + const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + moreThanAcalaHas, + ); + + let maliciousXcmProgramSent: any; + const maxWaitBlocks = 3; + + // Try to trick Unique + await usingAcalaPlaygrounds(acalaUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram); + + maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash + && event.outcome.isFailedToTransactAsset); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(0n); + + // But Acala still can send the correct amount + const validTransferAmount = acalaBalance / 2n; + const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + validTransferAmount, + ); + + await usingAcalaPlaygrounds(acalaUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, validXcmProgram); + }); + + await helper.wait.newBlocks(maxWaitBlocks); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(validTransferAmount); + }); + + itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => { + const testAmount = 10_000n * (10n ** UNQ_DECIMALS); + const [targetAccount] = await helper.arrange.createAccounts([0n], alice); + + const uniqueMultilocation = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }, + }; + + const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + uniqueAssetId, + testAmount, + ); + + const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + testAmount, + ); + + let maliciousXcmProgramFullIdSent: any; + let maliciousXcmProgramHereIdSent: any; + const maxWaitBlocks = 3; + + // Try to trick Unique using full UNQ identification + await usingAcalaPlaygrounds(acalaUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId); + + maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + let accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + + // Try to trick Unique using shortened UNQ identification + await usingAcalaPlaygrounds(acalaUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId); + + maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + }); +}); + +describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => { + let alice: IKeyringPair; + let randomAccount: IKeyringPair; + let unqFees: bigint; + let balanceUniqueTokenInit: bigint; + let balanceUniqueTokenMiddle: bigint; + let balanceUniqueTokenFinal: bigint; + const maxWaitBlocks = 6; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + [randomAccount] = await helper.arrange.createAccounts([0n], alice); + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { + const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', [])) + .toJSON() as []) + .map(nToBigInt).length != 0; + /* + Check whether the Unique token has been added + to the whitelist, since an error will occur + if it is added again. Needed for debugging + when this test is run multiple times. + */ + if(isWhitelisted) { + console.log('UNQ token is already whitelisted on Polkadex'); + } else { + await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId); + } + + await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n); + }); + + await usingPlaygrounds(async (helper) => { + await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT); + balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address); + }); + }); + + itSub('Should connect and send UNQ to Polkadex', async ({helper}) => { + + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: POLKADEX_CHAIN, + }, + }, + }, + }; + + const beneficiary = { + V2: { + parents: 0, + interior: { + X1: { + AccountId32: { + network: 'Any', + id: randomAccount.addressRaw, + }, + }, + }, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + ], + }; + + const feeAssetItem = 0; + + await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address); + + unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT; + console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees)); + expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true; + + await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { + /* + Since only the parachain part of the Polkadex + infrastructure is launched (without their + solochain validators), processing incoming + assets will lead to an error. + This error indicates that the Polkadex chain + received a message from the Unique network, + since the hash is being checked to ensure + it matches what was sent. + */ + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash); + }); + }); + + + itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => { + + const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + randomAccount.addressRaw, + uniqueAssetId, + TRANSFER_AMOUNT, + ); + + let xcmProgramSent: any; + + + await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, xcmProgram); + + xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash); + + balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address); + + expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees); + }); + + itSub('Polkadex can send only up to its balance', async ({helper}) => { + const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS); + const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN); + await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance); + const moreThanPolkadexHas = 2n * polkadexBalance; + + const targetAccount = helper.arrange.createEmptyAccount(); + + const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + moreThanPolkadexHas, + ); + + let maliciousXcmProgramSent: any; + + + await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram); + + maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await expectFailedToTransact(helper, maliciousXcmProgramSent); + + const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(0n); + }); + + itSub('Should not accept reserve transfer of UNQ from Polkadex', async ({helper}) => { + const testAmount = 10_000n * (10n ** UNQ_DECIMALS); + const targetAccount = helper.arrange.createEmptyAccount(); + + const uniqueMultilocation = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }, + }; + + const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + uniqueAssetId, + testAmount, + ); + + const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + testAmount, + ); + + let maliciousXcmProgramFullIdSent: any; + let maliciousXcmProgramHereIdSent: any; + const maxWaitBlocks = 3; + + // Try to trick Unique using full UNQ identification + await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId); + + maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent); + + let accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + + // Try to trick Unique using shortened UNQ identification + await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId); + + maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent); + + accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + }); +}); + +// These tests are relevant only when +// the the corresponding foreign assets are not registered +describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => { + let alice: IKeyringPair; + let alith: IKeyringPair; + + const testAmount = 100_000_000_000n; + let uniqueParachainJunction; + let uniqueAccountJunction; + + let uniqueParachainMultilocation: any; + let uniqueAccountMultilocation: any; + let uniqueCombinedMultilocation: any; + let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V2 + + let messageSent: any; + + const maxWaitBlocks = 3; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + + uniqueParachainJunction = {Parachain: UNIQUE_CHAIN}; + uniqueAccountJunction = { + AccountId32: { + network: 'Any', + id: alice.addressRaw, + }, + }; + + uniqueParachainMultilocation = { + V2: { + parents: 1, + interior: { + X1: uniqueParachainJunction, + }, + }, + }; + + uniqueAccountMultilocation = { + V2: { + parents: 0, + interior: { + X1: uniqueAccountJunction, + }, + }, + }; + + uniqueCombinedMultilocation = { + V2: { + parents: 1, + interior: { + X2: [uniqueParachainJunction, uniqueAccountJunction], + }, + }, + }; + + uniqueCombinedMultilocationAcala = { + V2: { + parents: 1, + interior: { + X2: [uniqueParachainJunction, uniqueAccountJunction], + }, + }, + }; + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + // eslint-disable-next-line require-await + await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { + alith = helper.account.alithAccount(); + }); + }); + + + + itSub('Unique rejects ACA tokens from Acala', async ({helper}) => { + await usingAcalaPlaygrounds(acalaUrl, async (helper) => { + const id = { + Token: 'ACA', + }; + const destination = uniqueCombinedMultilocationAcala; + await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited'); + + messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await expectFailedToTransact(helper, messageSent); + }); + + itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => { + await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { + const id = 'SelfReserve'; + const destination = uniqueCombinedMultilocation; + await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited'); + + messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await expectFailedToTransact(helper, messageSent); + }); + + itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => { + await usingAstarPlaygrounds(astarUrl, async (helper) => { + const destinationParachain = uniqueParachainMultilocation; + const beneficiary = uniqueAccountMultilocation; + const assets = { + V2: [{ + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: testAmount, + }, + }], + }; + const feeAssetItem = 0; + + await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [ + destinationParachain, + beneficiary, + assets, + feeAssetItem, + ]); + + messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await expectFailedToTransact(helper, messageSent); + }); + + itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => { + + const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( + helper.arrange.createEmptyAccount().addressRaw, + { + Concrete: { + parents: 1, + interior: { + X1: { + Parachain: POLKADEX_CHAIN, + }, + }, + }, + }, + testAmount, + ); + + await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId); + messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await expectFailedToTransact(helper, messageSent); + }); +}); + +describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => { + // Unique constants + let alice: IKeyringPair; + let uniqueAssetLocation; + + let randomAccountUnique: IKeyringPair; + let randomAccountMoonbeam: IKeyringPair; + + // Moonbeam constants + let assetId: string; + + const uniqueAssetMetadata = { + name: 'xcUnique', + symbol: 'xcUNQ', + decimals: 18, + isFrozen: false, + minimalBalance: 1n, + }; + + let balanceUniqueTokenInit: bigint; + let balanceUniqueTokenMiddle: bigint; + let balanceUniqueTokenFinal: bigint; + let balanceForeignUnqTokenInit: bigint; + let balanceForeignUnqTokenMiddle: bigint; + let balanceForeignUnqTokenFinal: bigint; + let balanceGlmrTokenInit: bigint; + let balanceGlmrTokenMiddle: bigint; + let balanceGlmrTokenFinal: bigint; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice); + + balanceForeignUnqTokenInit = 0n; + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { + const alithAccount = helper.account.alithAccount(); + const baltatharAccount = helper.account.baltatharAccount(); + const dorothyAccount = helper.account.dorothyAccount(); + + randomAccountMoonbeam = helper.account.create(); + + // >>> Sponsoring Dorothy >>> + console.log('Sponsoring Dorothy.......'); + await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n); + console.log('Sponsoring Dorothy.......DONE'); + // <<< Sponsoring Dorothy <<< + + uniqueAssetLocation = { + XCM: { + parents: 1, + interior: {X1: {Parachain: UNIQUE_CHAIN}}, + }, + }; + const existentialDeposit = 1n; + const isSufficient = true; + const unitsPerSecond = 1n; + const numAssetsWeightHint = 0; + + if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) { + console.log('Unique asset is already registered on MoonBeam'); + } else { + const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({ + location: uniqueAssetLocation, + metadata: uniqueAssetMetadata, + existentialDeposit, + isSufficient, + unitsPerSecond, + numAssetsWeightHint, + }); + + console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal); + + await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal); + } + + // >>> Acquire Unique AssetId Info on Moonbeam >>> + console.log('Acquire Unique AssetId Info on Moonbeam.......'); + + assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString(); + console.log('UNQ asset ID is %s', assetId); + console.log('Acquire Unique AssetId Info on Moonbeam.......DONE'); + // >>> Acquire Unique AssetId Info on Moonbeam >>> + + // >>> Sponsoring random Account >>> + console.log('Sponsoring random Account.......'); + await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n); + console.log('Sponsoring random Account.......DONE'); + // <<< Sponsoring random Account <<< + + balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address); + }); + + await usingPlaygrounds(async (helper) => { + await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT); + balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address); + }); + }); + + itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => { + const currencyId = { + NativeAssetId: 'Here', + }; + const dest = { + V2: { + parents: 1, + interior: { + X2: [ + {Parachain: MOONBEAM_CHAIN}, + {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}}, + ], + }, + }, + }; + const amount = TRANSFER_AMOUNT; + + await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited'); + + balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address); + expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true; + + const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT; + console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees)); + expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true; + + await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { + await helper.wait.newBlocks(3); + balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address); + + const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle; + console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees)); + expect(glmrFees == 0n).to.be.true; + + balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!; + + const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit; + console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer)); + expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true; + }); + }); + + itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => { + await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { + const asset = { + V2: { + id: { + Concrete: { + parents: 1, + interior: { + X1: {Parachain: UNIQUE_CHAIN}, + }, + }, + }, + fun: { + Fungible: TRANSFER_AMOUNT, + }, + }, + }; + const destination = { + V2: { + parents: 1, + interior: { + X2: [ + {Parachain: UNIQUE_CHAIN}, + {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}}, + ], + }, + }, + }; + + await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited'); + + balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address); + + const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal; + console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees)); + expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true; + + const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address); + + expect(unqRandomAccountAsset).to.be.null; + + balanceForeignUnqTokenFinal = 0n; + + const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal; + console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer)); + expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true; + }); + + await helper.wait.newBlocks(3); + + balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address); + const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle; + expect(actuallyDelivered > 0).to.be.true; + + console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered)); + + const unqFees = TRANSFER_AMOUNT - actuallyDelivered; + console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees)); + expect(unqFees == 0n).to.be.true; + }); + + itSub('Moonbeam can send only up to its balance', async ({helper}) => { + // set Moonbeam's sovereign account's balance + const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS); + const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN); + await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance); + + const moreThanMoonbeamHas = moonbeamBalance * 2n; + + let targetAccountBalance = 0n; + const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); + + const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + moreThanMoonbeamHas, + ); + + let maliciousXcmProgramSent: any; + const maxWaitBlocks = 3; + + // Try to trick Unique + await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgram]); + + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall); + + maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash + && event.outcome.isFailedToTransactAsset); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(0n); + + // But Moonbeam still can send the correct amount + const validTransferAmount = moonbeamBalance / 2n; + const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + validTransferAmount, + ); + + await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, validXcmProgram]); + + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall); + }); + + await helper.wait.newBlocks(maxWaitBlocks); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(validTransferAmount); + }); + + itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => { + const testAmount = 10_000n * (10n ** UNQ_DECIMALS); + const [targetAccount] = await helper.arrange.createAccounts([0n], alice); + + const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + uniqueAssetId, + testAmount, + ); + + const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + testAmount, + ); + + let maliciousXcmProgramFullIdSent: any; + let maliciousXcmProgramHereIdSent: any; + const maxWaitBlocks = 3; + + // Try to trick Unique using full UNQ identification + await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]); + + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall); + + maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + let accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + + // Try to trick Unique using shortened UNQ identification + await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => { + const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramHereId]); + + // Needed to bypass the call filter. + const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]); + await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall); + + maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + }); +}); + +describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => { + let alice: IKeyringPair; + let randomAccount: IKeyringPair; + + const UNQ_ASSET_ID_ON_ASTAR = 18_446_744_073_709_551_631n; // The value is taken from the live Astar + const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n; // The value is taken from the live Astar + + // Unique -> Astar + const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar. + const unitsPerSecond = 9_451_000_000_000_000_000n; // The value is taken from the live Astar + const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ + const unqToAstarArrived = 9_962_196_000_000_000_000n; // 9.962 ... UNQ, Astar takes a commision in foreign tokens + + // Astar -> Unique + const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ + const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.962_219_600_000_000_000n UNQ + + let balanceAfterUniqueToAstarXCM: bigint; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + alice = await privateKey('//Alice'); + [randomAccount] = await helper.arrange.createAccounts([100n], alice); + console.log('randomAccount', randomAccount.address); + + // Set the default version to wrap the first message to other chains. + await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION); + }); + + await usingAstarPlaygrounds(astarUrl, async (helper) => { + if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) { + console.log('1. Create foreign asset and metadata'); + await helper.getSudo().assets.forceCreate( + alice, + UNQ_ASSET_ID_ON_ASTAR, + alice.address, + UNQ_MINIMAL_BALANCE_ON_ASTAR, + ); + + await helper.assets.setMetadata( + alice, + UNQ_ASSET_ID_ON_ASTAR, + 'Unique Network', + 'UNQ', + Number(UNQ_DECIMALS), + ); + + console.log('2. Register asset location on Astar'); + const assetLocation = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }, + }; + + await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]); + + console.log('3. Set UNQ payment for XCM execution on Astar'); + await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]); + } else { + console.log('UNQ is already registered on Astar'); + } + console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)'); + await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance); + }); + }); + + itSub('Should connect and send UNQ to Astar', async ({helper}) => { + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: ASTAR_CHAIN, + }, + }, + }, + }; + + const beneficiary = { + V2: { + parents: 0, + interior: { + X1: { + AccountId32: { + network: 'Any', + id: randomAccount.addressRaw, + }, + }, + }, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + fun: { + Fungible: unqToAstarTransferred, + }, + }, + ], + }; + + // Initial balance is 100 UNQ + const balanceBefore = await helper.balance.getSubstrate(randomAccount.address); + console.log(`Initial balance is: ${balanceBefore}`); + + const feeAssetItem = 0; + await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited'); + + // Balance after reserve transfer is less than 90 + balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address); + console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`); + console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`); + expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true; + + await usingAstarPlaygrounds(astarUrl, async (helper) => { + await helper.wait.newBlocks(3); + const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address); + const astarBalance = await helper.balance.getSubstrate(randomAccount.address); + + console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`); + console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`); + + expect(xcUNQbalance).to.eq(unqToAstarArrived); + // Astar balance does not changed + expect(astarBalance).to.eq(astarInitialBalance); + }); + }); + + itSub('Should connect to Astar and send UNQ back', async ({helper}) => { + await usingAstarPlaygrounds(astarUrl, async (helper) => { + const destination = { + V2: { + parents: 1, + interior: { + X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }, + }; + + const beneficiary = { + V2: { + parents: 0, + interior: { + X1: { + AccountId32: { + network: 'Any', + id: randomAccount.addressRaw, + }, + }, + }, + }, + }; + + const assets = { + V2: [ + { + id: { + Concrete: { + parents: 1, + interior: { + X1: { + Parachain: UNIQUE_CHAIN, + }, + }, + }, + }, + fun: { + Fungible: unqFromAstarTransfered, + }, + }, + ], + }; + + // Initial balance is 1 ASTR + const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address); + console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`); + expect(balanceASTRbefore).to.eq(astarInitialBalance); + + const feeAssetItem = 0; + // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw + await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]); + + const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address); + const balanceAstar = await helper.balance.getSubstrate(randomAccount.address); + console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`); + + // Assert: xcUNQ balance correctly decreased + expect(xcUNQbalance).to.eq(unqOnAstarLeft); + // Assert: ASTR balance is 0.996... + expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n); + }); + + await helper.wait.newBlocks(3); + const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address); + console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`); + expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered); + }); + + itSub('Astar can send only up to its balance', async ({helper}) => { + // set Astar's sovereign account's balance + const astarBalance = 10000n * (10n ** UNQ_DECIMALS); + const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN); + await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance); + + const moreThanAstarHas = astarBalance * 2n; + + let targetAccountBalance = 0n; + const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice); + + const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + moreThanAstarHas, + ); + + let maliciousXcmProgramSent: any; + const maxWaitBlocks = 3; + + // Try to trick Unique + await usingAstarPlaygrounds(astarUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram); + + maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash + && event.outcome.isFailedToTransactAsset); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(0n); + + // But Astar still can send the correct amount + const validTransferAmount = astarBalance / 2n; + const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + validTransferAmount, + ); + + await usingAstarPlaygrounds(astarUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, validXcmProgram); + }); + + await helper.wait.newBlocks(maxWaitBlocks); + + targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(targetAccountBalance).to.be.equal(validTransferAmount); + }); + + itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => { + const testAmount = 10_000n * (10n ** UNQ_DECIMALS); + const [targetAccount] = await helper.arrange.createAccounts([0n], alice); + + const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + uniqueAssetId, + testAmount, + ); + + const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited( + targetAccount.addressRaw, + { + Concrete: { + parents: 0, + interior: 'Here', + }, + }, + testAmount, + ); + + let maliciousXcmProgramFullIdSent: any; + let maliciousXcmProgramHereIdSent: any; + const maxWaitBlocks = 3; + + // Try to trick Unique using full UNQ identification + await usingAstarPlaygrounds(astarUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramFullId); + + maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + let accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + + // Try to trick Unique using shortened UNQ identification + await usingAstarPlaygrounds(astarUrl, async (helper) => { + await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramHereId); + + maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent); + }); + + await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash + && event.outcome.isUntrustedReserveLocation); + + accountBalance = await helper.balance.getSubstrate(targetAccount.address); + expect(accountBalance).to.be.equal(0n); + }); +}); --- a/js-packages/tsconfig.base.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - /** - * There uses the strictest configs as the base - * https://github.com/tsconfig/bases/blob/f674fa6cbca17062ff02511b02872f8729a597ec/bases/strictest.json - */ - "extends": "@tsconfig/strictest/tsconfig.json", - "compilerOptions": { - /** - * This needs to align with scripts/polkadot-dev-build-ts & dev-ts/loader - * (target here is specifically tied to the minimum supported version) - */ - "module": "nodenext", - "moduleResolution": "nodenext", - "target": "es2022", - - /** - * Specific compilation configs for polkadot-js projects as it is used - * (we only compile *.d.ts via the tsc command-line) - */ - "declaration": true, - "emitDeclarationOnly": true, - "jsx": "preserve", - "verbatimModuleSyntax": true, - - /** - * These appear in strictest, however we don't (yet) use them. For the most part it means - * that we actually do have a large number of these lurking (especially on index checks) - */ - "checkJs": false, - "exactOptionalPropertyTypes": false, - "noPropertyAccessFromIndexSignature": false, - "noUncheckedIndexedAccess": false, - } - } \ No newline at end of file --- a/js-packages/tsconfig.json +++ b/js-packages/tsconfig.json @@ -1,11 +1,15 @@ { - "references": [ - { "path": "./types/tsconfig.json" }, - { "path": "./playgrounds/tsconfig.json" }, - { "path": "./scripts/tsconfig.json" }, - { "path": "./tests/tsconfig.json" } + "exclude": [ + "**/node_modules" ], - "files": [], - "exclude": ["**/node_modules"] - } - \ No newline at end of file + "compilerOptions": { + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "target": "ES2022", + "declaration": true, + "checkJs": false, + "strict": true, + "resolveJsonModule": true, + }, +} --- a/js-packages/tsconfig.packages.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "skipLibCheck": true, - - /** - * This needs to align with scripts/polkadot-dev-build-ts & dev-ts/loader - * (target here is specifically tied to the minimum supported version) - */ - "module": "nodenext", - "moduleResolution": "nodenext", - "target": "ES2022", - - /** - * Specific compilation configs for polkadot-js projects as it is used - * (we only compile *.d.ts via the tsc command-line) - */ - "declaration": true, - "emitDeclarationOnly": true, - "jsx": "preserve", - "verbatimModuleSyntax": false, - - /** - * These appear in strictest, however we don't (yet) use them. For the most part it means - * that we actually do have a large number of these lurking (especially on index checks) - */ - "checkJs": false, - "exactOptionalPropertyTypes": false, - "noPropertyAccessFromIndexSignature": false, - "noUncheckedIndexedAccess": false, - }, -} \ No newline at end of file --- /dev/null +++ b/js-packages/types/.gitignore @@ -0,0 +1 @@ +metadata.json --- /dev/null +++ b/js-packages/types/appPromotion/definitions.ts @@ -0,0 +1,58 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +type RpcParam = { + name: string; + type: string; + isOptional?: true; +}; + +const CROSS_ACCOUNT_ID_TYPE = 'PalletEvmAccountBasicCrossAccountIdRepr'; + +const crossAccountParam = (name = 'account') => ({name, type: CROSS_ACCOUNT_ID_TYPE}); +const atParam = {name: 'at', type: 'Hash', isOptional: true}; + +const fun = (description: string, params: RpcParam[], type: string) => ({ + description, + params: [...params, atParam], + type, +}); + +export default { + types: {}, + rpc: { + totalStaked: fun( + 'Returns the total amount of staked tokens', + [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}], + 'u128', + ), + totalStakedPerBlock: fun( + 'Returns the total amount of staked tokens per block when staked', + [crossAccountParam('staker')], + 'Vec<(u32, u128)>', + ), + pendingUnstake: fun( + 'Returns the total amount of unstaked tokens', + [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}], + 'u128', + ), + pendingUnstakePerBlock: fun( + 'Returns the total amount of unstaked tokens per block', + [crossAccountParam('staker')], + 'Vec<(u32, u128)>', + ), + }, +}; --- /dev/null +++ b/js-packages/types/appPromotion/index.ts @@ -0,0 +1,4 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +export * from './types.js'; --- /dev/null +++ b/js-packages/types/appPromotion/types.ts @@ -0,0 +1,4 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +export type PHANTOM_APPPROMOTION = 'appPromotion'; --- /dev/null +++ b/js-packages/types/augment-api-consts.ts @@ -0,0 +1,552 @@ +// Auto-generated via `yarn polkadot-types-from-chain`, do not edit +/* eslint-disable */ + +// import type lookup before we augment - in some environments +// this is required to allow for ambient/previous definitions +import '@polkadot/api-base/types/consts'; + +import type { ApiTypes, AugmentedConst } from '@polkadot/api-base/types'; +import type { Option, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; +import type { Codec, ITuple } from '@polkadot/types-codec/types'; +import type { H160, Perbill, Permill } from '@polkadot/types/interfaces/runtime'; +import type { FrameSupportPalletId, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, PalletReferendaTrackInfo, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, UpDataStructsCollectionLimits } from '@polkadot/types/lookup'; + +export type __AugmentedConst = AugmentedConst; + +declare module '@polkadot/api-base/types/consts' { + interface AugmentedConsts { + appPromotion: { + /** + * Freeze identifier used by the pallet + **/ + freezeIdentifier: U8aFixed & AugmentedConst; + /** + * Rate of return for interval in blocks defined in `RecalculationInterval`. + **/ + intervalIncome: Perbill & AugmentedConst; + /** + * Decimals for the `Currency`. + **/ + nominal: u128 & AugmentedConst; + /** + * The app's pallet id, used for deriving its sovereign account address. + **/ + palletId: FrameSupportPalletId & AugmentedConst; + /** + * In parachain blocks. + **/ + pendingInterval: u32 & AugmentedConst; + /** + * In relay blocks. + **/ + recalculationInterval: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + balances: { + /** + * The minimum amount required to keep an account open. MUST BE GREATER THAN ZERO! + * + * If you *really* need it to be zero, you can enable the feature `insecure_zero_ed` for + * this pallet. However, you do so at your own risk: this will open up a major DoS vector. + * In case you have multiple sources of provider references, you may also get unexpected + * behaviour if you set this to zero. + * + * Bottom line: Do yourself a favour and make it at least one! + **/ + existentialDeposit: u128 & AugmentedConst; + /** + * The maximum number of individual freeze locks that can exist on an account at any time. + **/ + maxFreezes: u32 & AugmentedConst; + /** + * The maximum number of holds that can exist on an account at any time. + **/ + maxHolds: u32 & AugmentedConst; + /** + * The maximum number of locks that should exist on an account. + * Not strictly enforced, but used for weight estimation. + **/ + maxLocks: u32 & AugmentedConst; + /** + * The maximum number of named reserves that can exist on an account. + **/ + maxReserves: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + common: { + /** + * Maximum admins per collection. + **/ + collectionAdminsLimit: u32 & AugmentedConst; + /** + * Set price to create a collection. + **/ + collectionCreationPrice: u128 & AugmentedConst; + /** + * Address under which the CollectionHelper contract would be available. + **/ + contractAddress: H160 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + configuration: { + appPromotionDailyRate: Perbill & AugmentedConst; + dayRelayBlocks: u32 & AugmentedConst; + defaultCollatorSelectionKickThreshold: u32 & AugmentedConst; + defaultCollatorSelectionLicenseBond: u128 & AugmentedConst; + defaultCollatorSelectionMaxCollators: u32 & AugmentedConst; + defaultMinGasPrice: u64 & AugmentedConst; + defaultWeightToFeeCoefficient: u64 & AugmentedConst; + maxXcmAllowedLocations: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + council: { + /** + * The maximum weight of a dispatch call that can be proposed and executed. + **/ + maxProposalWeight: SpWeightsWeightV2Weight & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + democracy: { + /** + * Period in blocks where an external proposal may not be re-submitted after being vetoed. + **/ + cooloffPeriod: u32 & AugmentedConst; + /** + * The period between a proposal being approved and enacted. + * + * It should generally be a little more than the unstake period to ensure that + * voting stakers have an opportunity to remove themselves from the system in the case + * where they are on the losing side of a vote. + **/ + enactmentPeriod: u32 & AugmentedConst; + /** + * Minimum voting period allowed for a fast-track referendum. + **/ + fastTrackVotingPeriod: u32 & AugmentedConst; + /** + * Indicator for whether an emergency origin is even allowed to happen. Some chains may + * want to set this permanently to `false`, others may want to condition it on things such + * as an upgrade having happened recently. + **/ + instantAllowed: bool & AugmentedConst; + /** + * How often (in blocks) new public referenda are launched. + **/ + launchPeriod: u32 & AugmentedConst; + /** + * The maximum number of items which can be blacklisted. + **/ + maxBlacklisted: u32 & AugmentedConst; + /** + * The maximum number of deposits a public proposal may have at any time. + **/ + maxDeposits: u32 & AugmentedConst; + /** + * The maximum number of public proposals that can exist at any time. + **/ + maxProposals: u32 & AugmentedConst; + /** + * The maximum number of votes for an account. + * + * Also used to compute weight, an overly big value can + * lead to extrinsic with very big weight: see `delegate` for instance. + **/ + maxVotes: u32 & AugmentedConst; + /** + * The minimum amount to be used as a deposit for a public referendum proposal. + **/ + minimumDeposit: u128 & AugmentedConst; + /** + * The minimum period of vote locking. + * + * It should be no shorter than enactment period to ensure that in the case of an approval, + * those successful voters are locked into the consequences that their votes entail. + **/ + voteLockingPeriod: u32 & AugmentedConst; + /** + * How often (in blocks) to check for new votes. + **/ + votingPeriod: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + evmContractHelpers: { + /** + * Address, under which magic contract will be available + **/ + contractAddress: H160 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + fellowshipReferenda: { + /** + * Quantization level for the referendum wakeup scheduler. A higher number will result in + * fewer storage reads/writes needed for smaller voters, but also result in delays to the + * automatic referendum status changes. Explicit servicing instructions are unaffected. + **/ + alarmInterval: u32 & AugmentedConst; + /** + * Maximum size of the referendum queue for a single track. + **/ + maxQueued: u32 & AugmentedConst; + /** + * The minimum amount to be used as a deposit for a public referendum proposal. + **/ + submissionDeposit: u128 & AugmentedConst; + /** + * Information concerning the different referendum tracks. + **/ + tracks: Vec> & AugmentedConst; + /** + * The number of blocks after submission that a referendum must begin being decided by. + * Once this passes, then anyone may cancel the referendum. + **/ + undecidingTimeout: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + identity: { + /** + * The amount held on deposit for a registered identity + **/ + basicDeposit: u128 & AugmentedConst; + /** + * The amount held on deposit per additional field for a registered identity. + **/ + fieldDeposit: u128 & AugmentedConst; + /** + * Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O + * required to access an identity, but can be pretty high. + **/ + maxAdditionalFields: u32 & AugmentedConst; + /** + * Maxmimum number of registrars allowed in the system. Needed to bound the complexity + * of, e.g., updating judgements. + **/ + maxRegistrars: u32 & AugmentedConst; + /** + * The maximum number of sub-accounts allowed per identified account. + **/ + maxSubAccounts: u32 & AugmentedConst; + /** + * The amount held on deposit for a registered subaccount. This should account for the fact + * that one storage item's value will increase by the size of an account ID, and there will + * be another trie item whose value is the size of an account ID plus 32 bytes. + **/ + subAccountDeposit: u128 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + inflation: { + /** + * Number of blocks that pass between treasury balance updates due to inflation + **/ + inflationBlockInterval: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + scheduler: { + /** + * The maximum weight that may be scheduled per block for any dispatchables. + **/ + maximumWeight: SpWeightsWeightV2Weight & AugmentedConst; + /** + * The maximum number of scheduled calls in the queue for a single block. + * + * NOTE: + * + Dependent pallets' benchmarks might require a higher limit for the setting. Set a + * higher limit under `runtime-benchmarks` feature. + **/ + maxScheduledPerBlock: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + stateTrieMigration: { + /** + * Maximal number of bytes that a key can have. + * + * FRAME itself does not limit the key length. + * The concrete value must therefore depend on your storage usage. + * A [`frame_support::storage::StorageNMap`] for example can have an arbitrary number of + * keys which are then hashed and concatenated, resulting in arbitrarily long keys. + * + * Use the *state migration RPC* to retrieve the length of the longest key in your + * storage: + * + * The migration will halt with a `Halted` event if this value is too small. + * Since there is no real penalty from over-estimating, it is advised to use a large + * value. The default is 512 byte. + * + * Some key lengths for reference: + * - [`frame_support::storage::StorageValue`]: 32 byte + * - [`frame_support::storage::StorageMap`]: 64 byte + * - [`frame_support::storage::StorageDoubleMap`]: 96 byte + * + * For more info see + * + **/ + maxKeyLen: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + system: { + /** + * Maximum number of block number to block hash mappings to keep (oldest pruned first). + **/ + blockHashCount: u32 & AugmentedConst; + /** + * The maximum length of a block (in bytes). + **/ + blockLength: FrameSystemLimitsBlockLength & AugmentedConst; + /** + * Block & extrinsics weights: base values and limits. + **/ + blockWeights: FrameSystemLimitsBlockWeights & AugmentedConst; + /** + * The weight of runtime database operations the runtime can invoke. + **/ + dbWeight: SpWeightsRuntimeDbWeight & AugmentedConst; + /** + * The designated SS58 prefix of this chain. + * + * This replaces the "ss58Format" property declared in the chain spec. Reason is + * that the runtime should know about the prefix in order to make use of it as + * an identifier of the chain. + **/ + ss58Prefix: u16 & AugmentedConst; + /** + * Get the chain's current version. + **/ + version: SpVersionRuntimeVersion & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + technicalCommittee: { + /** + * The maximum weight of a dispatch call that can be proposed and executed. + **/ + maxProposalWeight: SpWeightsWeightV2Weight & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + timestamp: { + /** + * The minimum period between blocks. Beware that this is different to the *expected* + * period that the block production apparatus provides. Your chosen consensus system will + * generally work with this to determine a sensible block time. e.g. For Aura, it will be + * double this period on default settings. + **/ + minimumPeriod: u64 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + tokens: { + maxLocks: u32 & AugmentedConst; + /** + * The maximum number of named reserves that can exist on an account. + **/ + maxReserves: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + transactionPayment: { + /** + * A fee mulitplier for `Operational` extrinsics to compute "virtual tip" to boost their + * `priority` + * + * This value is multipled by the `final_fee` to obtain a "virtual tip" that is later + * added to a tip component in regular `priority` calculations. + * It means that a `Normal` transaction can front-run a similarly-sized `Operational` + * extrinsic (with no tip), by including a tip value greater than the virtual tip. + * + * ```rust,ignore + * // For `Normal` + * let priority = priority_calc(tip); + * + * // For `Operational` + * let virtual_tip = (inclusion_fee + tip) * OperationalFeeMultiplier; + * let priority = priority_calc(tip + virtual_tip); + * ``` + * + * Note that since we use `final_fee` the multiplier applies also to the regular `tip` + * sent with the transaction. So, not only does the transaction get a priority bump based + * on the `inclusion_fee`, but we also amplify the impact of tips applied to `Operational` + * transactions. + **/ + operationalFeeMultiplier: u8 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + treasury: { + /** + * Percentage of spare funds (if any) that are burnt per spend period. + **/ + burn: Permill & AugmentedConst; + /** + * The maximum number of approvals that can wait in the spending queue. + * + * NOTE: This parameter is also used within the Bounties Pallet extension if enabled. + **/ + maxApprovals: u32 & AugmentedConst; + /** + * The treasury's pallet id, used for deriving its sovereign account ID. + **/ + palletId: FrameSupportPalletId & AugmentedConst; + /** + * Fraction of a proposal's value that should be bonded in order to place the proposal. + * An accepted proposal gets these back. A rejected proposal does not. + **/ + proposalBond: Permill & AugmentedConst; + /** + * Maximum amount of funds that should be placed in a deposit for making a proposal. + **/ + proposalBondMaximum: Option & AugmentedConst; + /** + * Minimum amount of funds that should be placed in a deposit for making a proposal. + **/ + proposalBondMinimum: u128 & AugmentedConst; + /** + * Period between successive spends. + **/ + spendPeriod: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + unique: { + /** + * Maximum admins per collection. + **/ + collectionAdminsLimit: u32 & AugmentedConst; + /** + * Default FT collection limit. + **/ + ftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst; + /** + * Maximal length of a collection description. + **/ + maxCollectionDescriptionLength: u32 & AugmentedConst; + /** + * Maximal length of a collection name. + **/ + maxCollectionNameLength: u32 & AugmentedConst; + /** + * Maximum size for all collection properties. + **/ + maxCollectionPropertiesSize: u32 & AugmentedConst; + /** + * A maximum number of token properties. + **/ + maxPropertiesPerItem: u32 & AugmentedConst; + /** + * Maximal length of a property key. + **/ + maxPropertyKeyLength: u32 & AugmentedConst; + /** + * Maximal length of a property value. + **/ + maxPropertyValueLength: u32 & AugmentedConst; + /** + * Maximal length of a token prefix. + **/ + maxTokenPrefixLength: u32 & AugmentedConst; + /** + * Maximum size of all token properties. + **/ + maxTokenPropertiesSize: u32 & AugmentedConst; + /** + * A maximum number of levels of depth in the token nesting tree. + **/ + nestingBudget: u32 & AugmentedConst; + /** + * Default NFT collection limit. + **/ + nftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst; + /** + * Default RFT collection limit. + **/ + rftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + utility: { + /** + * The limit on the number of batched calls. + **/ + batchedCallsLimit: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + vesting: { + /** + * The minimum amount transferred to call `vested_transfer`. + **/ + minVestedTransfer: u128 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + xTokens: { + /** + * Base XCM weight. + * + * The actually weight for an XCM message is `T::BaseXcmWeight + + * T::Weigher::weight(&msg)`. + **/ + baseXcmWeight: SpWeightsWeightV2Weight & AugmentedConst; + /** + * Self chain location. + **/ + selfLocation: StagingXcmV3MultiLocation & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; + } // AugmentedConsts +} // declare module --- /dev/null +++ b/js-packages/types/augment-api-errors.ts @@ -0,0 +1,1521 @@ +// Auto-generated via `yarn polkadot-types-from-chain`, do not edit +/* eslint-disable */ + +// import type lookup before we augment - in some environments +// this is required to allow for ambient/previous definitions +import '@polkadot/api-base/types/errors'; + +import type { ApiTypes, AugmentedError } from '@polkadot/api-base/types'; + +export type __AugmentedError = AugmentedError; + +declare module '@polkadot/api-base/types/errors' { + interface AugmentedErrors { + appPromotion: { + /** + * Error due to action requiring admin to be set. + **/ + AdminNotSet: AugmentedError; + /** + * Errors caused by incorrect state of a staker in context of the pallet. + **/ + InconsistencyState: AugmentedError; + /** + * Errors caused by insufficient staked balance. + **/ + InsufficientStakedBalance: AugmentedError; + /** + * No permission to perform an action. + **/ + NoPermission: AugmentedError; + /** + * Insufficient funds to perform an action. + **/ + NotSufficientFunds: AugmentedError; + /** + * Occurs when a pending unstake cannot be added in this block. PENDING_LIMIT_PER_BLOCK` limits exceeded. + **/ + PendingForBlockOverflow: AugmentedError; + /** + * The error is due to the fact that the collection/contract must already be sponsored in order to perform the action. + **/ + SponsorNotSet: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + balances: { + /** + * Beneficiary account must pre-exist. + **/ + DeadAccount: AugmentedError; + /** + * Value too low to create account due to existential deposit. + **/ + ExistentialDeposit: AugmentedError; + /** + * A vesting schedule already exists for this account. + **/ + ExistingVestingSchedule: AugmentedError; + /** + * Transfer/payment would kill account. + **/ + Expendability: AugmentedError; + /** + * Balance too low to send value. + **/ + InsufficientBalance: AugmentedError; + /** + * Account liquidity restrictions prevent withdrawal. + **/ + LiquidityRestrictions: AugmentedError; + /** + * Number of freezes exceed `MaxFreezes`. + **/ + TooManyFreezes: AugmentedError; + /** + * Number of holds exceed `MaxHolds`. + **/ + TooManyHolds: AugmentedError; + /** + * Number of named reserves exceed `MaxReserves`. + **/ + TooManyReserves: AugmentedError; + /** + * Vesting balance too high to send value. + **/ + VestingBalance: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + collatorSelection: { + /** + * User is already a candidate + **/ + AlreadyCandidate: AugmentedError; + /** + * User already holds license to collate + **/ + AlreadyHoldingLicense: AugmentedError; + /** + * User is already an Invulnerable + **/ + AlreadyInvulnerable: AugmentedError; + /** + * Account has no associated validator ID + **/ + NoAssociatedValidatorId: AugmentedError; + /** + * User does not hold a license to collate + **/ + NoLicense: AugmentedError; + /** + * User is not a candidate + **/ + NotCandidate: AugmentedError; + /** + * User is not an Invulnerable + **/ + NotInvulnerable: AugmentedError; + /** + * Permission issue + **/ + Permission: AugmentedError; + /** + * Too few invulnerables + **/ + TooFewInvulnerables: AugmentedError; + /** + * Too many candidates + **/ + TooManyCandidates: AugmentedError; + /** + * Too many invulnerables + **/ + TooManyInvulnerables: AugmentedError; + /** + * Unknown error + **/ + Unknown: AugmentedError; + /** + * Validator ID is not yet registered + **/ + ValidatorNotRegistered: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + common: { + /** + * Account token limit exceeded per collection + **/ + AccountTokenLimitExceeded: AugmentedError; + /** + * Only spending from eth mirror could be approved + **/ + AddressIsNotEthMirror: AugmentedError; + /** + * Can't transfer tokens to ethereum zero address + **/ + AddressIsZero: AugmentedError; + /** + * Address is not in allow list. + **/ + AddressNotInAllowlist: AugmentedError; + /** + * Requested value is more than the approved + **/ + ApprovedValueTooLow: AugmentedError; + /** + * Tried to approve more than owned + **/ + CantApproveMoreThanOwned: AugmentedError; + /** + * Destroying only empty collections is allowed + **/ + CantDestroyNotEmptyCollection: AugmentedError; + /** + * Exceeded max admin count + **/ + CollectionAdminCountExceeded: AugmentedError; + /** + * Collection description can not be longer than 255 char. + **/ + CollectionDescriptionLimitExceeded: AugmentedError; + /** + * Tried to store more data than allowed in collection field + **/ + CollectionFieldSizeExceeded: AugmentedError; + /** + * Tried to access an external collection with an internal API + **/ + CollectionIsExternal: AugmentedError; + /** + * Tried to access an internal collection with an external API + **/ + CollectionIsInternal: AugmentedError; + /** + * Collection limit bounds per collection exceeded + **/ + CollectionLimitBoundsExceeded: AugmentedError; + /** + * Collection name can not be longer than 63 char. + **/ + CollectionNameLimitExceeded: AugmentedError; + /** + * This collection does not exist. + **/ + CollectionNotFound: AugmentedError; + /** + * Collection token limit exceeded + **/ + CollectionTokenLimitExceeded: AugmentedError; + /** + * Token prefix can not be longer than 15 char. + **/ + CollectionTokenPrefixLimitExceeded: AugmentedError; + /** + * This address is not set as sponsor, use setCollectionSponsor first. + **/ + ConfirmSponsorshipFail: AugmentedError; + /** + * Empty property keys are forbidden + **/ + EmptyPropertyKey: AugmentedError; + /** + * Fungible tokens hold no ID, and the default value of TokenId for a fungible collection is 0. + **/ + FungibleItemsHaveNoId: AugmentedError; + /** + * Only ASCII letters, digits, and symbols `_`, `-`, and `.` are allowed + **/ + InvalidCharacterInPropertyKey: AugmentedError; + /** + * Metadata flag frozen + **/ + MetadataFlagFrozen: AugmentedError; + /** + * Sender parameter and item owner must be equal. + **/ + MustBeTokenOwner: AugmentedError; + /** + * No permission to perform action + **/ + NoPermission: AugmentedError; + /** + * Tried to store more property data than allowed + **/ + NoSpaceForProperty: AugmentedError; + /** + * Insufficient funds to perform an action + **/ + NotSufficientFounds: AugmentedError; + /** + * Tried to enable permissions which are only permitted to be disabled + **/ + OwnerPermissionsCantBeReverted: AugmentedError; + /** + * Property key is too long + **/ + PropertyKeyIsTooLong: AugmentedError; + /** + * Tried to store more property keys than allowed + **/ + PropertyLimitReached: AugmentedError; + /** + * Collection is not in mint mode. + **/ + PublicMintingNotAllowed: AugmentedError; + /** + * Only tokens from specific collections may nest tokens under this one + **/ + SourceCollectionIsNotAllowedToNest: AugmentedError; + /** + * Item does not exist + **/ + TokenNotFound: AugmentedError; + /** + * Item is balance not enough + **/ + TokenValueTooLow: AugmentedError; + /** + * Total collections bound exceeded. + **/ + TotalCollectionsLimitExceeded: AugmentedError; + /** + * Collection settings not allowing items transferring + **/ + TransferNotAllowed: AugmentedError; + /** + * The operation is not supported + **/ + UnsupportedOperation: AugmentedError; + /** + * User does not satisfy the nesting rule + **/ + UserIsNotAllowedToNest: AugmentedError; + /** + * The user is not an administrator. + **/ + UserIsNotCollectionAdmin: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + configuration: { + InconsistentConfiguration: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + council: { + /** + * Members are already initialized! + **/ + AlreadyInitialized: AugmentedError; + /** + * Duplicate proposals not allowed + **/ + DuplicateProposal: AugmentedError; + /** + * Duplicate vote ignored + **/ + DuplicateVote: AugmentedError; + /** + * Account is not a member + **/ + NotMember: AugmentedError; + /** + * Prime account is not a member + **/ + PrimeAccountNotMember: AugmentedError; + /** + * Proposal must exist + **/ + ProposalMissing: AugmentedError; + /** + * The close call was made too early, before the end of the voting. + **/ + TooEarly: AugmentedError; + /** + * There can only be a maximum of `MaxProposals` active proposals. + **/ + TooManyProposals: AugmentedError; + /** + * Mismatched index + **/ + WrongIndex: AugmentedError; + /** + * The given length bound for the proposal was too low. + **/ + WrongProposalLength: AugmentedError; + /** + * The given weight bound for the proposal was too low. + **/ + WrongProposalWeight: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + councilMembership: { + /** + * Already a member. + **/ + AlreadyMember: AugmentedError; + /** + * Not a member. + **/ + NotMember: AugmentedError; + /** + * Too many members. + **/ + TooManyMembers: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + cumulusXcm: { + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + democracy: { + /** + * Cannot cancel the same proposal twice + **/ + AlreadyCanceled: AugmentedError; + /** + * The account is already delegating. + **/ + AlreadyDelegating: AugmentedError; + /** + * Identity may not veto a proposal twice + **/ + AlreadyVetoed: AugmentedError; + /** + * Proposal already made + **/ + DuplicateProposal: AugmentedError; + /** + * The instant referendum origin is currently disallowed. + **/ + InstantNotAllowed: AugmentedError; + /** + * Too high a balance was provided that the account cannot afford. + **/ + InsufficientFunds: AugmentedError; + /** + * Invalid hash + **/ + InvalidHash: AugmentedError; + /** + * Maximum number of votes reached. + **/ + MaxVotesReached: AugmentedError; + /** + * No proposals waiting + **/ + NoneWaiting: AugmentedError; + /** + * Delegation to oneself makes no sense. + **/ + Nonsense: AugmentedError; + /** + * The actor has no permission to conduct the action. + **/ + NoPermission: AugmentedError; + /** + * No external proposal + **/ + NoProposal: AugmentedError; + /** + * The account is not currently delegating. + **/ + NotDelegating: AugmentedError; + /** + * Next external proposal not simple majority + **/ + NotSimpleMajority: AugmentedError; + /** + * The given account did not vote on the referendum. + **/ + NotVoter: AugmentedError; + /** + * The preimage does not exist. + **/ + PreimageNotExist: AugmentedError; + /** + * Proposal still blacklisted + **/ + ProposalBlacklisted: AugmentedError; + /** + * Proposal does not exist + **/ + ProposalMissing: AugmentedError; + /** + * Vote given for invalid referendum + **/ + ReferendumInvalid: AugmentedError; + /** + * Maximum number of items reached. + **/ + TooMany: AugmentedError; + /** + * Value too low + **/ + ValueLow: AugmentedError; + /** + * The account currently has votes attached to it and the operation cannot succeed until + * these are removed, either through `unvote` or `reap_vote`. + **/ + VotesExist: AugmentedError; + /** + * Voting period too low + **/ + VotingPeriodLow: AugmentedError; + /** + * Invalid upper bound. + **/ + WrongUpperBound: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + dmpQueue: { + /** + * The amount of weight given is possibly not enough for executing the message. + **/ + OverLimit: AugmentedError; + /** + * The message index given is unknown. + **/ + Unknown: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + ethereum: { + /** + * Signature is invalid. + **/ + InvalidSignature: AugmentedError; + /** + * Pre-log is present, therefore transact is not allowed. + **/ + PreLogExists: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + evm: { + /** + * Not enough balance to perform action + **/ + BalanceLow: AugmentedError; + /** + * Calculating total fee overflowed + **/ + FeeOverflow: AugmentedError; + /** + * Gas limit is too high. + **/ + GasLimitTooHigh: AugmentedError; + /** + * Gas limit is too low. + **/ + GasLimitTooLow: AugmentedError; + /** + * Gas price is too low. + **/ + GasPriceTooLow: AugmentedError; + /** + * Nonce is invalid + **/ + InvalidNonce: AugmentedError; + /** + * Calculating total payment overflowed + **/ + PaymentOverflow: AugmentedError; + /** + * EVM reentrancy + **/ + Reentrancy: AugmentedError; + /** + * EIP-3607, + **/ + TransactionMustComeFromEOA: AugmentedError; + /** + * Undefined error. + **/ + Undefined: AugmentedError; + /** + * Withdraw fee failed + **/ + WithdrawFailed: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + evmCoderSubstrate: { + OutOfFund: AugmentedError; + OutOfGas: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + evmContractHelpers: { + /** + * No pending sponsor for contract. + **/ + NoPendingSponsor: AugmentedError; + /** + * This method is only executable by contract owner + **/ + NoPermission: AugmentedError; + /** + * Number of methods that sponsored limit is defined for exceeds maximum. + **/ + TooManyMethodsHaveSponsoredLimit: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + evmMigration: { + /** + * Migration of this account is not yet started, or already finished. + **/ + AccountIsNotMigrating: AugmentedError; + /** + * Can only migrate to empty address. + **/ + AccountNotEmpty: AugmentedError; + /** + * Failed to decode event bytes + **/ + BadEvent: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + fellowshipCollective: { + /** + * Account is already a member. + **/ + AlreadyMember: AugmentedError; + /** + * Unexpected error in state. + **/ + Corruption: AugmentedError; + /** + * The information provided is incorrect. + **/ + InvalidWitness: AugmentedError; + /** + * There are no further records to be removed. + **/ + NoneRemaining: AugmentedError; + /** + * The origin is not sufficiently privileged to do the operation. + **/ + NoPermission: AugmentedError; + /** + * Account is not a member. + **/ + NotMember: AugmentedError; + /** + * The given poll index is unknown or has closed. + **/ + NotPolling: AugmentedError; + /** + * The given poll is still ongoing. + **/ + Ongoing: AugmentedError; + /** + * The member's rank is too low to vote. + **/ + RankTooLow: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + fellowshipReferenda: { + /** + * The referendum index provided is invalid in this context. + **/ + BadReferendum: AugmentedError; + /** + * The referendum status is invalid for this operation. + **/ + BadStatus: AugmentedError; + /** + * The track identifier given was invalid. + **/ + BadTrack: AugmentedError; + /** + * There are already a full complement of referenda in progress for this track. + **/ + Full: AugmentedError; + /** + * Referendum's decision deposit is already paid. + **/ + HasDeposit: AugmentedError; + /** + * The deposit cannot be refunded since none was made. + **/ + NoDeposit: AugmentedError; + /** + * The deposit refunder is not the depositor. + **/ + NoPermission: AugmentedError; + /** + * There was nothing to do in the advancement. + **/ + NothingToDo: AugmentedError; + /** + * Referendum is not ongoing. + **/ + NotOngoing: AugmentedError; + /** + * No track exists for the proposal origin. + **/ + NoTrack: AugmentedError; + /** + * The preimage does not exist. + **/ + PreimageNotExist: AugmentedError; + /** + * The queue of the track is empty. + **/ + QueueEmpty: AugmentedError; + /** + * Any deposit cannot be refunded until after the decision is over. + **/ + Unfinished: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + foreignAssets: { + /** + * AssetId exists + **/ + AssetIdExisted: AugmentedError; + /** + * AssetId not exists + **/ + AssetIdNotExists: AugmentedError; + /** + * The given location could not be used (e.g. because it cannot be expressed in the + * desired version of XCM). + **/ + BadLocation: AugmentedError; + /** + * MultiLocation existed + **/ + MultiLocationExisted: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + fungible: { + /** + * Fungible token does not support nesting. + **/ + FungibleDisallowsNesting: AugmentedError; + /** + * Tried to set data for fungible item. + **/ + FungibleItemsDontHaveData: AugmentedError; + /** + * Only a fungible collection could be possibly broken; any fungible token is valid. + **/ + FungibleTokensAreAlwaysValid: AugmentedError; + /** + * Not Fungible item data used to mint in Fungible collection. + **/ + NotFungibleDataUsedToMintFungibleCollectionToken: AugmentedError; + /** + * Setting allowance for all is not allowed. + **/ + SettingAllowanceForAllNotAllowed: AugmentedError; + /** + * Setting item properties is not allowed. + **/ + SettingPropertiesNotAllowed: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + identity: { + /** + * Account ID is already named. + **/ + AlreadyClaimed: AugmentedError; + /** + * Empty index. + **/ + EmptyIndex: AugmentedError; + /** + * Fee is changed. + **/ + FeeChanged: AugmentedError; + /** + * The index is invalid. + **/ + InvalidIndex: AugmentedError; + /** + * Invalid judgement. + **/ + InvalidJudgement: AugmentedError; + /** + * The target is invalid. + **/ + InvalidTarget: AugmentedError; + /** + * The provided judgement was for a different identity. + **/ + JudgementForDifferentIdentity: AugmentedError; + /** + * Judgement given. + **/ + JudgementGiven: AugmentedError; + /** + * Error that occurs when there is an issue paying for judgement. + **/ + JudgementPaymentFailed: AugmentedError; + /** + * No identity found. + **/ + NoIdentity: AugmentedError; + /** + * Account isn't found. + **/ + NotFound: AugmentedError; + /** + * Account isn't named. + **/ + NotNamed: AugmentedError; + /** + * Sub-account isn't owned by sender. + **/ + NotOwned: AugmentedError; + /** + * Sender is not a sub-account. + **/ + NotSub: AugmentedError; + /** + * Sticky judgement. + **/ + StickyJudgement: AugmentedError; + /** + * Too many additional fields. + **/ + TooManyFields: AugmentedError; + /** + * Maximum amount of registrars reached. Cannot add any more. + **/ + TooManyRegistrars: AugmentedError; + /** + * Too many subs-accounts. + **/ + TooManySubAccounts: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + maintenance: { + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + nonfungible: { + /** + * Unable to burn NFT with children + **/ + CantBurnNftWithChildren: AugmentedError; + /** + * Used amount > 1 with NFT + **/ + NonfungibleItemsHaveNoAmount: AugmentedError; + /** + * Not Nonfungible item data used to mint in Nonfungible collection. + **/ + NotNonfungibleDataUsedToMintFungibleCollectionToken: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + parachainSystem: { + /** + * The inherent which supplies the host configuration did not run this block. + **/ + HostConfigurationNotAvailable: AugmentedError; + /** + * No code upgrade has been authorized. + **/ + NothingAuthorized: AugmentedError; + /** + * No validation function upgrade is currently scheduled. + **/ + NotScheduled: AugmentedError; + /** + * Attempt to upgrade validation function while existing upgrade pending. + **/ + OverlappingUpgrades: AugmentedError; + /** + * Polkadot currently prohibits this parachain from upgrading its validation function. + **/ + ProhibitedByPolkadot: AugmentedError; + /** + * The supplied validation function has compiled into a blob larger than Polkadot is + * willing to run. + **/ + TooBig: AugmentedError; + /** + * The given code upgrade has not been authorized. + **/ + Unauthorized: AugmentedError; + /** + * The inherent which supplies the validation data did not run this block. + **/ + ValidationDataNotAvailable: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + polkadotXcm: { + /** + * The given account is not an identifiable sovereign account for any location. + **/ + AccountNotSovereign: AugmentedError; + /** + * The location is invalid since it already has a subscription from us. + **/ + AlreadySubscribed: AugmentedError; + /** + * The given location could not be used (e.g. because it cannot be expressed in the + * desired version of XCM). + **/ + BadLocation: AugmentedError; + /** + * The version of the `Versioned` value used is not able to be interpreted. + **/ + BadVersion: AugmentedError; + /** + * Could not re-anchor the assets to declare the fees for the destination chain. + **/ + CannotReanchor: AugmentedError; + /** + * The destination `MultiLocation` provided cannot be inverted. + **/ + DestinationNotInvertible: AugmentedError; + /** + * The assets to be sent are empty. + **/ + Empty: AugmentedError; + /** + * The operation required fees to be paid which the initiator could not meet. + **/ + FeesNotMet: AugmentedError; + /** + * The message execution fails the filter. + **/ + Filtered: AugmentedError; + /** + * The unlock operation cannot succeed because there are still consumers of the lock. + **/ + InUse: AugmentedError; + /** + * Invalid asset for the operation. + **/ + InvalidAsset: AugmentedError; + /** + * Origin is invalid for sending. + **/ + InvalidOrigin: AugmentedError; + /** + * A remote lock with the corresponding data could not be found. + **/ + LockNotFound: AugmentedError; + /** + * The owner does not own (all) of the asset that they wish to do the operation on. + **/ + LowBalance: AugmentedError; + /** + * The referenced subscription could not be found. + **/ + NoSubscription: AugmentedError; + /** + * There was some other issue (i.e. not to do with routing) in sending the message. + * Perhaps a lack of space for buffering the message. + **/ + SendFailure: AugmentedError; + /** + * Too many assets have been attempted for transfer. + **/ + TooManyAssets: AugmentedError; + /** + * The asset owner has too many locks on the asset. + **/ + TooManyLocks: AugmentedError; + /** + * The desired destination was unreachable, generally because there is a no way of routing + * to it. + **/ + Unreachable: AugmentedError; + /** + * The message's weight could not be determined. + **/ + UnweighableMessage: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + preimage: { + /** + * Preimage has already been noted on-chain. + **/ + AlreadyNoted: AugmentedError; + /** + * The user is not authorized to perform this action. + **/ + NotAuthorized: AugmentedError; + /** + * The preimage cannot be removed since it has not yet been noted. + **/ + NotNoted: AugmentedError; + /** + * The preimage request cannot be removed since no outstanding requests exist. + **/ + NotRequested: AugmentedError; + /** + * A preimage may not be removed when there are outstanding requests. + **/ + Requested: AugmentedError; + /** + * Preimage is too large to store on-chain. + **/ + TooBig: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + refungible: { + /** + * Not Refungible item data used to mint in Refungible collection. + **/ + NotRefungibleDataUsedToMintFungibleCollectionToken: AugmentedError; + /** + * Refungible token can't nest other tokens. + **/ + RefungibleDisallowsNesting: AugmentedError; + /** + * Refungible token can't be repartitioned by user who isn't owns all pieces. + **/ + RepartitionWhileNotOwningAllPieces: AugmentedError; + /** + * Setting item properties is not allowed. + **/ + SettingPropertiesNotAllowed: AugmentedError; + /** + * Maximum refungibility exceeded. + **/ + WrongRefungiblePieces: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + scheduler: { + /** + * Failed to schedule a call + **/ + FailedToSchedule: AugmentedError; + /** + * Attempt to use a non-named function on a named task. + **/ + Named: AugmentedError; + /** + * Cannot find the scheduled call. + **/ + NotFound: AugmentedError; + /** + * Reschedule failed because it does not change scheduled time. + **/ + RescheduleNoChange: AugmentedError; + /** + * Given target block number is in the past. + **/ + TargetBlockNumberInPast: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + session: { + /** + * Registered duplicate key. + **/ + DuplicatedKey: AugmentedError; + /** + * Invalid ownership proof. + **/ + InvalidProof: AugmentedError; + /** + * Key setting account is not live, so it's impossible to associate keys. + **/ + NoAccount: AugmentedError; + /** + * No associated validator ID for account. + **/ + NoAssociatedValidatorId: AugmentedError; + /** + * No keys are associated with this account. + **/ + NoKeys: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + stateTrieMigration: { + /** + * Bad child root provided. + **/ + BadChildRoot: AugmentedError; + /** + * Bad witness data provided. + **/ + BadWitness: AugmentedError; + /** + * A key was longer than the configured maximum. + * + * This means that the migration halted at the current [`Progress`] and + * can be resumed with a larger [`crate::Config::MaxKeyLen`] value. + * Retrying with the same [`crate::Config::MaxKeyLen`] value will not work. + * The value should only be increased to avoid a storage migration for the currently + * stored [`crate::Progress::LastKey`]. + **/ + KeyTooLong: AugmentedError; + /** + * Max signed limits not respected. + **/ + MaxSignedLimits: AugmentedError; + /** + * submitter does not have enough funds. + **/ + NotEnoughFunds: AugmentedError; + /** + * Signed migration is not allowed because the maximum limit is not set yet. + **/ + SignedMigrationNotAllowed: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + structure: { + /** + * While nesting, reached the breadth limit of nesting, exceeding the provided budget. + **/ + BreadthLimit: AugmentedError; + /** + * Tried to nest token under collection contract address, instead of token address + **/ + CantNestTokenUnderCollection: AugmentedError; + /** + * While nesting, reached the depth limit of nesting, exceeding the provided budget. + **/ + DepthLimit: AugmentedError; + /** + * While nesting, encountered an already checked account, detecting a loop. + **/ + OuroborosDetected: AugmentedError; + /** + * Couldn't find the token owner that is itself a token. + **/ + TokenNotFound: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + sudo: { + /** + * Sender must be the Sudo account + **/ + RequireSudo: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + system: { + /** + * The origin filter prevent the call to be dispatched. + **/ + CallFiltered: AugmentedError; + /** + * Failed to extract the runtime version from the new runtime. + * + * Either calling `Core_version` or decoding `RuntimeVersion` failed. + **/ + FailedToExtractRuntimeVersion: AugmentedError; + /** + * The name of specification does not match between the current runtime + * and the new runtime. + **/ + InvalidSpecName: AugmentedError; + /** + * Suicide called when the account has non-default composite data. + **/ + NonDefaultComposite: AugmentedError; + /** + * There is a non-zero reference count preventing the account from being purged. + **/ + NonZeroRefCount: AugmentedError; + /** + * The specification version is not allowed to decrease between the current runtime + * and the new runtime. + **/ + SpecVersionNeedsToIncrease: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + technicalCommittee: { + /** + * Members are already initialized! + **/ + AlreadyInitialized: AugmentedError; + /** + * Duplicate proposals not allowed + **/ + DuplicateProposal: AugmentedError; + /** + * Duplicate vote ignored + **/ + DuplicateVote: AugmentedError; + /** + * Account is not a member + **/ + NotMember: AugmentedError; + /** + * Prime account is not a member + **/ + PrimeAccountNotMember: AugmentedError; + /** + * Proposal must exist + **/ + ProposalMissing: AugmentedError; + /** + * The close call was made too early, before the end of the voting. + **/ + TooEarly: AugmentedError; + /** + * There can only be a maximum of `MaxProposals` active proposals. + **/ + TooManyProposals: AugmentedError; + /** + * Mismatched index + **/ + WrongIndex: AugmentedError; + /** + * The given length bound for the proposal was too low. + **/ + WrongProposalLength: AugmentedError; + /** + * The given weight bound for the proposal was too low. + **/ + WrongProposalWeight: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + technicalCommitteeMembership: { + /** + * Already a member. + **/ + AlreadyMember: AugmentedError; + /** + * Not a member. + **/ + NotMember: AugmentedError; + /** + * Too many members. + **/ + TooManyMembers: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + testUtils: { + TestPalletDisabled: AugmentedError; + TriggerRollback: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + tokens: { + /** + * Cannot convert Amount into Balance type + **/ + AmountIntoBalanceFailed: AugmentedError; + /** + * The balance is too low + **/ + BalanceTooLow: AugmentedError; + /** + * Beneficiary account must pre-exist + **/ + DeadAccount: AugmentedError; + /** + * Value too low to create account due to existential deposit + **/ + ExistentialDeposit: AugmentedError; + /** + * Transfer/payment would kill account + **/ + KeepAlive: AugmentedError; + /** + * Failed because liquidity restrictions due to locking + **/ + LiquidityRestrictions: AugmentedError; + /** + * Failed because the maximum locks was exceeded + **/ + MaxLocksExceeded: AugmentedError; + TooManyReserves: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + treasury: { + /** + * The spend origin is valid but the amount it is allowed to spend is lower than the + * amount to be spent. + **/ + InsufficientPermission: AugmentedError; + /** + * Proposer's balance is too low. + **/ + InsufficientProposersBalance: AugmentedError; + /** + * No proposal or bounty at that index. + **/ + InvalidIndex: AugmentedError; + /** + * Proposal has not been approved. + **/ + ProposalNotApproved: AugmentedError; + /** + * Too many approvals in the queue. + **/ + TooManyApprovals: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + unique: { + /** + * Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`]. + **/ + CollectionDecimalPointLimitExceeded: AugmentedError; + /** + * Length of items properties must be greater than 0. + **/ + EmptyArgument: AugmentedError; + /** + * Repertition is only supported by refungible collection. + **/ + RepartitionCalledOnNonRefungibleCollection: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + utility: { + /** + * Too many calls batched. + **/ + TooManyCalls: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + vesting: { + /** + * The vested transfer amount is too low + **/ + AmountLow: AugmentedError; + /** + * Insufficient amount of balance to lock + **/ + InsufficientBalanceToLock: AugmentedError; + /** + * Failed because the maximum vesting schedules was exceeded + **/ + MaxVestingSchedulesExceeded: AugmentedError; + /** + * This account have too many vesting schedules + **/ + TooManyVestingSchedules: AugmentedError; + /** + * Vesting period is zero + **/ + ZeroVestingPeriod: AugmentedError; + /** + * Number of vests is zero + **/ + ZeroVestingPeriodCount: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + xcmpQueue: { + /** + * Bad overweight index. + **/ + BadOverweightIndex: AugmentedError; + /** + * Bad XCM data. + **/ + BadXcm: AugmentedError; + /** + * Bad XCM origin. + **/ + BadXcmOrigin: AugmentedError; + /** + * Failed to send XCM message. + **/ + FailedToSend: AugmentedError; + /** + * Provided weight is possibly not enough to execute the message. + **/ + WeightOverLimit: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + xTokens: { + /** + * Asset has no reserve location. + **/ + AssetHasNoReserve: AugmentedError; + /** + * The specified index does not exist in a MultiAssets struct. + **/ + AssetIndexNonExistent: AugmentedError; + /** + * The version of the `Versioned` value used is not able to be + * interpreted. + **/ + BadVersion: AugmentedError; + /** + * Could not re-anchor the assets to declare the fees for the + * destination chain. + **/ + CannotReanchor: AugmentedError; + /** + * The destination `MultiLocation` provided cannot be inverted. + **/ + DestinationNotInvertible: AugmentedError; + /** + * We tried sending distinct asset and fee but they have different + * reserve chains. + **/ + DistinctReserveForAssetAndFee: AugmentedError; + /** + * Fee is not enough. + **/ + FeeNotEnough: AugmentedError; + /** + * Could not get ancestry of asset reserve location. + **/ + InvalidAncestry: AugmentedError; + /** + * The MultiAsset is invalid. + **/ + InvalidAsset: AugmentedError; + /** + * Invalid transfer destination. + **/ + InvalidDest: AugmentedError; + /** + * MinXcmFee not registered for certain reserve location + **/ + MinXcmFeeNotDefined: AugmentedError; + /** + * Not cross-chain transfer. + **/ + NotCrossChainTransfer: AugmentedError; + /** + * Currency is not cross-chain transferable. + **/ + NotCrossChainTransferableCurrency: AugmentedError; + /** + * Not supported MultiLocation + **/ + NotSupportedMultiLocation: AugmentedError; + /** + * The number of assets to be sent is over the maximum. + **/ + TooManyAssetsBeingSent: AugmentedError; + /** + * The message's weight could not be determined. + **/ + UnweighableMessage: AugmentedError; + /** + * XCM execution failed. + **/ + XcmExecutionFailed: AugmentedError; + /** + * The transfering asset amount is zero. + **/ + ZeroAmount: AugmentedError; + /** + * The fee is zero. + **/ + ZeroFee: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; + } // AugmentedErrors +} // declare module --- /dev/null +++ b/js-packages/types/augment-api-events.ts @@ -0,0 +1,1294 @@ +// Auto-generated via `yarn polkadot-types-from-chain`, do not edit +/* eslint-disable */ + +// import type lookup before we augment - in some environments +// this is required to allow for ambient/previous definitions +import '@polkadot/api-base/types/events'; + +import type { ApiTypes, AugmentedEvent } from '@polkadot/api-base/types'; +import type { Bytes, Null, Option, Result, U8aFixed, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; +import type { ITuple } from '@polkadot/types-codec/types'; +import type { AccountId32, H160, H256 } from '@polkadot/types/interfaces/runtime'; +import type { EthereumLog, EvmCoreErrorExitReason, FrameSupportDispatchDispatchInfo, FrameSupportPreimagesBounded, FrameSupportTokensMiscBalanceStatus, OrmlVestingVestingSchedule, PalletDemocracyMetadataOwner, PalletDemocracyVoteAccountVote, PalletDemocracyVoteThreshold, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletRankedCollectiveTally, PalletRankedCollectiveVoteRecord, PalletStateTrieMigrationError, PalletStateTrieMigrationMigrationCompute, SpRuntimeDispatchError, SpWeightsWeightV2Weight, StagingXcmV3MultiAsset, StagingXcmV3MultiLocation, StagingXcmV3MultiassetMultiAssets, StagingXcmV3Response, StagingXcmV3TraitsError, StagingXcmV3TraitsOutcome, StagingXcmV3Xcm, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation } from '@polkadot/types/lookup'; + +export type __AugmentedEvent = AugmentedEvent; + +declare module '@polkadot/api-base/types/events' { + interface AugmentedEvents { + appPromotion: { + /** + * The admin was set + * + * # Arguments + * * AccountId: account address of the admin + **/ + SetAdmin: AugmentedEvent; + /** + * Staking was performed + * + * # Arguments + * * AccountId: account of the staker + * * Balance : staking amount + **/ + Stake: AugmentedEvent; + /** + * Staking recalculation was performed + * + * # Arguments + * * AccountId: account of the staker. + * * Balance : recalculation base + * * Balance : total income + **/ + StakingRecalculation: AugmentedEvent; + /** + * Unstaking was performed + * + * # Arguments + * * AccountId: account of the staker + * * Balance : unstaking amount + **/ + Unstake: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + balances: { + /** + * A balance was set by root. + **/ + BalanceSet: AugmentedEvent; + /** + * Some amount was burned from an account. + **/ + Burned: AugmentedEvent; + /** + * Some amount was deposited (e.g. for transaction fees). + **/ + Deposit: AugmentedEvent; + /** + * An account was removed whose balance was non-zero but below ExistentialDeposit, + * resulting in an outright loss. + **/ + DustLost: AugmentedEvent; + /** + * An account was created with some free balance. + **/ + Endowed: AugmentedEvent; + /** + * Some balance was frozen. + **/ + Frozen: AugmentedEvent; + /** + * Total issuance was increased by `amount`, creating a credit to be balanced. + **/ + Issued: AugmentedEvent; + /** + * Some balance was locked. + **/ + Locked: AugmentedEvent; + /** + * Some amount was minted into an account. + **/ + Minted: AugmentedEvent; + /** + * Total issuance was decreased by `amount`, creating a debt to be balanced. + **/ + Rescinded: AugmentedEvent; + /** + * Some balance was reserved (moved from free to reserved). + **/ + Reserved: AugmentedEvent; + /** + * Some balance was moved from the reserve of the first account to the second account. + * Final argument indicates the destination balance type. + **/ + ReserveRepatriated: AugmentedEvent; + /** + * Some amount was restored into an account. + **/ + Restored: AugmentedEvent; + /** + * Some amount was removed from the account (e.g. for misbehavior). + **/ + Slashed: AugmentedEvent; + /** + * Some amount was suspended from an account (it can be restored later). + **/ + Suspended: AugmentedEvent; + /** + * Some balance was thawed. + **/ + Thawed: AugmentedEvent; + /** + * Transfer succeeded. + **/ + Transfer: AugmentedEvent; + /** + * Some balance was unlocked. + **/ + Unlocked: AugmentedEvent; + /** + * Some balance was unreserved (moved from reserved to free). + **/ + Unreserved: AugmentedEvent; + /** + * An account was upgraded. + **/ + Upgraded: AugmentedEvent; + /** + * Some amount was withdrawn from the account (e.g. for transaction fees). + **/ + Withdraw: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + collatorSelection: { + CandidateAdded: AugmentedEvent; + CandidateRemoved: AugmentedEvent; + InvulnerableAdded: AugmentedEvent; + InvulnerableRemoved: AugmentedEvent; + LicenseObtained: AugmentedEvent; + LicenseReleased: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + common: { + /** + * Address was added to the allow list. + **/ + AllowListAddressAdded: AugmentedEvent; + /** + * Address was removed from the allow list. + **/ + AllowListAddressRemoved: AugmentedEvent; + /** + * Amount pieces of token owned by `sender` was approved for `spender`. + **/ + Approved: AugmentedEvent; + /** + * A `sender` approves operations on all owned tokens for `spender`. + **/ + ApprovedForAll: AugmentedEvent; + /** + * Collection admin was added. + **/ + CollectionAdminAdded: AugmentedEvent; + /** + * Collection admin was removed. + **/ + CollectionAdminRemoved: AugmentedEvent; + /** + * New collection was created + **/ + CollectionCreated: AugmentedEvent; + /** + * New collection was destroyed + **/ + CollectionDestroyed: AugmentedEvent; + /** + * Collection limits were set. + **/ + CollectionLimitSet: AugmentedEvent; + /** + * Collection owned was changed. + **/ + CollectionOwnerChanged: AugmentedEvent; + /** + * Collection permissions were set. + **/ + CollectionPermissionSet: AugmentedEvent; + /** + * The property has been deleted. + **/ + CollectionPropertyDeleted: AugmentedEvent; + /** + * The colletion property has been added or edited. + **/ + CollectionPropertySet: AugmentedEvent; + /** + * Collection sponsor was removed. + **/ + CollectionSponsorRemoved: AugmentedEvent; + /** + * Collection sponsor was set. + **/ + CollectionSponsorSet: AugmentedEvent; + /** + * New item was created. + **/ + ItemCreated: AugmentedEvent; + /** + * Collection item was burned. + **/ + ItemDestroyed: AugmentedEvent; + /** + * The token property permission of a collection has been set. + **/ + PropertyPermissionSet: AugmentedEvent; + /** + * New sponsor was confirm. + **/ + SponsorshipConfirmed: AugmentedEvent; + /** + * The token property has been deleted. + **/ + TokenPropertyDeleted: AugmentedEvent; + /** + * The token property has been added or edited. + **/ + TokenPropertySet: AugmentedEvent; + /** + * Item was transferred + **/ + Transfer: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + configuration: { + NewCollatorKickThreshold: AugmentedEvent], { lengthInBlocks: Option }>; + NewCollatorLicenseBond: AugmentedEvent], { bondCost: Option }>; + NewDesiredCollators: AugmentedEvent], { desiredCollators: Option }>; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + council: { + /** + * A motion was approved by the required threshold. + **/ + Approved: AugmentedEvent; + /** + * A proposal was closed because its threshold was reached or after its duration was up. + **/ + Closed: AugmentedEvent; + /** + * A motion was not approved by the required threshold. + **/ + Disapproved: AugmentedEvent; + /** + * A motion was executed; result will be `Ok` if it returned without error. + **/ + Executed: AugmentedEvent], { proposalHash: H256, result: Result }>; + /** + * A single member did some action; result will be `Ok` if it returned without error. + **/ + MemberExecuted: AugmentedEvent], { proposalHash: H256, result: Result }>; + /** + * A motion (given hash) has been proposed (by given account) with a threshold (given + * `MemberCount`). + **/ + Proposed: AugmentedEvent; + /** + * A motion (given hash) has been voted on by given account, leaving + * a tally (yes votes and no votes given respectively as `MemberCount`). + **/ + Voted: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + councilMembership: { + /** + * Phantom member, never used. + **/ + Dummy: AugmentedEvent; + /** + * One of the members' keys changed. + **/ + KeyChanged: AugmentedEvent; + /** + * The given member was added; see the transaction for who. + **/ + MemberAdded: AugmentedEvent; + /** + * The given member was removed; see the transaction for who. + **/ + MemberRemoved: AugmentedEvent; + /** + * The membership was reset; see the transaction for who the new set is. + **/ + MembersReset: AugmentedEvent; + /** + * Two members were swapped; see the transaction for who. + **/ + MembersSwapped: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + cumulusXcm: { + /** + * Downward message executed with the given outcome. + * \[ id, outcome \] + **/ + ExecutedDownward: AugmentedEvent; + /** + * Downward message is invalid XCM. + * \[ id \] + **/ + InvalidFormat: AugmentedEvent; + /** + * Downward message is unsupported version of XCM. + * \[ id \] + **/ + UnsupportedVersion: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + democracy: { + /** + * A proposal_hash has been blacklisted permanently. + **/ + Blacklisted: AugmentedEvent; + /** + * A referendum has been cancelled. + **/ + Cancelled: AugmentedEvent; + /** + * An account has delegated their vote to another account. + **/ + Delegated: AugmentedEvent; + /** + * An external proposal has been tabled. + **/ + ExternalTabled: AugmentedEvent; + /** + * Metadata for a proposal or a referendum has been cleared. + **/ + MetadataCleared: AugmentedEvent; + /** + * Metadata for a proposal or a referendum has been set. + **/ + MetadataSet: AugmentedEvent; + /** + * Metadata has been transferred to new owner. + **/ + MetadataTransferred: AugmentedEvent; + /** + * A proposal has been rejected by referendum. + **/ + NotPassed: AugmentedEvent; + /** + * A proposal has been approved by referendum. + **/ + Passed: AugmentedEvent; + /** + * A proposal got canceled. + **/ + ProposalCanceled: AugmentedEvent; + /** + * A motion has been proposed by a public account. + **/ + Proposed: AugmentedEvent; + /** + * An account has secconded a proposal + **/ + Seconded: AugmentedEvent; + /** + * A referendum has begun. + **/ + Started: AugmentedEvent; + /** + * A public proposal has been tabled for referendum vote. + **/ + Tabled: AugmentedEvent; + /** + * An account has cancelled a previous delegation operation. + **/ + Undelegated: AugmentedEvent; + /** + * An external proposal has been vetoed. + **/ + Vetoed: AugmentedEvent; + /** + * An account has voted in a referendum + **/ + Voted: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + dmpQueue: { + /** + * Downward message executed with the given outcome. + **/ + ExecutedDownward: AugmentedEvent; + /** + * Downward message is invalid XCM. + **/ + InvalidFormat: AugmentedEvent; + /** + * The maximum number of downward messages was reached. + **/ + MaxMessagesExhausted: AugmentedEvent; + /** + * Downward message is overweight and was placed in the overweight queue. + **/ + OverweightEnqueued: AugmentedEvent; + /** + * Downward message from the overweight queue was executed. + **/ + OverweightServiced: AugmentedEvent; + /** + * Downward message is unsupported version of XCM. + **/ + UnsupportedVersion: AugmentedEvent; + /** + * The weight limit for handling downward messages was reached. + **/ + WeightExhausted: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + ethereum: { + /** + * An ethereum transaction was successfully executed. + **/ + Executed: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + evm: { + /** + * A contract has been created at given address. + **/ + Created: AugmentedEvent; + /** + * A contract was attempted to be created, but the execution failed. + **/ + CreatedFailed: AugmentedEvent; + /** + * A contract has been executed successfully with states applied. + **/ + Executed: AugmentedEvent; + /** + * A contract has been executed with errors. States are reverted with only gas fees applied. + **/ + ExecutedFailed: AugmentedEvent; + /** + * Ethereum events from contracts. + **/ + Log: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + evmContractHelpers: { + /** + * Collection sponsor was removed. + **/ + ContractSponsorRemoved: AugmentedEvent; + /** + * Contract sponsor was set. + **/ + ContractSponsorSet: AugmentedEvent; + /** + * New sponsor was confirm. + **/ + ContractSponsorshipConfirmed: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + evmMigration: { + /** + * This event is used in benchmarking and can be used for tests + **/ + TestEvent: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + fellowshipCollective: { + /** + * A member `who` has been added. + **/ + MemberAdded: AugmentedEvent; + /** + * The member `who` of given `rank` has been removed from the collective. + **/ + MemberRemoved: AugmentedEvent; + /** + * The member `who`se rank has been changed to the given `rank`. + **/ + RankChanged: AugmentedEvent; + /** + * The member `who` has voted for the `poll` with the given `vote` leading to an updated + * `tally`. + **/ + Voted: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + fellowshipReferenda: { + /** + * A referendum has been approved and its proposal has been scheduled. + **/ + Approved: AugmentedEvent; + /** + * A referendum has been cancelled. + **/ + Cancelled: AugmentedEvent; + ConfirmAborted: AugmentedEvent; + /** + * A referendum has ended its confirmation phase and is ready for approval. + **/ + Confirmed: AugmentedEvent; + ConfirmStarted: AugmentedEvent; + /** + * The decision deposit has been placed. + **/ + DecisionDepositPlaced: AugmentedEvent; + /** + * The decision deposit has been refunded. + **/ + DecisionDepositRefunded: AugmentedEvent; + /** + * A referendum has moved into the deciding phase. + **/ + DecisionStarted: AugmentedEvent; + /** + * A deposit has been slashaed. + **/ + DepositSlashed: AugmentedEvent; + /** + * A referendum has been killed. + **/ + Killed: AugmentedEvent; + /** + * Metadata for a referendum has been cleared. + **/ + MetadataCleared: AugmentedEvent; + /** + * Metadata for a referendum has been set. + **/ + MetadataSet: AugmentedEvent; + /** + * A proposal has been rejected by referendum. + **/ + Rejected: AugmentedEvent; + /** + * The submission deposit has been refunded. + **/ + SubmissionDepositRefunded: AugmentedEvent; + /** + * A referendum has been submitted. + **/ + Submitted: AugmentedEvent; + /** + * A referendum has been timed out without being decided. + **/ + TimedOut: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + foreignAssets: { + /** + * The asset registered. + **/ + AssetRegistered: AugmentedEvent; + /** + * The asset updated. + **/ + AssetUpdated: AugmentedEvent; + /** + * The foreign asset registered. + **/ + ForeignAssetRegistered: AugmentedEvent; + /** + * The foreign asset updated. + **/ + ForeignAssetUpdated: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + identity: { + /** + * A number of identities and associated info were forcibly inserted. + **/ + IdentitiesInserted: AugmentedEvent; + /** + * A number of identities and all associated info were forcibly removed. + **/ + IdentitiesRemoved: AugmentedEvent; + /** + * A name was cleared, and the given balance returned. + **/ + IdentityCleared: AugmentedEvent; + /** + * A name was removed and the given balance slashed. + **/ + IdentityKilled: AugmentedEvent; + /** + * A name was set or reset (which will remove all judgements). + **/ + IdentitySet: AugmentedEvent; + /** + * A judgement was given by a registrar. + **/ + JudgementGiven: AugmentedEvent; + /** + * A judgement was asked from a registrar. + **/ + JudgementRequested: AugmentedEvent; + /** + * A judgement request was retracted. + **/ + JudgementUnrequested: AugmentedEvent; + /** + * A registrar was added. + **/ + RegistrarAdded: AugmentedEvent; + /** + * A number of identities were forcibly updated with new sub-identities. + **/ + SubIdentitiesInserted: AugmentedEvent; + /** + * A sub-identity was added to an identity and the deposit paid. + **/ + SubIdentityAdded: AugmentedEvent; + /** + * A sub-identity was removed from an identity and the deposit freed. + **/ + SubIdentityRemoved: AugmentedEvent; + /** + * A sub-identity was cleared, and the given deposit repatriated from the + * main identity account to the sub-identity account. + **/ + SubIdentityRevoked: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + maintenance: { + MaintenanceDisabled: AugmentedEvent; + MaintenanceEnabled: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + parachainSystem: { + /** + * Downward messages were processed using the given weight. + **/ + DownwardMessagesProcessed: AugmentedEvent; + /** + * Some downward messages have been received and will be processed. + **/ + DownwardMessagesReceived: AugmentedEvent; + /** + * An upgrade has been authorized. + **/ + UpgradeAuthorized: AugmentedEvent; + /** + * An upward message was sent to the relay chain. + **/ + UpwardMessageSent: AugmentedEvent], { messageHash: Option }>; + /** + * The validation function was applied as of the contained relay chain block number. + **/ + ValidationFunctionApplied: AugmentedEvent; + /** + * The relay-chain aborted the upgrade process. + **/ + ValidationFunctionDiscarded: AugmentedEvent; + /** + * The validation function has been scheduled to apply. + **/ + ValidationFunctionStored: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + polkadotXcm: { + /** + * Some assets have been claimed from an asset trap + **/ + AssetsClaimed: AugmentedEvent; + /** + * Some assets have been placed in an asset trap. + **/ + AssetsTrapped: AugmentedEvent; + /** + * Execution of an XCM message was attempted. + **/ + Attempted: AugmentedEvent; + /** + * Fees were paid from a location for an operation (often for using `SendXcm`). + **/ + FeesPaid: AugmentedEvent; + /** + * Expected query response has been received but the querier location of the response does + * not match the expected. The query remains registered for a later, valid, response to + * be received and acted upon. + **/ + InvalidQuerier: AugmentedEvent], { origin: StagingXcmV3MultiLocation, queryId: u64, expectedQuerier: StagingXcmV3MultiLocation, maybeActualQuerier: Option }>; + /** + * Expected query response has been received but the expected querier location placed in + * storage by this runtime previously cannot be decoded. The query remains registered. + * + * This is unexpected (since a location placed in storage in a previously executing + * runtime should be readable prior to query timeout) and dangerous since the possibly + * valid response will be dropped. Manual governance intervention is probably going to be + * needed. + **/ + InvalidQuerierVersion: AugmentedEvent; + /** + * Expected query response has been received but the origin location of the response does + * not match that expected. The query remains registered for a later, valid, response to + * be received and acted upon. + **/ + InvalidResponder: AugmentedEvent], { origin: StagingXcmV3MultiLocation, queryId: u64, expectedLocation: Option }>; + /** + * Expected query response has been received but the expected origin location placed in + * storage by this runtime previously cannot be decoded. The query remains registered. + * + * This is unexpected (since a location placed in storage in a previously executing + * runtime should be readable prior to query timeout) and dangerous since the possibly + * valid response will be dropped. Manual governance intervention is probably going to be + * needed. + **/ + InvalidResponderVersion: AugmentedEvent; + /** + * Query response has been received and query is removed. The registered notification has + * been dispatched and executed successfully. + **/ + Notified: AugmentedEvent; + /** + * Query response has been received and query is removed. The dispatch was unable to be + * decoded into a `Call`; this might be due to dispatch function having a signature which + * is not `(origin, QueryId, Response)`. + **/ + NotifyDecodeFailed: AugmentedEvent; + /** + * Query response has been received and query is removed. There was a general error with + * dispatching the notification call. + **/ + NotifyDispatchError: AugmentedEvent; + /** + * Query response has been received and query is removed. The registered notification + * could not be dispatched because the dispatch weight is greater than the maximum weight + * originally budgeted by this runtime for the query result. + **/ + NotifyOverweight: AugmentedEvent; + /** + * A given location which had a version change subscription was dropped owing to an error + * migrating the location to our new XCM format. + **/ + NotifyTargetMigrationFail: AugmentedEvent; + /** + * A given location which had a version change subscription was dropped owing to an error + * sending the notification to it. + **/ + NotifyTargetSendFail: AugmentedEvent; + /** + * Query response has been received and is ready for taking with `take_response`. There is + * no registered notification call. + **/ + ResponseReady: AugmentedEvent; + /** + * Received query response has been read and removed. + **/ + ResponseTaken: AugmentedEvent; + /** + * A XCM message was sent. + **/ + Sent: AugmentedEvent; + /** + * The supported version of a location has been changed. This might be through an + * automatic notification or a manual intervention. + **/ + SupportedVersionChanged: AugmentedEvent; + /** + * Query response received which does not match a registered query. This may be because a + * matching query was never registered, it may be because it is a duplicate response, or + * because the query timed out. + **/ + UnexpectedResponse: AugmentedEvent; + /** + * An XCM version change notification message has been attempted to be sent. + * + * The cost of sending it (borne by the chain) is included. + **/ + VersionChangeNotified: AugmentedEvent; + /** + * We have requested that a remote chain send us XCM version change notifications. + **/ + VersionNotifyRequested: AugmentedEvent; + /** + * A remote has requested XCM version change notification from us and we have honored it. + * A version information message is sent to them and its cost is included. + **/ + VersionNotifyStarted: AugmentedEvent; + /** + * We have requested that a remote chain stops sending us XCM version change + * notifications. + **/ + VersionNotifyUnrequested: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + preimage: { + /** + * A preimage has ben cleared. + **/ + Cleared: AugmentedEvent; + /** + * A preimage has been noted. + **/ + Noted: AugmentedEvent; + /** + * A preimage has been requested. + **/ + Requested: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + scheduler: { + /** + * The call for the provided hash was not found so the task has been aborted. + **/ + CallUnavailable: AugmentedEvent, id: Option], { task: ITuple<[u32, u32]>, id: Option }>; + /** + * Canceled some task. + **/ + Canceled: AugmentedEvent; + /** + * Dispatched some task. + **/ + Dispatched: AugmentedEvent, id: Option, result: Result], { task: ITuple<[u32, u32]>, id: Option, result: Result }>; + /** + * The given task was unable to be renewed since the agenda is full at that block. + **/ + PeriodicFailed: AugmentedEvent, id: Option], { task: ITuple<[u32, u32]>, id: Option }>; + /** + * The given task can never be executed since it is overweight. + **/ + PermanentlyOverweight: AugmentedEvent, id: Option], { task: ITuple<[u32, u32]>, id: Option }>; + /** + * Scheduled some task. + **/ + Scheduled: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + session: { + /** + * New session has happened. Note that the argument is the session index, not the + * block number as the type might suggest. + **/ + NewSession: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + stateTrieMigration: { + /** + * The auto migration task finished. + **/ + AutoMigrationFinished: AugmentedEvent; + /** + * Migration got halted due to an error or miss-configuration. + **/ + Halted: AugmentedEvent; + /** + * Given number of `(top, child)` keys were migrated respectively, with the given + * `compute`. + **/ + Migrated: AugmentedEvent; + /** + * Some account got slashed by the given amount. + **/ + Slashed: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + structure: { + /** + * Executed call on behalf of the token. + **/ + Executed: AugmentedEvent]>; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + sudo: { + /** + * The \[sudoer\] just switched identity; the old key is supplied if one existed. + **/ + KeyChanged: AugmentedEvent], { oldSudoer: Option }>; + /** + * A sudo just took place. \[result\] + **/ + Sudid: AugmentedEvent], { sudoResult: Result }>; + /** + * A sudo just took place. \[result\] + **/ + SudoAsDone: AugmentedEvent], { sudoResult: Result }>; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + system: { + /** + * `:code` was updated. + **/ + CodeUpdated: AugmentedEvent; + /** + * An extrinsic failed. + **/ + ExtrinsicFailed: AugmentedEvent; + /** + * An extrinsic completed successfully. + **/ + ExtrinsicSuccess: AugmentedEvent; + /** + * An account was reaped. + **/ + KilledAccount: AugmentedEvent; + /** + * A new account was created. + **/ + NewAccount: AugmentedEvent; + /** + * On on-chain remark happened. + **/ + Remarked: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + technicalCommittee: { + /** + * A motion was approved by the required threshold. + **/ + Approved: AugmentedEvent; + /** + * A proposal was closed because its threshold was reached or after its duration was up. + **/ + Closed: AugmentedEvent; + /** + * A motion was not approved by the required threshold. + **/ + Disapproved: AugmentedEvent; + /** + * A motion was executed; result will be `Ok` if it returned without error. + **/ + Executed: AugmentedEvent], { proposalHash: H256, result: Result }>; + /** + * A single member did some action; result will be `Ok` if it returned without error. + **/ + MemberExecuted: AugmentedEvent], { proposalHash: H256, result: Result }>; + /** + * A motion (given hash) has been proposed (by given account) with a threshold (given + * `MemberCount`). + **/ + Proposed: AugmentedEvent; + /** + * A motion (given hash) has been voted on by given account, leaving + * a tally (yes votes and no votes given respectively as `MemberCount`). + **/ + Voted: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + technicalCommitteeMembership: { + /** + * Phantom member, never used. + **/ + Dummy: AugmentedEvent; + /** + * One of the members' keys changed. + **/ + KeyChanged: AugmentedEvent; + /** + * The given member was added; see the transaction for who. + **/ + MemberAdded: AugmentedEvent; + /** + * The given member was removed; see the transaction for who. + **/ + MemberRemoved: AugmentedEvent; + /** + * The membership was reset; see the transaction for who the new set is. + **/ + MembersReset: AugmentedEvent; + /** + * Two members were swapped; see the transaction for who. + **/ + MembersSwapped: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + testUtils: { + BatchCompleted: AugmentedEvent; + ShouldRollback: AugmentedEvent; + ValueIsSet: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + tokens: { + /** + * A balance was set by root. + **/ + BalanceSet: AugmentedEvent; + /** + * Deposited some balance into an account + **/ + Deposited: AugmentedEvent; + /** + * An account was removed whose balance was non-zero but below + * ExistentialDeposit, resulting in an outright loss. + **/ + DustLost: AugmentedEvent; + /** + * An account was created with some free balance. + **/ + Endowed: AugmentedEvent; + Issued: AugmentedEvent; + /** + * Some free balance was locked. + **/ + Locked: AugmentedEvent; + /** + * Some locked funds were unlocked + **/ + LockRemoved: AugmentedEvent; + /** + * Some funds are locked + **/ + LockSet: AugmentedEvent; + Rescinded: AugmentedEvent; + /** + * Some balance was reserved (moved from free to reserved). + **/ + Reserved: AugmentedEvent; + /** + * Some reserved balance was repatriated (moved from reserved to + * another account). + **/ + ReserveRepatriated: AugmentedEvent; + /** + * Some balances were slashed (e.g. due to mis-behavior) + **/ + Slashed: AugmentedEvent; + /** + * The total issuance of an currency has been set + **/ + TotalIssuanceSet: AugmentedEvent; + /** + * Transfer succeeded. + **/ + Transfer: AugmentedEvent; + /** + * Some locked balance was freed. + **/ + Unlocked: AugmentedEvent; + /** + * Some balance was unreserved (moved from reserved to free). + **/ + Unreserved: AugmentedEvent; + /** + * Some balances were withdrawn (e.g. pay for transaction fee) + **/ + Withdrawn: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + transactionPayment: { + /** + * A transaction fee `actual_fee`, of which `tip` was added to the minimum inclusion fee, + * has been paid by `who`. + **/ + TransactionFeePaid: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + treasury: { + /** + * Some funds have been allocated. + **/ + Awarded: AugmentedEvent; + /** + * Some of our funds have been burnt. + **/ + Burnt: AugmentedEvent; + /** + * Some funds have been deposited. + **/ + Deposit: AugmentedEvent; + /** + * New proposal. + **/ + Proposed: AugmentedEvent; + /** + * A proposal was rejected; funds were slashed. + **/ + Rejected: AugmentedEvent; + /** + * Spending has finished; this is the amount that rolls over until next spend. + **/ + Rollover: AugmentedEvent; + /** + * A new spend proposal has been approved. + **/ + SpendApproved: AugmentedEvent; + /** + * We have ended a spend period and will now allocate funds. + **/ + Spending: AugmentedEvent; + /** + * The inactive funds of the pallet have been updated. + **/ + UpdatedInactive: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + utility: { + /** + * Batch of dispatches completed fully with no error. + **/ + BatchCompleted: AugmentedEvent; + /** + * Batch of dispatches completed but has errors. + **/ + BatchCompletedWithErrors: AugmentedEvent; + /** + * Batch of dispatches did not complete fully. Index of first failing dispatch given, as + * well as the error. + **/ + BatchInterrupted: AugmentedEvent; + /** + * A call was dispatched. + **/ + DispatchedAs: AugmentedEvent], { result: Result }>; + /** + * A single item within a Batch of dispatches has completed with no error. + **/ + ItemCompleted: AugmentedEvent; + /** + * A single item within a Batch of dispatches has completed with error. + **/ + ItemFailed: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + vesting: { + /** + * Claimed vesting. + **/ + Claimed: AugmentedEvent; + /** + * Added new vesting schedule. + **/ + VestingScheduleAdded: AugmentedEvent; + /** + * Updated vesting schedules. + **/ + VestingSchedulesUpdated: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + xcmpQueue: { + /** + * Bad XCM format used. + **/ + BadFormat: AugmentedEvent; + /** + * Bad XCM version used. + **/ + BadVersion: AugmentedEvent; + /** + * Some XCM failed. + **/ + Fail: AugmentedEvent; + /** + * An XCM exceeded the individual message weight budget. + **/ + OverweightEnqueued: AugmentedEvent; + /** + * An XCM from the overweight queue was executed with the given actual weight used. + **/ + OverweightServiced: AugmentedEvent; + /** + * Some XCM was executed ok. + **/ + Success: AugmentedEvent; + /** + * An HRMP message was sent to a sibling parachain. + **/ + XcmpMessageSent: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + xTokens: { + /** + * Transferred `MultiAsset` with fee. + **/ + TransferredMultiAssets: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; + } // AugmentedEvents +} // declare module --- /dev/null +++ b/js-packages/types/augment-api-query.ts @@ -0,0 +1,1442 @@ +// Auto-generated via `yarn polkadot-types-from-chain`, do not edit +/* eslint-disable */ + +// import type lookup before we augment - in some environments +// this is required to allow for ambient/previous definitions +import '@polkadot/api-base/types/storage'; + +import type { ApiTypes, AugmentedQuery, QueryableStorageEntry } from '@polkadot/api-base/types'; +import type { Data } from '@polkadot/types'; +import type { BTreeMap, Bytes, Option, Struct, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; +import type { AnyNumber, ITuple } from '@polkadot/types-codec/types'; +import type { AccountId32, Call, H160, H256 } from '@polkadot/types/interfaces/runtime'; +import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCodeUpgradeAuthorization, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletParachainSystemUnincludedSegmentAncestor, CumulusPalletParachainSystemUnincludedSegmentSegmentTracker, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportDispatchPerDispatchClassWeight, FrameSupportPreimagesBounded, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OpalRuntimeRuntimeCommonSessionKeys, OpalRuntimeRuntimeHoldReason, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensReserveData, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesIdAmount, PalletBalancesReserveData, PalletCollectiveVotes, PalletConfigurationAppPromotionConfiguration, PalletDemocracyMetadataOwner, PalletDemocracyReferendumInfo, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCodeMetadata, PalletEvmContractHelpersSponsoringModeT, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletIdentityRegistrarInfo, PalletIdentityRegistration, PalletNonfungibleItemData, PalletPreimageRequestStatus, PalletRankedCollectiveMemberRecord, PalletRankedCollectiveVoteRecord, PalletReferendaReferendumInfo, PalletSchedulerScheduled, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletXcmQueryStatus, PalletXcmRemoteLockedFungibleRecord, PalletXcmVersionMigrationStage, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV5AbridgedHostConfiguration, PolkadotPrimitivesV5PersistedValidationData, PolkadotPrimitivesV5UpgradeGoAhead, PolkadotPrimitivesV5UpgradeRestriction, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpRuntimeDigest, SpTrieStorageProof, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, StagingXcmVersionedAssetId, StagingXcmVersionedMultiLocation, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyScope, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild } from '@polkadot/types/lookup'; +import type { Observable } from '@polkadot/types/types'; + +export type __AugmentedQuery = AugmentedQuery unknown>; +export type __QueryableStorageEntry = QueryableStorageEntry; + +declare module '@polkadot/api-base/types/storage' { + interface AugmentedQueries { + appPromotion: { + /** + * Stores the `admin` account. Some extrinsics can only be executed if they were signed by `admin`. + **/ + admin: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Pending unstake records for an `Account`. + * + * * **Key** - Staker account. + * * **Value** - Amount of stakes. + **/ + pendingUnstake: AugmentedQuery Observable>>, [u32]> & QueryableStorageEntry; + /** + * Stores a key for record for which the revenue recalculation was performed. + * If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers. + **/ + previousCalculatedRecord: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * Stores the amount of tokens staked by account in the blocknumber. + * + * * **Key1** - Staker account. + * * **Key2** - Relay block number when the stake was made. + * * **(Balance, BlockNumber)** - Balance of the stake. + * The number of the relay block in which we must perform the interest recalculation + **/ + staked: AugmentedQuery Observable>, [AccountId32, u32]> & QueryableStorageEntry; + /** + * Stores number of stake records for an `Account`. + * + * * **Key** - Staker account. + * * **Value** - Amount of stakes. + **/ + stakesPerAccount: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; + /** + * Stores the total staked amount. + **/ + totalStaked: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + aura: { + /** + * The current authority set. + **/ + authorities: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The current slot of this block. + * + * This will be set in `on_initialize`. + **/ + currentSlot: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + auraExt: { + /** + * Serves as cache for the authorities. + * + * The authorities in AuRa are overwritten in `on_initialize` when we switch to a new session, + * but we require the old authorities to verify the seal when validating a PoV. This will + * always be updated to the latest AuRa authorities in `on_finalize`. + **/ + authorities: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Current slot paired with a number of authored blocks. + * + * Updated on each block initialization. + **/ + slotInfo: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + authorship: { + /** + * Author of current block. + **/ + author: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + balances: { + /** + * The Balances pallet example of storing the balance of an account. + * + * # Example + * + * ```nocompile + * impl pallet_balances::Config for Runtime { + * type AccountStore = StorageMapShim, frame_system::Provider, AccountId, Self::AccountData> + * } + * ``` + * + * You can also store the balance of an account in the `System` pallet. + * + * # Example + * + * ```nocompile + * impl pallet_balances::Config for Runtime { + * type AccountStore = System + * } + * ``` + * + * But this comes with tradeoffs, storing account balances in the system pallet stores + * `frame_system` data alongside the account data contrary to storing account balances in the + * `Balances` pallet, which uses a `StorageMap` to store balances data only. + * NOTE: This is only used in the case that this pallet is used to store balances. + **/ + account: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; + /** + * Freeze locks on account balances. + **/ + freezes: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; + /** + * Holds on account balances. + **/ + holds: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; + /** + * The total units of outstanding deactivated balance in the system. + **/ + inactiveIssuance: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Any liquidity locks on some account balances. + * NOTE: Should only be accessed when setting, changing and freeing a lock. + **/ + locks: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; + /** + * Named reserves on some account balances. + **/ + reserves: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; + /** + * The total units issued in the system. + **/ + totalIssuance: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + collatorSelection: { + /** + * The (community, limited) collation candidates. + **/ + candidates: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The invulnerable, fixed collators. + **/ + invulnerables: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Last block authored by collator. + **/ + lastAuthoredBlock: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; + /** + * The (community) collation license holders. + **/ + licenseDepositOf: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + common: { + /** + * Storage of the amount of collection admins. + **/ + adminAmount: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; + /** + * Allowlisted collection users. + **/ + allowlist: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; + /** + * Storage of collection info. + **/ + collectionById: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; + /** + * Storage of collection properties. + **/ + collectionProperties: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; + /** + * Storage of token property permissions of a collection. + **/ + collectionPropertyPermissions: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; + /** + * Storage of the count of created collections. Essentially contains the last collection ID. + **/ + createdCollectionCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Storage of the count of deleted collections. + **/ + destroyedCollectionCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Not used by code, exists only to provide some types to metadata. + **/ + dummyStorageValue: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * List of collection admins. + **/ + isAdmin: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + configuration: { + appPromomotionConfigurationOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; + collatorSelectionDesiredCollatorsOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; + collatorSelectionKickThresholdOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; + collatorSelectionLicenseBondOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; + minGasPriceOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; + weightToFeeCoefficientOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + council: { + /** + * The current members of the collective. This is stored sorted (just by value). + **/ + members: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The prime member that helps determine the default vote behavior in case of absentations. + **/ + prime: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Proposals so far. + **/ + proposalCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Actual proposal for a given hash, if it's current. + **/ + proposalOf: AugmentedQuery Observable>, [H256]> & QueryableStorageEntry; + /** + * The hashes of the active proposals. + **/ + proposals: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Votes on a given proposal, if it is ongoing. + **/ + voting: AugmentedQuery Observable>, [H256]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + councilMembership: { + /** + * The current membership, stored as an ordered Vec. + **/ + members: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The current prime member, if one exists. + **/ + prime: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + democracy: { + /** + * A record of who vetoed what. Maps proposal hash to a possible existent block number + * (until when it may not be resubmitted) and who vetoed it. + **/ + blacklist: AugmentedQuery Observable]>>>, [H256]> & QueryableStorageEntry; + /** + * Record of all proposals that have been subject to emergency cancellation. + **/ + cancellations: AugmentedQuery Observable, [H256]> & QueryableStorageEntry; + /** + * Those who have locked a deposit. + * + * TWOX-NOTE: Safe, as increasing integer keys are safe. + **/ + depositOf: AugmentedQuery Observable, u128]>>>, [u32]> & QueryableStorageEntry; + /** + * True if the last referendum tabled was submitted externally. False if it was a public + * proposal. + **/ + lastTabledWasExternal: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The lowest referendum index representing an unbaked referendum. Equal to + * `ReferendumCount` if there isn't a unbaked referendum. + **/ + lowestUnbaked: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * General information concerning any proposal or referendum. + * The `PreimageHash` refers to the preimage of the `Preimages` provider which can be a JSON + * dump or IPFS hash of a JSON file. + * + * Consider a garbage collection for a metadata of finished referendums to `unrequest` (remove) + * large preimages. + **/ + metadataOf: AugmentedQuery Observable>, [PalletDemocracyMetadataOwner]> & QueryableStorageEntry; + /** + * The referendum to be tabled whenever it would be valid to table an external proposal. + * This happens when a referendum needs to be tabled and one of two conditions are met: + * - `LastTabledWasExternal` is `false`; or + * - `PublicProps` is empty. + **/ + nextExternal: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * The number of (public) proposals that have been made so far. + **/ + publicPropCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The public proposals. Unsorted. The second item is the proposal. + **/ + publicProps: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * The next free referendum index, aka the number of referenda started so far. + **/ + referendumCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Information concerning any given referendum. + * + * TWOX-NOTE: SAFE as indexes are not under an attacker’s control. + **/ + referendumInfoOf: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; + /** + * All votes for a particular voter. We store the balance for the number of votes that we + * have recorded. The second item is the total amount of delegations, that will be added. + * + * TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway. + **/ + votingOf: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + dmpQueue: { + /** + * The configuration. + **/ + configuration: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Counter for the related counted storage map + **/ + counterForOverweight: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The overweight messages. + **/ + overweight: AugmentedQuery Observable>>, [u64]> & QueryableStorageEntry; + /** + * The page index. + **/ + pageIndex: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The queue pages. + **/ + pages: AugmentedQuery Observable>>, [u32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + ethereum: { + blockHash: AugmentedQuery Observable, [U256]> & QueryableStorageEntry; + /** + * The current Ethereum block. + **/ + currentBlock: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The current Ethereum receipts. + **/ + currentReceipts: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * The current transaction statuses. + **/ + currentTransactionStatuses: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * Injected transactions should have unique nonce, here we store current + **/ + injectedNonce: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Current building block's transactions and receipts. + **/ + pending: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + evm: { + accountCodes: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; + accountCodesMetadata: AugmentedQuery Observable>, [H160]> & QueryableStorageEntry; + accountStorages: AugmentedQuery Observable, [H160, H256]> & QueryableStorageEntry; + /** + * Written on log, reset after transaction + * Should be empty between transactions + **/ + currentLogs: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + evmContractHelpers: { + /** + * Storage for users that allowed for sponsorship. + * + * ### Usage + * Prefer to delete record from storage if user no more allowed for sponsorship. + * + * * **Key1** - contract address. + * * **Key2** - user that allowed for sponsorship. + * * **Value** - allowance for sponsorship. + **/ + allowlist: AugmentedQuery Observable, [H160, H160]> & QueryableStorageEntry; + /** + * Storege for contracts with [`Allowlisted`](SponsoringModeT::Allowlisted) sponsoring mode. + * + * ### Usage + * Prefer to delete collection from storage if mode chaged to non `Allowlisted`, than set **Value** to **false**. + * + * * **Key** - contract address. + * * **Value** - is contract in [`Allowlisted`](SponsoringModeT::Allowlisted) mode. + **/ + allowlistEnabled: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; + /** + * Store owner for contract. + * + * * **Key** - contract address. + * * **Value** - owner for contract. + **/ + owner: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; + /** + * Deprecated: this storage is deprecated + **/ + selfSponsoring: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; + sponsorBasket: AugmentedQuery Observable>, [H160, H160]> & QueryableStorageEntry; + /** + * Store for contract sponsorship state. + * + * * **Key** - contract address. + * * **Value** - sponsorship state. + **/ + sponsoring: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; + /** + * Storage for last sponsored block. + * + * * **Key1** - contract address. + * * **Key2** - sponsored user address. + * * **Value** - last sponsored block number. + **/ + sponsoringFeeLimit: AugmentedQuery Observable>, [H160]> & QueryableStorageEntry; + /** + * Store for sponsoring mode. + * + * ### Usage + * Prefer to delete collection from storage if mode chaged to [`Disabled`](SponsoringModeT::Disabled). + * + * * **Key** - contract address. + * * **Value** - [`sponsoring mode`](SponsoringModeT). + **/ + sponsoringMode: AugmentedQuery Observable>, [H160]> & QueryableStorageEntry; + /** + * Storage for sponsoring rate limit in blocks. + * + * * **Key** - contract address. + * * **Value** - amount of sponsored blocks. + **/ + sponsoringRateLimit: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + evmMigration: { + migrationPending: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + fellowshipCollective: { + /** + * The index of each ranks's member into the group of members who have at least that rank. + **/ + idToIndex: AugmentedQuery Observable>, [u16, AccountId32]> & QueryableStorageEntry; + /** + * The members in the collective by index. All indices in the range `0..MemberCount` will + * return `Some`, however a member's index is not guaranteed to remain unchanged over time. + **/ + indexToId: AugmentedQuery Observable>, [u16, u32]> & QueryableStorageEntry; + /** + * The number of members in the collective who have at least the rank according to the index + * of the vec. + **/ + memberCount: AugmentedQuery Observable, [u16]> & QueryableStorageEntry; + /** + * The current members of the collective. + **/ + members: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; + /** + * Votes on a given proposal, if it is ongoing. + **/ + voting: AugmentedQuery Observable>, [u32, AccountId32]> & QueryableStorageEntry; + votingCleanup: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + fellowshipReferenda: { + /** + * The number of referenda being decided currently. + **/ + decidingCount: AugmentedQuery Observable, [u16]> & QueryableStorageEntry; + /** + * The metadata is a general information concerning the referendum. + * The `PreimageHash` refers to the preimage of the `Preimages` provider which can be a JSON + * dump or IPFS hash of a JSON file. + * + * Consider a garbage collection for a metadata of finished referendums to `unrequest` (remove) + * large preimages. + **/ + metadataOf: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; + /** + * The next free referendum index, aka the number of referenda started so far. + **/ + referendumCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Information concerning any given referendum. + **/ + referendumInfoFor: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; + /** + * The sorted list of referenda ready to be decided but not yet being decided, ordered by + * conviction-weighted approvals. + * + * This should be empty if `DecidingCount` is less than `TrackInfo::max_deciding`. + **/ + trackQueue: AugmentedQuery Observable>>, [u16]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + foreignAssets: { + /** + * The storages for assets to fungible collection binding + * + **/ + assetBinding: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; + /** + * The storages for AssetMetadatas. + * + * AssetMetadatas: map AssetIds => Option + **/ + assetMetadatas: AugmentedQuery Observable>, [PalletForeignAssetsAssetId]> & QueryableStorageEntry; + /** + * The storages for MultiLocations. + * + * ForeignAssetLocations: map ForeignAssetId => Option + **/ + foreignAssetLocations: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; + /** + * The storages for CurrencyIds. + * + * LocationToCurrencyIds: map MultiLocation => Option + **/ + locationToCurrencyIds: AugmentedQuery Observable>, [StagingXcmV3MultiLocation]> & QueryableStorageEntry; + /** + * Next available Foreign AssetId ID. + * + * NextForeignAssetId: ForeignAssetId + **/ + nextForeignAssetId: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + fungible: { + /** + * Storage for assets delegated to a limited extent to other users. + **/ + allowance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; + /** + * Amount of tokens owned by an account inside a collection. + **/ + balance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; + /** + * Total amount of fungible tokens inside a collection. + **/ + totalSupply: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + identity: { + /** + * Information that is pertinent to identify the entity behind an account. + * + * TWOX-NOTE: OK ― `AccountId` is a secure hash. + **/ + identityOf: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; + /** + * The set of registrars. Not expected to get very big as can only be added through a + * special origin (likely a council motion). + * + * The index into this can be cast to `RegistrarIndex` to get a valid value. + **/ + registrars: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * Alternative "sub" identities of this account. + * + * The first item is the deposit, the second is a vector of the accounts. + * + * TWOX-NOTE: OK ― `AccountId` is a secure hash. + **/ + subsOf: AugmentedQuery Observable]>>, [AccountId32]> & QueryableStorageEntry; + /** + * The super-identity of an alternative "sub" identity together with its name, within that + * context. If the account is not some other account's sub-identity, then just `None`. + **/ + superOf: AugmentedQuery Observable>>, [AccountId32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + inflation: { + /** + * Current inflation for `InflationBlockInterval` number of blocks + **/ + blockInflation: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Next target (relay) block when inflation will be applied + **/ + nextInflationBlock: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Next target (relay) block when inflation is recalculated + **/ + nextRecalculationBlock: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Relay block when inflation has started + **/ + startBlock: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * starting year total issuance + **/ + startingYearTotalIssuance: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + maintenance: { + enabled: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + nonfungible: { + /** + * Amount of tokens owned by an account in a collection. + **/ + accountBalance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; + /** + * Allowance set by a token owner for another user to perform one of certain transactions on a token. + **/ + allowance: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; + /** + * Operator set by a wallet owner that could perform certain transactions on all tokens in the wallet. + **/ + collectionAllowance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; + /** + * Used to enumerate tokens owned by account. + **/ + owned: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry; + /** + * Custom data of a token that is serialized to bytes, + * primarily reserved for on-chain operations, + * normally obscured from the external users. + * + * Auxiliary properties are slightly different from + * usual [`TokenProperties`] due to an unlimited number + * and separately stored and written-to key-value pairs. + * + * Currently unused. + **/ + tokenAuxProperties: AugmentedQuery Observable>, [u32, u32, UpDataStructsPropertyScope, Bytes]> & QueryableStorageEntry; + /** + * Used to enumerate token's children. + **/ + tokenChildren: AugmentedQuery | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array]) => Observable, [u32, u32, ITuple<[u32, u32]>]> & QueryableStorageEntry]>; + /** + * Token data, used to partially describe a token. + **/ + tokenData: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; + /** + * Map of key-value pairs, describing the metadata of a token. + **/ + tokenProperties: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; + /** + * Amount of burnt tokens in a collection. + **/ + tokensBurnt: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; + /** + * Total amount of minted tokens in a collection. + **/ + tokensMinted: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + parachainInfo: { + parachainId: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + parachainSystem: { + /** + * Storage field that keeps track of bandwidth used by the unincluded segment along with the + * latest the latest HRMP watermark. Used for limiting the acceptance of new blocks with + * respect to relay chain constraints. + **/ + aggregatedUnincludedSegment: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The number of HRMP messages we observed in `on_initialize` and thus used that number for + * announcing the weight of `on_initialize` and `on_finalize`. + **/ + announcedHrmpMessagesPerCandidate: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The next authorized upgrade, if there is one. + **/ + authorizedUpgrade: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * A custom head data that should be returned as result of `validate_block`. + * + * See `Pallet::set_custom_validation_head_data` for more information. + **/ + customValidationHeadData: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Were the validation data set to notify the relay chain? + **/ + didSetValidationCode: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The parachain host configuration that was obtained from the relay parent. + * + * This field is meant to be updated each block with the validation data inherent. Therefore, + * before processing of the inherent, e.g. in `on_initialize` this data may be stale. + * + * This data is also absent from the genesis. + **/ + hostConfiguration: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * HRMP messages that were sent in a block. + * + * This will be cleared in `on_initialize` of each new block. + **/ + hrmpOutboundMessages: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * HRMP watermark that was set in a block. + * + * This will be cleared in `on_initialize` of each new block. + **/ + hrmpWatermark: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The last downward message queue chain head we have observed. + * + * This value is loaded before and saved after processing inbound downward messages carried + * by the system inherent. + **/ + lastDmqMqcHead: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The message queue chain heads we have observed per each channel incoming channel. + * + * This value is loaded before and saved after processing inbound downward messages carried + * by the system inherent. + **/ + lastHrmpMqcHeads: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The relay chain block number associated with the last parachain block. + **/ + lastRelayChainBlockNumber: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Validation code that is set by the parachain and is to be communicated to collator and + * consequently the relay-chain. + * + * This will be cleared in `on_initialize` of each new block if no other pallet already set + * the value. + **/ + newValidationCode: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Upward messages that are still pending and not yet send to the relay chain. + **/ + pendingUpwardMessages: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * In case of a scheduled upgrade, this storage field contains the validation code to be + * applied. + * + * As soon as the relay chain gives us the go-ahead signal, we will overwrite the + * [`:code`][sp_core::storage::well_known_keys::CODE] which will result the next block process + * with the new validation code. This concludes the upgrade process. + **/ + pendingValidationCode: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Number of downward messages processed in a block. + * + * This will be cleared in `on_initialize` of each new block. + **/ + processedDownwardMessages: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The state proof for the last relay parent block. + * + * This field is meant to be updated each block with the validation data inherent. Therefore, + * before processing of the inherent, e.g. in `on_initialize` this data may be stale. + * + * This data is also absent from the genesis. + **/ + relayStateProof: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The snapshot of some state related to messaging relevant to the current parachain as per + * the relay parent. + * + * This field is meant to be updated each block with the validation data inherent. Therefore, + * before processing of the inherent, e.g. in `on_initialize` this data may be stale. + * + * This data is also absent from the genesis. + **/ + relevantMessagingState: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The weight we reserve at the beginning of the block for processing DMP messages. This + * overrides the amount set in the Config trait. + **/ + reservedDmpWeightOverride: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The weight we reserve at the beginning of the block for processing XCMP messages. This + * overrides the amount set in the Config trait. + **/ + reservedXcmpWeightOverride: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Latest included block descendants the runtime accepted. In other words, these are + * ancestors of the currently executing block which have not been included in the observed + * relay-chain state. + * + * The segment length is limited by the capacity returned from the [`ConsensusHook`] configured + * in the pallet. + **/ + unincludedSegment: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Optional upgrade go-ahead signal from the relay-chain. + * + * This storage item is a mirror of the corresponding value for the current parachain from the + * relay-chain. This value is ephemeral which means it doesn't hit the storage. This value is + * set after the inherent. + **/ + upgradeGoAhead: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * An option which indicates if the relay-chain restricts signalling a validation code upgrade. + * In other words, if this is `Some` and [`NewValidationCode`] is `Some` then the produced + * candidate will be invalid. + * + * This storage item is a mirror of the corresponding value for the current parachain from the + * relay-chain. This value is ephemeral which means it doesn't hit the storage. This value is + * set after the inherent. + **/ + upgradeRestrictionSignal: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Upward messages that were sent in a block. + * + * This will be cleared in `on_initialize` of each new block. + **/ + upwardMessages: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The [`PersistedValidationData`] set for this block. + * This value is expected to be set only once per block and it's never stored + * in the trie. + **/ + validationData: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + polkadotXcm: { + /** + * The existing asset traps. + * + * Key is the blake2 256 hash of (origin, versioned `MultiAssets`) pair. Value is the number of + * times this pair has been trapped (usually just 1 if it exists at all). + **/ + assetTraps: AugmentedQuery Observable, [H256]> & QueryableStorageEntry; + /** + * The current migration's stage, if any. + **/ + currentMigration: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Fungible assets which we know are locked on this chain. + **/ + lockedFungibles: AugmentedQuery Observable>>>, [AccountId32]> & QueryableStorageEntry; + /** + * The ongoing queries. + **/ + queries: AugmentedQuery Observable>, [u64]> & QueryableStorageEntry; + /** + * The latest available query index. + **/ + queryCounter: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Fungible assets which we know are locked on a remote chain. + **/ + remoteLockedFungibles: AugmentedQuery Observable>, [u32, AccountId32, StagingXcmVersionedAssetId]> & QueryableStorageEntry; + /** + * Default version to encode XCM when latest version of destination is unknown. If `None`, + * then the destinations whose XCM version is unknown are considered unreachable. + **/ + safeXcmVersion: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The Latest versions that we know various locations support. + **/ + supportedVersion: AugmentedQuery Observable>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry; + /** + * Destinations whose latest XCM version we would like to know. Duplicates not allowed, and + * the `u32` counter is the number of times that a send to the destination has been attempted, + * which is used as a prioritization. + **/ + versionDiscoveryQueue: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * All locations that we have requested version notifications from. + **/ + versionNotifiers: AugmentedQuery Observable>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry; + /** + * The target locations that are subscribed to our version changes, as well as the most recent + * of our versions we informed them of. + **/ + versionNotifyTargets: AugmentedQuery Observable>>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry; + /** + * Global suspension state of the XCM executor. + **/ + xcmExecutionSuspended: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + preimage: { + preimageFor: AugmentedQuery | [H256 | string | Uint8Array, u32 | AnyNumber | Uint8Array]) => Observable>, [ITuple<[H256, u32]>]> & QueryableStorageEntry]>; + /** + * The request status of a given hash. + **/ + statusFor: AugmentedQuery Observable>, [H256]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + refungible: { + /** + * Amount of tokens (not pieces) partially owned by an account within a collection. + **/ + accountBalance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; + /** + * Allowance set by a token owner for another user to perform one of certain transactions on a number of pieces of a token. + **/ + allowance: AugmentedQuery Observable, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; + /** + * Amount of token pieces owned by account. + **/ + balance: AugmentedQuery Observable, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; + /** + * Spender set by a wallet owner that could perform certain transactions on all tokens in the wallet. + **/ + collectionAllowance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; + /** + * Used to enumerate tokens owned by account. + **/ + owned: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry; + /** + * Amount of pieces a refungible token is split into. + **/ + tokenProperties: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; + /** + * Amount of tokens burnt in a collection. + **/ + tokensBurnt: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; + /** + * Total amount of minted tokens in a collection. + **/ + tokensMinted: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; + /** + * Total amount of pieces for token + **/ + totalSupply: AugmentedQuery Observable, [u32, u32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + scheduler: { + /** + * Items to be executed, indexed by the block number that they should be executed on. + **/ + agenda: AugmentedQuery Observable>>, [u32]> & QueryableStorageEntry; + incompleteSince: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Lookup from a name to the block number and index of the task. + * + * For v3 -> v4 the previously unbounded identities are Blake2-256 hashed to form the v4 + * identities. + **/ + lookup: AugmentedQuery Observable>>, [U8aFixed]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + session: { + /** + * Current index of the session. + **/ + currentIndex: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Indices of disabled validators. + * + * The vec is always kept sorted so that we can find whether a given validator is + * disabled using binary search. It gets cleared when `on_session_ending` returns + * a new set of identities. + **/ + disabledValidators: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The owner of a key. The key is the `KeyTypeId` + the encoded key. + **/ + keyOwner: AugmentedQuery | [SpCoreCryptoKeyTypeId | string | Uint8Array, Bytes | string | Uint8Array]) => Observable>, [ITuple<[SpCoreCryptoKeyTypeId, Bytes]>]> & QueryableStorageEntry]>; + /** + * The next session keys for a validator. + **/ + nextKeys: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; + /** + * True if the underlying economic identities or weighting behind the validators + * has changed in the queued validator set. + **/ + queuedChanged: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The queued keys for the next session. When the next session begins, these keys + * will be used to determine the validator's session keys. + **/ + queuedKeys: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + /** + * The current set of validators. + **/ + validators: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + stateTrieMigration: { + /** + * The limits that are imposed on automatic migrations. + * + * If set to None, then no automatic migration happens. + **/ + autoLimits: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Migration progress. + * + * This stores the snapshot of the last migrated keys. It can be set into motion and move + * forward by any of the means provided by this pallet. + **/ + migrationProcess: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The maximum limits that the signed migration could use. + * + * If not set, no signed submission is allowed. + **/ + signedMigrationMaxLimits: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + sudo: { + /** + * The `AccountId` of the sudo key. + **/ + key: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + system: { + /** + * The full account information for a particular account ID. + **/ + account: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; + /** + * Total length (in bytes) for all extrinsics put together, for the current block. + **/ + allExtrinsicsLen: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Map of block numbers to block hashes. + **/ + blockHash: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; + /** + * The current weight for the block. + **/ + blockWeight: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Digest of the current block, also part of the block header. + **/ + digest: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The number of events in the `Events` list. + **/ + eventCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Events deposited for the current block. + * + * NOTE: The item is unbound and should therefore never be read on chain. + * It could otherwise inflate the PoV size of a block. + * + * Events have a large in-memory size. Box the events to not go out-of-memory + * just in case someone still reads them from within the runtime. + **/ + events: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Mapping between a topic (represented by T::Hash) and a vector of indexes + * of events in the `>` list. + * + * All topic vectors have deterministic storage locations depending on the topic. This + * allows light-clients to leverage the changes trie storage tracking mechanism and + * in case of changes fetch the list of events of interest. + * + * The value has the type `(BlockNumberFor, EventIndex)` because if we used only just + * the `EventIndex` then in case if the topic has the same contents on the next block + * no notification will be triggered thus the event might be lost. + **/ + eventTopics: AugmentedQuery Observable>>, [H256]> & QueryableStorageEntry; + /** + * The execution phase of the block. + **/ + executionPhase: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Total extrinsics count for the current block. + **/ + extrinsicCount: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Extrinsics data for the current block (maps an extrinsic's index to its data). + **/ + extrinsicData: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; + /** + * Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened. + **/ + lastRuntimeUpgrade: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The current block number being processed. Set by `execute_block`. + **/ + number: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Hash of the previous block. + **/ + parentHash: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * True if we have upgraded so that AccountInfo contains three types of `RefCount`. False + * (default) if not. + **/ + upgradedToTripleRefCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * True if we have upgraded so that `type RefCount` is `u32`. False (default) if not. + **/ + upgradedToU32RefCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + technicalCommittee: { + /** + * The current members of the collective. This is stored sorted (just by value). + **/ + members: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The prime member that helps determine the default vote behavior in case of absentations. + **/ + prime: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Proposals so far. + **/ + proposalCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Actual proposal for a given hash, if it's current. + **/ + proposalOf: AugmentedQuery Observable>, [H256]> & QueryableStorageEntry; + /** + * The hashes of the active proposals. + **/ + proposals: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Votes on a given proposal, if it is ongoing. + **/ + voting: AugmentedQuery Observable>, [H256]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + technicalCommitteeMembership: { + /** + * The current membership, stored as an ordered Vec. + **/ + members: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The current prime member, if one exists. + **/ + prime: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + testUtils: { + enabled: AugmentedQuery Observable, []> & QueryableStorageEntry; + testValue: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + timestamp: { + /** + * Did the timestamp get updated in this block? + **/ + didUpdate: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Current time for the current block. + **/ + now: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + tokens: { + /** + * The balance of a token type under an account. + * + * NOTE: If the total is ever zero, decrease account ref account. + * + * NOTE: This is only used in the case that this module is used to store + * balances. + **/ + accounts: AugmentedQuery Observable, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry; + /** + * Any liquidity locks of a token type under an account. + * NOTE: Should only be accessed when setting, changing and freeing a lock. + **/ + locks: AugmentedQuery Observable>, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry; + /** + * Named reserves on some account balances. + **/ + reserves: AugmentedQuery Observable>, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry; + /** + * The total issuance of a token type. + **/ + totalIssuance: AugmentedQuery Observable, [PalletForeignAssetsAssetId]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + transactionPayment: { + nextFeeMultiplier: AugmentedQuery Observable, []> & QueryableStorageEntry; + storageVersion: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + treasury: { + /** + * Proposal indices that have been approved but not yet awarded. + **/ + approvals: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The amount which has been reported as inactive to Currency. + **/ + deactivated: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Number of proposals that have been made. + **/ + proposalCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Proposals that have been made. + **/ + proposals: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + unique: { + /** + * Used for migrations + **/ + chainVersion: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * (Collection id (controlled?2), who created (real)) + * TODO: Off chain worker should remove from this map when collection gets removed + **/ + createItemBasket: AugmentedQuery | [u32 | AnyNumber | Uint8Array, AccountId32 | string | Uint8Array]) => Observable>, [ITuple<[u32, AccountId32]>]> & QueryableStorageEntry]>; + /** + * Last sponsoring of fungible tokens approval in a collection + **/ + fungibleApproveBasket: AugmentedQuery Observable>, [u32, AccountId32]> & QueryableStorageEntry; + /** + * Collection id (controlled?2), owning user (real) + **/ + fungibleTransferBasket: AugmentedQuery Observable>, [u32, AccountId32]> & QueryableStorageEntry; + /** + * Last sponsoring of NFT approval in a collection + **/ + nftApproveBasket: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; + /** + * Collection id (controlled?2), token id (controlled?2) + **/ + nftTransferBasket: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; + /** + * Last sponsoring of RFT approval in a collection + **/ + refungibleApproveBasket: AugmentedQuery Observable>, [u32, u32, AccountId32]> & QueryableStorageEntry; + /** + * Collection id (controlled?2), token id (controlled?2) + **/ + reFungibleTransferBasket: AugmentedQuery Observable>, [u32, u32, AccountId32]> & QueryableStorageEntry; + /** + * Last sponsoring of token property setting // todo:doc rephrase this and the following + **/ + tokenPropertyBasket: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + vesting: { + /** + * Vesting schedules of an account. + * + * VestingSchedules: map AccountId => Vec + **/ + vestingSchedules: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + xcmpQueue: { + /** + * Counter for the related counted storage map + **/ + counterForOverweight: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Inbound aggregate XCMP messages. It can only be one per ParaId/block. + **/ + inboundXcmpMessages: AugmentedQuery Observable, [u32, u32]> & QueryableStorageEntry; + /** + * Status of the inbound XCMP channels. + **/ + inboundXcmpStatus: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The messages outbound in a given XCMP channel. + **/ + outboundXcmpMessages: AugmentedQuery Observable, [u32, u16]> & QueryableStorageEntry; + /** + * The non-empty XCMP channels in order of becoming non-empty, and the index of the first + * and last outbound message. If the two indices are equal, then it indicates an empty + * queue and there must be a non-`Ok` `OutboundStatus`. We assume queues grow no greater + * than 65535 items. Queue indices for normal messages begin at one; zero is reserved in + * case of the need to send a high-priority signal message this block. + * The bool is true if there is a signal message waiting to be sent. + **/ + outboundXcmpStatus: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * The messages that exceeded max individual message weight budget. + * + * These message stay in this storage map until they are manually dispatched via + * `service_overweight`. + **/ + overweight: AugmentedQuery Observable>>, [u64]> & QueryableStorageEntry; + /** + * The number of overweight messages ever recorded in `Overweight`. Also doubles as the next + * available free overweight index. + **/ + overweightCount: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * The configuration which controls the dynamics of the outbound queue. + **/ + queueConfig: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Whether or not the XCMP queue is suspended from executing incoming XCMs or not. + **/ + queueSuspended: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Any signal messages waiting to be sent. + **/ + signalMessages: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; + } // AugmentedQueries +} // declare module --- /dev/null +++ b/js-packages/types/augment-api-rpc.ts @@ -0,0 +1,752 @@ +// Auto-generated via `yarn polkadot-types-from-chain`, do not edit +/* eslint-disable */ + +// import type lookup before we augment - in some environments +// this is required to allow for ambient/previous definitions +import '@polkadot/rpc-core/types/jsonrpc'; + +import type { PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCollectionLimits, UpDataStructsCollectionStats, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsRpcCollection, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo } from './default'; +import type { AugmentedRpc } from '@polkadot/rpc-core/types'; +import type { Metadata, StorageKey } from '@polkadot/types'; +import type { Bytes, HashMap, Json, Null, Option, Text, U256, U64, Vec, bool, f64, u128, u32, u64 } from '@polkadot/types-codec'; +import type { AnyNumber, Codec, ITuple } from '@polkadot/types-codec/types'; +import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author'; +import type { EpochAuthorship } from '@polkadot/types/interfaces/babe'; +import type { BeefySignedCommitment } from '@polkadot/types/interfaces/beefy'; +import type { BlockHash } from '@polkadot/types/interfaces/chain'; +import type { PrefixedStorageKey } from '@polkadot/types/interfaces/childstate'; +import type { AuthorityId } from '@polkadot/types/interfaces/consensus'; +import type { CodeUploadRequest, CodeUploadResult, ContractCallRequest, ContractExecResult, ContractInstantiateResult, InstantiateRequestV1 } from '@polkadot/types/interfaces/contracts'; +import type { BlockStats } from '@polkadot/types/interfaces/dev'; +import type { CreatedBlock } from '@polkadot/types/interfaces/engine'; +import type { EthAccount, EthCallRequest, EthFeeHistory, EthFilter, EthFilterChanges, EthLog, EthReceipt, EthRichBlock, EthSubKind, EthSubParams, EthSyncStatus, EthTransaction, EthTransactionRequest, EthWork } from '@polkadot/types/interfaces/eth'; +import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics'; +import type { EncodedFinalityProofs, JustificationNotification, ReportedRoundStates } from '@polkadot/types/interfaces/grandpa'; +import type { MmrHash, MmrLeafBatchProof } from '@polkadot/types/interfaces/mmr'; +import type { StorageKind } from '@polkadot/types/interfaces/offchain'; +import type { FeeDetails, RuntimeDispatchInfoV1 } from '@polkadot/types/interfaces/payment'; +import type { RpcMethods } from '@polkadot/types/interfaces/rpc'; +import type { AccountId, BlockNumber, H160, H256, H64, Hash, Header, Index, Justification, KeyValue, SignedBlock, StorageData } from '@polkadot/types/interfaces/runtime'; +import type { MigrationStatusResult, ReadProof, RuntimeVersion, TraceBlockResponse } from '@polkadot/types/interfaces/state'; +import type { ApplyExtrinsicResult, ChainProperties, ChainType, Health, NetworkState, NodeRole, PeerInfo, SyncState } from '@polkadot/types/interfaces/system'; +import type { IExtrinsic, Observable } from '@polkadot/types/types'; + +export type __AugmentedRpc = AugmentedRpc<() => unknown>; + +declare module '@polkadot/rpc-core/types/jsonrpc' { + interface RpcInterface { + appPromotion: { + /** + * Returns the total amount of unstaked tokens + **/ + pendingUnstake: AugmentedRpc<(staker?: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Returns the total amount of unstaked tokens per block + **/ + pendingUnstakePerBlock: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>>; + /** + * Returns the total amount of staked tokens + **/ + totalStaked: AugmentedRpc<(staker?: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Returns the total amount of staked tokens per block when staked + **/ + totalStakedPerBlock: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>>; + }; + author: { + /** + * Returns true if the keystore has private keys for the given public key and key type. + **/ + hasKey: AugmentedRpc<(publicKey: Bytes | string | Uint8Array, keyType: Text | string) => Observable>; + /** + * Returns true if the keystore has private keys for the given session public keys. + **/ + hasSessionKeys: AugmentedRpc<(sessionKeys: Bytes | string | Uint8Array) => Observable>; + /** + * Insert a key into the keystore. + **/ + insertKey: AugmentedRpc<(keyType: Text | string, suri: Text | string, publicKey: Bytes | string | Uint8Array) => Observable>; + /** + * Returns all pending extrinsics, potentially grouped by sender + **/ + pendingExtrinsics: AugmentedRpc<() => Observable>>; + /** + * Remove given extrinsic from the pool and temporarily ban it to prevent reimporting + **/ + removeExtrinsic: AugmentedRpc<(bytesOrHash: Vec | (ExtrinsicOrHash | { Hash: any } | { Extrinsic: any } | string | Uint8Array)[]) => Observable>>; + /** + * Generate new session keys and returns the corresponding public keys + **/ + rotateKeys: AugmentedRpc<() => Observable>; + /** + * Submit and subscribe to watch an extrinsic until unsubscribed + **/ + submitAndWatchExtrinsic: AugmentedRpc<(extrinsic: Extrinsic | IExtrinsic | string | Uint8Array) => Observable>; + /** + * Submit a fully formatted extrinsic for block inclusion + **/ + submitExtrinsic: AugmentedRpc<(extrinsic: Extrinsic | IExtrinsic | string | Uint8Array) => Observable>; + }; + babe: { + /** + * Returns data about which slots (primary or secondary) can be claimed in the current epoch with the keys in the keystore + **/ + epochAuthorship: AugmentedRpc<() => Observable>>; + }; + beefy: { + /** + * Returns hash of the latest BEEFY finalized block as seen by this client. + **/ + getFinalizedHead: AugmentedRpc<() => Observable>; + /** + * Returns the block most recently finalized by BEEFY, alongside side its justification. + **/ + subscribeJustifications: AugmentedRpc<() => Observable>; + }; + chain: { + /** + * Get header and body of a relay chain block + **/ + getBlock: AugmentedRpc<(hash?: BlockHash | string | Uint8Array) => Observable>; + /** + * Get the block hash for a specific block + **/ + getBlockHash: AugmentedRpc<(blockNumber?: BlockNumber | AnyNumber | Uint8Array) => Observable>; + /** + * Get hash of the last finalized block in the canon chain + **/ + getFinalizedHead: AugmentedRpc<() => Observable>; + /** + * Retrieves the header for a specific block + **/ + getHeader: AugmentedRpc<(hash?: BlockHash | string | Uint8Array) => Observable
>; + /** + * Retrieves the newest header via subscription + **/ + subscribeAllHeads: AugmentedRpc<() => Observable
>; + /** + * Retrieves the best finalized header via subscription + **/ + subscribeFinalizedHeads: AugmentedRpc<() => Observable
>; + /** + * Retrieves the best header via subscription + **/ + subscribeNewHeads: AugmentedRpc<() => Observable
>; + }; + childstate: { + /** + * Returns the keys with prefix from a child storage, leave empty to get all the keys + **/ + getKeys: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, prefix: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Returns the keys with prefix from a child storage with pagination support + **/ + getKeysPaged: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, prefix: StorageKey | string | Uint8Array | any, count: u32 | AnyNumber | Uint8Array, startKey?: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Returns a child storage entry at a specific block state + **/ + getStorage: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Returns child storage entries for multiple keys at a specific block state + **/ + getStorageEntries: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, keys: Vec | (StorageKey | string | Uint8Array | any)[], at?: Hash | string | Uint8Array) => Observable>>>; + /** + * Returns the hash of a child storage entry at a block state + **/ + getStorageHash: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Returns the size of a child storage entry at a block state + **/ + getStorageSize: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable>>; + }; + contracts: { + /** + * @deprecated Use the runtime interface `api.call.contractsApi.call` instead + * Executes a call to a contract + **/ + call: AugmentedRpc<(callRequest: ContractCallRequest | { origin?: any; dest?: any; value?: any; gasLimit?: any; storageDepositLimit?: any; inputData?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; + /** + * @deprecated Use the runtime interface `api.call.contractsApi.getStorage` instead + * Returns the value under a specified storage key in a contract + **/ + getStorage: AugmentedRpc<(address: AccountId | string | Uint8Array, key: H256 | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>>; + /** + * @deprecated Use the runtime interface `api.call.contractsApi.instantiate` instead + * Instantiate a new contract + **/ + instantiate: AugmentedRpc<(request: InstantiateRequestV1 | { origin?: any; value?: any; gasLimit?: any; code?: any; data?: any; salt?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; + /** + * @deprecated Not available in newer versions of the contracts interfaces + * Returns the projected time a given contract will be able to sustain paying its rent + **/ + rentProjection: AugmentedRpc<(address: AccountId | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>>; + /** + * @deprecated Use the runtime interface `api.call.contractsApi.uploadCode` instead + * Upload new code without instantiating a contract from it + **/ + uploadCode: AugmentedRpc<(uploadRequest: CodeUploadRequest | { origin?: any; code?: any; storageDepositLimit?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; + }; + dev: { + /** + * Reexecute the specified `block_hash` and gather statistics while doing so + **/ + getBlockStats: AugmentedRpc<(at: Hash | string | Uint8Array) => Observable>>; + }; + engine: { + /** + * Instructs the manual-seal authorship task to create a new block + **/ + createBlock: AugmentedRpc<(createEmpty: bool | boolean | Uint8Array, finalize: bool | boolean | Uint8Array, parentHash?: BlockHash | string | Uint8Array) => Observable>; + /** + * Instructs the manual-seal authorship task to finalize a block + **/ + finalizeBlock: AugmentedRpc<(hash: BlockHash | string | Uint8Array, justification?: Justification) => Observable>; + }; + eth: { + /** + * Returns accounts list. + **/ + accounts: AugmentedRpc<() => Observable>>; + /** + * Returns the blockNumber + **/ + blockNumber: AugmentedRpc<() => Observable>; + /** + * Call contract, returning the output data. + **/ + call: AugmentedRpc<(request: EthCallRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; + /** + * Returns the chain ID used for transaction signing at the current best block. None is returned if not available. + **/ + chainId: AugmentedRpc<() => Observable>; + /** + * Returns block author. + **/ + coinbase: AugmentedRpc<() => Observable>; + /** + * Estimate gas needed for execution of given contract. + **/ + estimateGas: AugmentedRpc<(request: EthCallRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; + /** + * Returns fee history for given block count & reward percentiles + **/ + feeHistory: AugmentedRpc<(blockCount: U256 | AnyNumber | Uint8Array, newestBlock: BlockNumber | AnyNumber | Uint8Array, rewardPercentiles: Option> | null | Uint8Array | Vec | (f64)[]) => Observable>; + /** + * Returns current gas price. + **/ + gasPrice: AugmentedRpc<() => Observable>; + /** + * Returns balance of the given account. + **/ + getBalance: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; + /** + * Returns block with given hash. + **/ + getBlockByHash: AugmentedRpc<(hash: H256 | string | Uint8Array, full: bool | boolean | Uint8Array) => Observable>>; + /** + * Returns block with given number. + **/ + getBlockByNumber: AugmentedRpc<(block: BlockNumber | AnyNumber | Uint8Array, full: bool | boolean | Uint8Array) => Observable>>; + /** + * Returns the number of transactions in a block with given hash. + **/ + getBlockTransactionCountByHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable>; + /** + * Returns the number of transactions in a block with given block number. + **/ + getBlockTransactionCountByNumber: AugmentedRpc<(block: BlockNumber | AnyNumber | Uint8Array) => Observable>; + /** + * Returns the code at given address at given time (block number). + **/ + getCode: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; + /** + * Returns filter changes since last poll. + **/ + getFilterChanges: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable>; + /** + * Returns all logs matching given filter (in a range 'from' - 'to'). + **/ + getFilterLogs: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable>>; + /** + * Returns logs matching given filter object. + **/ + getLogs: AugmentedRpc<(filter: EthFilter | { fromBlock?: any; toBlock?: any; blockHash?: any; address?: any; topics?: any } | string | Uint8Array) => Observable>>; + /** + * Returns proof for account and storage. + **/ + getProof: AugmentedRpc<(address: H160 | string | Uint8Array, storageKeys: Vec | (H256 | string | Uint8Array)[], number: BlockNumber | AnyNumber | Uint8Array) => Observable>; + /** + * Returns content of the storage at given address. + **/ + getStorageAt: AugmentedRpc<(address: H160 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; + /** + * Returns transaction at given block hash and index. + **/ + getTransactionByBlockHashAndIndex: AugmentedRpc<(hash: H256 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable>; + /** + * Returns transaction by given block number and index. + **/ + getTransactionByBlockNumberAndIndex: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable>; + /** + * Get transaction by its hash. + **/ + getTransactionByHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable>; + /** + * Returns the number of transactions sent from given address at given time (block number). + **/ + getTransactionCount: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; + /** + * Returns transaction receipt by transaction hash. + **/ + getTransactionReceipt: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable>; + /** + * Returns an uncles at given block and index. + **/ + getUncleByBlockHashAndIndex: AugmentedRpc<(hash: H256 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable>; + /** + * Returns an uncles at given block and index. + **/ + getUncleByBlockNumberAndIndex: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable>; + /** + * Returns the number of uncles in a block with given hash. + **/ + getUncleCountByBlockHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable>; + /** + * Returns the number of uncles in a block with given block number. + **/ + getUncleCountByBlockNumber: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array) => Observable>; + /** + * Returns the hash of the current block, the seedHash, and the boundary condition to be met. + **/ + getWork: AugmentedRpc<() => Observable>; + /** + * Returns the number of hashes per second that the node is mining with. + **/ + hashrate: AugmentedRpc<() => Observable>; + /** + * Returns max priority fee per gas + **/ + maxPriorityFeePerGas: AugmentedRpc<() => Observable>; + /** + * Returns true if client is actively mining new blocks. + **/ + mining: AugmentedRpc<() => Observable>; + /** + * Returns id of new block filter. + **/ + newBlockFilter: AugmentedRpc<() => Observable>; + /** + * Returns id of new filter. + **/ + newFilter: AugmentedRpc<(filter: EthFilter | { fromBlock?: any; toBlock?: any; blockHash?: any; address?: any; topics?: any } | string | Uint8Array) => Observable>; + /** + * Returns id of new block filter. + **/ + newPendingTransactionFilter: AugmentedRpc<() => Observable>; + /** + * Returns protocol version encoded as a string (quotes are necessary). + **/ + protocolVersion: AugmentedRpc<() => Observable>; + /** + * Sends signed transaction, returning its hash. + **/ + sendRawTransaction: AugmentedRpc<(bytes: Bytes | string | Uint8Array) => Observable>; + /** + * Sends transaction; will block waiting for signer to return the transaction hash + **/ + sendTransaction: AugmentedRpc<(tx: EthTransactionRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array) => Observable>; + /** + * Used for submitting mining hashrate. + **/ + submitHashrate: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array, hash: H256 | string | Uint8Array) => Observable>; + /** + * Used for submitting a proof-of-work solution. + **/ + submitWork: AugmentedRpc<(nonce: H64 | string | Uint8Array, headerHash: H256 | string | Uint8Array, mixDigest: H256 | string | Uint8Array) => Observable>; + /** + * Subscribe to Eth subscription. + **/ + subscribe: AugmentedRpc<(kind: EthSubKind | 'newHeads' | 'logs' | 'newPendingTransactions' | 'syncing' | number | Uint8Array, params?: EthSubParams | { None: any } | { Logs: any } | string | Uint8Array) => Observable>; + /** + * Returns an object with data about the sync status or false. + **/ + syncing: AugmentedRpc<() => Observable>; + /** + * Uninstalls filter. + **/ + uninstallFilter: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable>; + }; + grandpa: { + /** + * Prove finality for the given block number, returning the Justification for the last block in the set. + **/ + proveFinality: AugmentedRpc<(blockNumber: BlockNumber | AnyNumber | Uint8Array) => Observable>>; + /** + * Returns the state of the current best round state as well as the ongoing background rounds + **/ + roundState: AugmentedRpc<() => Observable>; + /** + * Subscribes to grandpa justifications + **/ + subscribeJustifications: AugmentedRpc<() => Observable>; + }; + mmr: { + /** + * Generate MMR proof for the given block numbers. + **/ + generateProof: AugmentedRpc<(blockNumbers: Vec | (u64 | AnyNumber | Uint8Array)[], bestKnownBlockNumber?: u64 | AnyNumber | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; + /** + * Get the MMR root hash for the current best block. + **/ + root: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable>; + /** + * Verify an MMR proof + **/ + verifyProof: AugmentedRpc<(proof: MmrLeafBatchProof | { blockHash?: any; leaves?: any; proof?: any } | string | Uint8Array) => Observable>; + /** + * Verify an MMR proof statelessly given an mmr_root + **/ + verifyProofStateless: AugmentedRpc<(root: MmrHash | string | Uint8Array, proof: MmrLeafBatchProof | { blockHash?: any; leaves?: any; proof?: any } | string | Uint8Array) => Observable>; + }; + net: { + /** + * Returns true if client is actively listening for network connections. Otherwise false. + **/ + listening: AugmentedRpc<() => Observable>; + /** + * Returns number of peers connected to node. + **/ + peerCount: AugmentedRpc<() => Observable>; + /** + * Returns protocol version. + **/ + version: AugmentedRpc<() => Observable>; + }; + offchain: { + /** + * Get offchain local storage under given key and prefix + **/ + localStorageGet: AugmentedRpc<(kind: StorageKind | 'PERSISTENT' | 'LOCAL' | number | Uint8Array, key: Bytes | string | Uint8Array) => Observable>>; + /** + * Set offchain local storage under given key and prefix + **/ + localStorageSet: AugmentedRpc<(kind: StorageKind | 'PERSISTENT' | 'LOCAL' | number | Uint8Array, key: Bytes | string | Uint8Array, value: Bytes | string | Uint8Array) => Observable>; + }; + payment: { + /** + * @deprecated Use `api.call.transactionPaymentApi.queryFeeDetails` instead + * Query the detailed fee of a given encoded extrinsic + **/ + queryFeeDetails: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; + /** + * @deprecated Use `api.call.transactionPaymentApi.queryInfo` instead + * Retrieves the fee information for an encoded extrinsic + **/ + queryInfo: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; + }; + povinfo: { + /** + * Estimate PoV size of encoded signed extrinsics + **/ + estimateExtrinsicPoV: AugmentedRpc<(encodedXt: Vec | (Bytes | string | Uint8Array)[], at?: Hash | string | Uint8Array) => Observable>; + }; + rpc: { + /** + * Retrieves the list of RPC methods that are exposed by the node + **/ + methods: AugmentedRpc<() => Observable>; + }; + state: { + /** + * Perform a call to a builtin on the chain + **/ + call: AugmentedRpc<(method: Text | string, data: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; + /** + * Retrieves the keys with prefix of a specific child storage + **/ + getChildKeys: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>>; + /** + * Returns proof of storage for child key entries at a specific block state. + **/ + getChildReadProof: AugmentedRpc<(childStorageKey: PrefixedStorageKey | string | Uint8Array, keys: Vec | (StorageKey | string | Uint8Array | any)[], at?: BlockHash | string | Uint8Array) => Observable>; + /** + * Retrieves the child storage for a key + **/ + getChildStorage: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>; + /** + * Retrieves the child storage hash + **/ + getChildStorageHash: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>; + /** + * Retrieves the child storage size + **/ + getChildStorageSize: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>; + /** + * @deprecated Use `api.rpc.state.getKeysPaged` to retrieve keys + * Retrieves the keys with a certain prefix + **/ + getKeys: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>>; + /** + * Returns the keys with prefix with pagination support. + **/ + getKeysPaged: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, count: u32 | AnyNumber | Uint8Array, startKey?: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>>; + /** + * Returns the runtime metadata + **/ + getMetadata: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable>; + /** + * @deprecated Use `api.rpc.state.getKeysPaged` to retrieve keys + * Returns the keys with prefix, leave empty to get all the keys (deprecated: Use getKeysPaged) + **/ + getPairs: AugmentedRpc<(prefix: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>>; + /** + * Returns proof of storage entries at a specific block state + **/ + getReadProof: AugmentedRpc<(keys: Vec | (StorageKey | string | Uint8Array | any)[], at?: BlockHash | string | Uint8Array) => Observable>; + /** + * Get the runtime version + **/ + getRuntimeVersion: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable>; + /** + * Retrieves the storage for a key + **/ + getStorage: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, block?: Hash | Uint8Array | string) => Observable>; + /** + * Retrieves the storage hash + **/ + getStorageHash: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>; + /** + * Retrieves the storage size + **/ + getStorageSize: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>; + /** + * Query historical storage entries (by key) starting from a start block + **/ + queryStorage: AugmentedRpc<(keys: Vec | (StorageKey | string | Uint8Array | any)[], fromBlock?: Hash | Uint8Array | string, toBlock?: Hash | Uint8Array | string) => Observable<[Hash, T][]>>; + /** + * Query storage entries (by key) starting at block hash given as the second parameter + **/ + queryStorageAt: AugmentedRpc<(keys: Vec | (StorageKey | string | Uint8Array | any)[], at?: Hash | Uint8Array | string) => Observable>; + /** + * Retrieves the runtime version via subscription + **/ + subscribeRuntimeVersion: AugmentedRpc<() => Observable>; + /** + * Subscribes to storage changes for the provided keys + **/ + subscribeStorage: AugmentedRpc<(keys?: Vec | (StorageKey | string | Uint8Array | any)[]) => Observable>; + /** + * Provides a way to trace the re-execution of a single block + **/ + traceBlock: AugmentedRpc<(block: Hash | string | Uint8Array, targets: Option | null | Uint8Array | Text | string, storageKeys: Option | null | Uint8Array | Text | string, methods: Option | null | Uint8Array | Text | string) => Observable>; + /** + * Check current migration state + **/ + trieMigrationStatus: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable>; + }; + syncstate: { + /** + * Returns the json-serialized chainspec running the node, with a sync state. + **/ + genSyncSpec: AugmentedRpc<(raw: bool | boolean | Uint8Array) => Observable>; + }; + system: { + /** + * Retrieves the next accountIndex as available on the node + **/ + accountNextIndex: AugmentedRpc<(accountId: AccountId | string | Uint8Array) => Observable>; + /** + * Adds the supplied directives to the current log filter + **/ + addLogFilter: AugmentedRpc<(directives: Text | string) => Observable>; + /** + * Adds a reserved peer + **/ + addReservedPeer: AugmentedRpc<(peer: Text | string) => Observable>; + /** + * Retrieves the chain + **/ + chain: AugmentedRpc<() => Observable>; + /** + * Retrieves the chain type + **/ + chainType: AugmentedRpc<() => Observable>; + /** + * Dry run an extrinsic at a given block + **/ + dryRun: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; + /** + * Return health status of the node + **/ + health: AugmentedRpc<() => Observable>; + /** + * The addresses include a trailing /p2p/ with the local PeerId, and are thus suitable to be passed to addReservedPeer or as a bootnode address for example + **/ + localListenAddresses: AugmentedRpc<() => Observable>>; + /** + * Returns the base58-encoded PeerId of the node + **/ + localPeerId: AugmentedRpc<() => Observable>; + /** + * Retrieves the node name + **/ + name: AugmentedRpc<() => Observable>; + /** + * Returns current state of the network + **/ + networkState: AugmentedRpc<() => Observable>; + /** + * Returns the roles the node is running as + **/ + nodeRoles: AugmentedRpc<() => Observable>>; + /** + * Returns the currently connected peers + **/ + peers: AugmentedRpc<() => Observable>>; + /** + * Get a custom set of properties as a JSON object, defined in the chain spec + **/ + properties: AugmentedRpc<() => Observable>; + /** + * Remove a reserved peer + **/ + removeReservedPeer: AugmentedRpc<(peerId: Text | string) => Observable>; + /** + * Returns the list of reserved peers + **/ + reservedPeers: AugmentedRpc<() => Observable>>; + /** + * Resets the log filter to Substrate defaults + **/ + resetLogFilter: AugmentedRpc<() => Observable>; + /** + * Returns the state of the syncing of the node + **/ + syncState: AugmentedRpc<() => Observable>; + /** + * Retrieves the version of the node + **/ + version: AugmentedRpc<() => Observable>; + }; + unique: { + /** + * Get the amount of any user tokens owned by an account + **/ + accountBalance: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Get tokens owned by an account in a collection + **/ + accountTokens: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get the list of admin accounts of a collection + **/ + adminlist: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get the amount of currently possible sponsored transactions on a token for the fee to be taken off a sponsor + **/ + allowance: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, sender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, spender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Tells whether the given `owner` approves the `operator`. + **/ + allowanceForAll: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, operator: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Check if a user is allowed to operate within a collection + **/ + allowed: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Get the list of accounts allowed to operate within a collection + **/ + allowlist: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get the amount of a specific token owned by an account + **/ + balance: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Get a collection by the specified ID + **/ + collectionById: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get collection properties, optionally limited to the provided keys + **/ + collectionProperties: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, propertyKeys?: Option> | null | Uint8Array | Vec | (Text | string)[], at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get chain stats about collections + **/ + collectionStats: AugmentedRpc<(at?: Hash | string | Uint8Array) => Observable>; + /** + * Get tokens contained within a collection + **/ + collectionTokens: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get token constant metadata + **/ + constMetadata: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Get effective collection limits + **/ + effectiveCollectionLimits: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get the last token ID created in a collection + **/ + lastTokenId: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Get the number of blocks until sponsoring a transaction is available + **/ + nextSponsored: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get property permissions, optionally limited to the provided keys + **/ + propertyPermissions: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, propertyKeys?: Option> | null | Uint8Array | Vec | (Text | string)[], at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get tokens nested directly into the token + **/ + tokenChildren: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get token data, including properties, optionally limited to the provided keys, and total pieces for an RFT + **/ + tokenData: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys?: Option> | null | Uint8Array | Vec | (Text | string)[], at?: Hash | string | Uint8Array) => Observable>; + /** + * Check if the token exists + **/ + tokenExists: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Get the token owner + **/ + tokenOwner: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Returns 10 tokens owners in no particular order + **/ + tokenOwners: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get token properties, optionally limited to the provided keys + **/ + tokenProperties: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys?: Option> | null | Uint8Array | Vec | (Text | string)[], at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get the topmost token owner in the hierarchy of a possibly nested token + **/ + topmostTokenOwner: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get the total amount of pieces of an RFT + **/ + totalPieces: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** + * Get the amount of distinctive tokens present in a collection + **/ + totalSupply: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Get token variable metadata + **/ + variableMetadata: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + }; + web3: { + /** + * Returns current client version. + **/ + clientVersion: AugmentedRpc<() => Observable>; + /** + * Returns sha3 of the given data + **/ + sha3: AugmentedRpc<(data: Bytes | string | Uint8Array) => Observable>; + }; + } // RpcInterface +} // declare module --- /dev/null +++ b/js-packages/types/augment-api-runtime.ts @@ -0,0 +1,264 @@ +// Auto-generated via `yarn polkadot-types-from-chain`, do not edit +/* eslint-disable */ + +// import type lookup before we augment - in some environments +// this is required to allow for ambient/previous definitions +import '@polkadot/api-base/types/calls'; + +import type { ApiTypes, AugmentedCall, DecoratedCallBase } from '@polkadot/api-base/types'; +import type { Bytes, Null, Option, Result, U256, Vec, bool, u256, u32, u64 } from '@polkadot/types-codec'; +import type { AnyNumber, ITuple } from '@polkadot/types-codec/types'; +import type { CheckInherentsResult, InherentData } from '@polkadot/types/interfaces/blockbuilder'; +import type { BlockHash } from '@polkadot/types/interfaces/chain'; +import type { AuthorityId } from '@polkadot/types/interfaces/consensus'; +import type { CollationInfo } from '@polkadot/types/interfaces/cumulus'; +import type { BlockV2, EthReceiptV3, EthTransactionStatus, TransactionV2 } from '@polkadot/types/interfaces/eth'; +import type { EvmAccount, EvmCallInfoV2, EvmCreateInfoV2 } from '@polkadot/types/interfaces/evm'; +import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics'; +import type { OpaqueMetadata } from '@polkadot/types/interfaces/metadata'; +import type { FeeDetails, RuntimeDispatchInfo } from '@polkadot/types/interfaces/payment'; +import type { AccountId, Balance, Block, H160, H256, Header, Index, KeyTypeId, Permill, SlotDuration, Weight } from '@polkadot/types/interfaces/runtime'; +import type { RuntimeVersion } from '@polkadot/types/interfaces/state'; +import type { ApplyExtrinsicResult, DispatchError } from '@polkadot/types/interfaces/system'; +import type { TransactionSource, TransactionValidity } from '@polkadot/types/interfaces/txqueue'; +import type { IExtrinsic, Observable } from '@polkadot/types/types'; + +export type __AugmentedCall = AugmentedCall; +export type __DecoratedCallBase = DecoratedCallBase; + +declare module '@polkadot/api-base/types/calls' { + interface AugmentedCalls { + /** 0xbc9d89904f5b923f/1 */ + accountNonceApi: { + /** + * The API to query account nonce (aka transaction index) + **/ + accountNonce: AugmentedCall Observable>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + /** 0xdd718d5cc53262d4/1 */ + auraApi: { + /** + * Return the current set of authorities. + **/ + authorities: AugmentedCall Observable>>; + /** + * Returns the slot duration for Aura. + **/ + slotDuration: AugmentedCall Observable>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + /** 0x40fe3ad401f8959a/6 */ + blockBuilder: { + /** + * Apply the given extrinsic. + **/ + applyExtrinsic: AugmentedCall Observable>; + /** + * Check that the inherents are valid. + **/ + checkInherents: AugmentedCall Observable>; + /** + * Finish the current block. + **/ + finalizeBlock: AugmentedCall Observable
>; + /** + * Generate inherent extrinsics. + **/ + inherentExtrinsics: AugmentedCall Observable>>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + /** 0xea93e3f16f3d6962/2 */ + collectCollationInfo: { + /** + * Collect information about a collation. + **/ + collectCollationInfo: AugmentedCall Observable>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + /** 0xe65b00e46cedd0aa/2 */ + convertTransactionRuntimeApi: { + /** + * Converts an Ethereum-style transaction to Extrinsic + **/ + convertTransaction: AugmentedCall Observable>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + /** 0xdf6acb689907609b/4 */ + core: { + /** + * Execute the given block. + **/ + executeBlock: AugmentedCall Observable>; + /** + * Initialize a block with the given header. + **/ + initializeBlock: AugmentedCall Observable>; + /** + * Returns the version of the runtime. + **/ + version: AugmentedCall Observable>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + /** 0x582211f65bb14b89/5 */ + ethereumRuntimeRPCApi: { + /** + * Returns pallet_evm::Accounts by address. + **/ + accountBasic: AugmentedCall Observable>; + /** + * For a given account address, returns pallet_evm::AccountCodes. + **/ + accountCodeAt: AugmentedCall Observable>; + /** + * Returns the converted FindAuthor::find_author authority id. + **/ + author: AugmentedCall Observable>; + /** + * Returns a frame_ethereum::call response. If `estimate` is true, + **/ + call: AugmentedCall | null | Uint8Array | U256 | AnyNumber, maxPriorityFeePerGas: Option | null | Uint8Array | U256 | AnyNumber, nonce: Option | null | Uint8Array | U256 | AnyNumber, estimate: bool | boolean | Uint8Array, accessList: Option]>>> | null | Uint8Array | Vec]>> | ([H160 | string | Uint8Array, Vec | (H256 | string | Uint8Array)[]])[]) => Observable>>; + /** + * Returns runtime defined pallet_evm::ChainId. + **/ + chainId: AugmentedCall Observable>; + /** + * Returns a frame_ethereum::call response. If `estimate` is true, + **/ + create: AugmentedCall | null | Uint8Array | U256 | AnyNumber, maxPriorityFeePerGas: Option | null | Uint8Array | U256 | AnyNumber, nonce: Option | null | Uint8Array | U256 | AnyNumber, estimate: bool | boolean | Uint8Array, accessList: Option]>>> | null | Uint8Array | Vec]>> | ([H160 | string | Uint8Array, Vec | (H256 | string | Uint8Array)[]])[]) => Observable>>; + /** + * Return all the current data for a block in a single runtime call. + **/ + currentAll: AugmentedCall Observable, Option>, Option>]>>>; + /** + * Return the current block. + **/ + currentBlock: AugmentedCall Observable>; + /** + * Return the current receipt. + **/ + currentReceipts: AugmentedCall Observable>>>; + /** + * Return the current transaction status. + **/ + currentTransactionStatuses: AugmentedCall Observable>>>; + /** + * Return the elasticity multiplier. + **/ + elasticity: AugmentedCall Observable>>; + /** + * Receives a `Vec` and filters all the ethereum transactions. + **/ + extrinsicFilter: AugmentedCall | (Extrinsic | IExtrinsic | string | Uint8Array)[]) => Observable>>; + /** + * Returns FixedGasPrice::min_gas_price + **/ + gasPrice: AugmentedCall Observable>; + /** + * For a given account address and index, returns pallet_evm::AccountStorages. + **/ + storageAt: AugmentedCall Observable>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + /** 0x37e397fc7c91f5e4/2 */ + metadata: { + /** + * Returns the metadata of a runtime + **/ + metadata: AugmentedCall Observable>; + /** + * Returns the metadata at a given version. + **/ + metadataAtVersion: AugmentedCall Observable>>; + /** + * Returns the supported metadata versions. + **/ + metadataVersions: AugmentedCall Observable>>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + /** 0xf78b278be53f454c/2 */ + offchainWorkerApi: { + /** + * Starts the off-chain task for given block header. + **/ + offchainWorker: AugmentedCall Observable>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + /** 0xab3c0572291feb8b/1 */ + sessionKeys: { + /** + * Decode the given public session keys. + **/ + decodeSessionKeys: AugmentedCall Observable>>>>; + /** + * Generate a set of session keys with optionally using the given seed. + **/ + generateSessionKeys: AugmentedCall | null | Uint8Array | Bytes | string) => Observable>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + /** 0xd2bc9897eed08f15/3 */ + taggedTransactionQueue: { + /** + * Validate the transaction. + **/ + validateTransaction: AugmentedCall Observable>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + /** 0x37c8bb1350a9a2a8/4 */ + transactionPaymentApi: { + /** + * The transaction fee details + **/ + queryFeeDetails: AugmentedCall Observable>; + /** + * The transaction info + **/ + queryInfo: AugmentedCall Observable>; + /** + * Query the output of the current LengthToFee given some input + **/ + queryLengthToFee: AugmentedCall Observable>; + /** + * Query the output of the current WeightToFee given some input + **/ + queryWeightToFee: AugmentedCall Observable>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; + } // AugmentedCalls +} // declare module --- /dev/null +++ b/js-packages/types/augment-api-tx.ts @@ -0,0 +1,1250 @@ +// Auto-generated via `yarn polkadot-types-from-chain`, do not edit +/* eslint-disable */ + +// import type lookup before we augment - in some environments +// this is required to allow for ambient/previous definitions +import '@polkadot/api-base/types/submittable'; + +import type { ApiTypes, AugmentedSubmittable, SubmittableExtrinsic, SubmittableExtrinsicFunction } from '@polkadot/api-base/types'; +import type { Data } from '@polkadot/types'; +import type { Bytes, Compact, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; +import type { AnyNumber, IMethod, ITuple } from '@polkadot/types-codec/types'; +import type { AccountId32, Call, H160, H256, MultiAddress } from '@polkadot/types/interfaces/runtime'; +import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumLog, EthereumTransactionTransactionV2, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime, OpalRuntimeOriginCaller, OpalRuntimeRuntimeCommonSessionKeys, OrmlVestingVestingSchedule, PalletConfigurationAppPromotionConfiguration, PalletDemocracyConviction, PalletDemocracyMetadataOwner, PalletDemocracyVoteAccountVote, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletIdentityBitFlags, PalletIdentityIdentityInfo, PalletIdentityJudgement, PalletIdentityRegistration, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletStateTrieMigrationProgress, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, StagingXcmV3WeightLimit, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation, StagingXcmVersionedXcm, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsProperty, UpDataStructsPropertyKeyPermission } from '@polkadot/types/lookup'; + +export type __AugmentedSubmittable = AugmentedSubmittable<() => unknown>; +export type __SubmittableExtrinsic = SubmittableExtrinsic; +export type __SubmittableExtrinsicFunction = SubmittableExtrinsicFunction; + +declare module '@polkadot/api-base/types/submittable' { + interface AugmentedSubmittables { + appPromotion: { + /** + * See [`Pallet::force_unstake`]. + **/ + forceUnstake: AugmentedSubmittable<(pendingBlocks: Vec | (u32 | AnyNumber | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * See [`Pallet::payout_stakers`]. + **/ + payoutStakers: AugmentedSubmittable<(stakersNumber: Option | null | Uint8Array | u8 | AnyNumber) => SubmittableExtrinsic, [Option]>; + /** + * See [`Pallet::set_admin_address`]. + **/ + setAdminAddress: AugmentedSubmittable<(admin: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr]>; + /** + * See [`Pallet::sponsor_collection`]. + **/ + sponsorCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::sponsor_contract`]. + **/ + sponsorContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic, [H160]>; + /** + * See [`Pallet::stake`]. + **/ + stake: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u128]>; + /** + * See [`Pallet::stop_sponsoring_collection`]. + **/ + stopSponsoringCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::stop_sponsoring_contract`]. + **/ + stopSponsoringContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic, [H160]>; + /** + * See [`Pallet::unstake_all`]. + **/ + unstakeAll: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::unstake_partial`]. + **/ + unstakePartial: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u128]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + balances: { + /** + * See [`Pallet::force_set_balance`]. + **/ + forceSetBalance: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, newFree: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Compact]>; + /** + * See [`Pallet::force_transfer`]. + **/ + forceTransfer: AugmentedSubmittable<(source: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, MultiAddress, Compact]>; + /** + * See [`Pallet::force_unreserve`]. + **/ + forceUnreserve: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, u128]>; + /** + * See [`Pallet::set_balance_deprecated`]. + **/ + setBalanceDeprecated: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, newFree: Compact | AnyNumber | Uint8Array, oldReserved: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Compact, Compact]>; + /** + * See [`Pallet::transfer`]. + **/ + transfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Compact]>; + /** + * See [`Pallet::transfer_all`]. + **/ + transferAll: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, keepAlive: bool | boolean | Uint8Array) => SubmittableExtrinsic, [MultiAddress, bool]>; + /** + * See [`Pallet::transfer_allow_death`]. + **/ + transferAllowDeath: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Compact]>; + /** + * See [`Pallet::transfer_keep_alive`]. + **/ + transferKeepAlive: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Compact]>; + /** + * See [`Pallet::upgrade_accounts`]. + **/ + upgradeAccounts: AugmentedSubmittable<(who: Vec | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + collatorSelection: { + /** + * See [`Pallet::add_invulnerable`]. + **/ + addInvulnerable: AugmentedSubmittable<(updated: AccountId32 | string | Uint8Array) => SubmittableExtrinsic, [AccountId32]>; + /** + * See [`Pallet::force_release_license`]. + **/ + forceReleaseLicense: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic, [AccountId32]>; + /** + * See [`Pallet::get_license`]. + **/ + getLicense: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::offboard`]. + **/ + offboard: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::onboard`]. + **/ + onboard: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::release_license`]. + **/ + releaseLicense: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::remove_invulnerable`]. + **/ + removeInvulnerable: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic, [AccountId32]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + configuration: { + /** + * See [`Pallet::set_app_promotion_configuration_override`]. + **/ + setAppPromotionConfigurationOverride: AugmentedSubmittable<(configuration: PalletConfigurationAppPromotionConfiguration | { recalculationInterval?: any; pendingInterval?: any; intervalIncome?: any; maxStakersPerCalculation?: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletConfigurationAppPromotionConfiguration]>; + /** + * See [`Pallet::set_collator_selection_desired_collators`]. + **/ + setCollatorSelectionDesiredCollators: AugmentedSubmittable<(max: Option | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic, [Option]>; + /** + * See [`Pallet::set_collator_selection_kick_threshold`]. + **/ + setCollatorSelectionKickThreshold: AugmentedSubmittable<(threshold: Option | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic, [Option]>; + /** + * See [`Pallet::set_collator_selection_license_bond`]. + **/ + setCollatorSelectionLicenseBond: AugmentedSubmittable<(amount: Option | null | Uint8Array | u128 | AnyNumber) => SubmittableExtrinsic, [Option]>; + /** + * See [`Pallet::set_min_gas_price_override`]. + **/ + setMinGasPriceOverride: AugmentedSubmittable<(coeff: Option | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic, [Option]>; + /** + * See [`Pallet::set_weight_to_fee_coefficient_override`]. + **/ + setWeightToFeeCoefficientOverride: AugmentedSubmittable<(coeff: Option | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic, [Option]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + council: { + /** + * See [`Pallet::close`]. + **/ + close: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, index: Compact | AnyNumber | Uint8Array, proposalWeightBound: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [H256, Compact, SpWeightsWeightV2Weight, Compact]>; + /** + * See [`Pallet::disapprove_proposal`]. + **/ + disapproveProposal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; + /** + * See [`Pallet::execute`]. + **/ + execute: AugmentedSubmittable<(proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Call, Compact]>; + /** + * See [`Pallet::propose`]. + **/ + propose: AugmentedSubmittable<(threshold: Compact | AnyNumber | Uint8Array, proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact, Call, Compact]>; + /** + * See [`Pallet::set_members`]. + **/ + setMembers: AugmentedSubmittable<(newMembers: Vec | (AccountId32 | string | Uint8Array)[], prime: Option | null | Uint8Array | AccountId32 | string, oldCount: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Vec, Option, u32]>; + /** + * See [`Pallet::vote`]. + **/ + vote: AugmentedSubmittable<(proposal: H256 | string | Uint8Array, index: Compact | AnyNumber | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic, [H256, Compact, bool]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + councilMembership: { + /** + * See [`Pallet::add_member`]. + **/ + addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::change_key`]. + **/ + changeKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::clear_prime`]. + **/ + clearPrime: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::remove_member`]. + **/ + removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::reset_members`]. + **/ + resetMembers: AugmentedSubmittable<(members: Vec | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * See [`Pallet::set_prime`]. + **/ + setPrime: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::swap_member`]. + **/ + swapMember: AugmentedSubmittable<(remove: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, add: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, MultiAddress]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + cumulusXcm: { + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + democracy: { + /** + * See [`Pallet::blacklist`]. + **/ + blacklist: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, maybeRefIndex: Option | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic, [H256, Option]>; + /** + * See [`Pallet::cancel_proposal`]. + **/ + cancelProposal: AugmentedSubmittable<(propIndex: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; + /** + * See [`Pallet::cancel_referendum`]. + **/ + cancelReferendum: AugmentedSubmittable<(refIndex: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; + /** + * See [`Pallet::clear_public_proposals`]. + **/ + clearPublicProposals: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::delegate`]. + **/ + delegate: AugmentedSubmittable<(to: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, conviction: PalletDemocracyConviction | 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x' | number | Uint8Array, balance: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletDemocracyConviction, u128]>; + /** + * See [`Pallet::emergency_cancel`]. + **/ + emergencyCancel: AugmentedSubmittable<(refIndex: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::external_propose`]. + **/ + externalPropose: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic, [FrameSupportPreimagesBounded]>; + /** + * See [`Pallet::external_propose_default`]. + **/ + externalProposeDefault: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic, [FrameSupportPreimagesBounded]>; + /** + * See [`Pallet::external_propose_majority`]. + **/ + externalProposeMajority: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic, [FrameSupportPreimagesBounded]>; + /** + * See [`Pallet::fast_track`]. + **/ + fastTrack: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, votingPeriod: u32 | AnyNumber | Uint8Array, delay: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [H256, u32, u32]>; + /** + * See [`Pallet::propose`]. + **/ + propose: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array, value: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [FrameSupportPreimagesBounded, Compact]>; + /** + * See [`Pallet::remove_other_vote`]. + **/ + removeOtherVote: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, u32]>; + /** + * See [`Pallet::remove_vote`]. + **/ + removeVote: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::second`]. + **/ + second: AugmentedSubmittable<(proposal: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; + /** + * See [`Pallet::set_metadata`]. + **/ + setMetadata: AugmentedSubmittable<(owner: PalletDemocracyMetadataOwner | { External: any } | { Proposal: any } | { Referendum: any } | string | Uint8Array, maybeHash: Option | null | Uint8Array | H256 | string) => SubmittableExtrinsic, [PalletDemocracyMetadataOwner, Option]>; + /** + * See [`Pallet::undelegate`]. + **/ + undelegate: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::unlock`]. + **/ + unlock: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::veto_external`]. + **/ + vetoExternal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; + /** + * See [`Pallet::vote`]. + **/ + vote: AugmentedSubmittable<(refIndex: Compact | AnyNumber | Uint8Array, vote: PalletDemocracyVoteAccountVote | { Standard: any } | { Split: any } | string | Uint8Array) => SubmittableExtrinsic, [Compact, PalletDemocracyVoteAccountVote]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + dmpQueue: { + /** + * See [`Pallet::service_overweight`]. + **/ + serviceOverweight: AugmentedSubmittable<(index: u64 | AnyNumber | Uint8Array, weightLimit: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [u64, SpWeightsWeightV2Weight]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + ethereum: { + /** + * See [`Pallet::transact`]. + **/ + transact: AugmentedSubmittable<(transaction: EthereumTransactionTransactionV2 | { Legacy: any } | { EIP2930: any } | { EIP1559: any } | string | Uint8Array) => SubmittableExtrinsic, [EthereumTransactionTransactionV2]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + evm: { + /** + * See [`Pallet::call`]. + **/ + call: AugmentedSubmittable<(source: H160 | string | Uint8Array, target: H160 | string | Uint8Array, input: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option | null | Uint8Array | U256 | AnyNumber, nonce: Option | null | Uint8Array | U256 | AnyNumber, accessList: Vec]>> | ([H160 | string | Uint8Array, Vec | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic, [H160, H160, Bytes, U256, u64, U256, Option, Option, Vec]>>]>; + /** + * See [`Pallet::create`]. + **/ + create: AugmentedSubmittable<(source: H160 | string | Uint8Array, init: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option | null | Uint8Array | U256 | AnyNumber, nonce: Option | null | Uint8Array | U256 | AnyNumber, accessList: Vec]>> | ([H160 | string | Uint8Array, Vec | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic, [H160, Bytes, U256, u64, U256, Option, Option, Vec]>>]>; + /** + * See [`Pallet::create2`]. + **/ + create2: AugmentedSubmittable<(source: H160 | string | Uint8Array, init: Bytes | string | Uint8Array, salt: H256 | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option | null | Uint8Array | U256 | AnyNumber, nonce: Option | null | Uint8Array | U256 | AnyNumber, accessList: Vec]>> | ([H160 | string | Uint8Array, Vec | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic, [H160, Bytes, H256, U256, u64, U256, Option, Option, Vec]>>]>; + /** + * See [`Pallet::withdraw`]. + **/ + withdraw: AugmentedSubmittable<(address: H160 | string | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [H160, u128]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + evmContractHelpers: { + /** + * See [`Pallet::migrate_from_self_sponsoring`]. + **/ + migrateFromSelfSponsoring: AugmentedSubmittable<(addresses: Vec | (H160 | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + evmMigration: { + /** + * See [`Pallet::begin`]. + **/ + begin: AugmentedSubmittable<(address: H160 | string | Uint8Array) => SubmittableExtrinsic, [H160]>; + /** + * See [`Pallet::finish`]. + **/ + finish: AugmentedSubmittable<(address: H160 | string | Uint8Array, code: Bytes | string | Uint8Array) => SubmittableExtrinsic, [H160, Bytes]>; + /** + * See [`Pallet::insert_eth_logs`]. + **/ + insertEthLogs: AugmentedSubmittable<(logs: Vec | (EthereumLog | { address?: any; topics?: any; data?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * See [`Pallet::insert_events`]. + **/ + insertEvents: AugmentedSubmittable<(events: Vec | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * See [`Pallet::remove_rmrk_data`]. + **/ + removeRmrkData: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::set_data`]. + **/ + setData: AugmentedSubmittable<(address: H160 | string | Uint8Array, data: Vec> | ([H256 | string | Uint8Array, H256 | string | Uint8Array])[]) => SubmittableExtrinsic, [H160, Vec>]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + fellowshipCollective: { + /** + * See [`Pallet::add_member`]. + **/ + addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::cleanup_poll`]. + **/ + cleanupPoll: AugmentedSubmittable<(pollIndex: u32 | AnyNumber | Uint8Array, max: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32]>; + /** + * See [`Pallet::demote_member`]. + **/ + demoteMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::promote_member`]. + **/ + promoteMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::remove_member`]. + **/ + removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, minRank: u16 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, u16]>; + /** + * See [`Pallet::vote`]. + **/ + vote: AugmentedSubmittable<(poll: u32 | AnyNumber | Uint8Array, aye: bool | boolean | Uint8Array) => SubmittableExtrinsic, [u32, bool]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + fellowshipReferenda: { + /** + * See [`Pallet::cancel`]. + **/ + cancel: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::kill`]. + **/ + kill: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::nudge_referendum`]. + **/ + nudgeReferendum: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::one_fewer_deciding`]. + **/ + oneFewerDeciding: AugmentedSubmittable<(track: u16 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u16]>; + /** + * See [`Pallet::place_decision_deposit`]. + **/ + placeDecisionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::refund_decision_deposit`]. + **/ + refundDecisionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::refund_submission_deposit`]. + **/ + refundSubmissionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::set_metadata`]. + **/ + setMetadata: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array, maybeHash: Option | null | Uint8Array | H256 | string) => SubmittableExtrinsic, [u32, Option]>; + /** + * See [`Pallet::submit`]. + **/ + submit: AugmentedSubmittable<(proposalOrigin: OpalRuntimeOriginCaller | { system: any } | { Void: any } | { Council: any } | { TechnicalCommittee: any } | { PolkadotXcm: any } | { CumulusXcm: any } | { Origins: any } | { Ethereum: any } | string | Uint8Array, proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array, enactmentMoment: FrameSupportScheduleDispatchTime | { At: any } | { After: any } | string | Uint8Array) => SubmittableExtrinsic, [OpalRuntimeOriginCaller, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + foreignAssets: { + /** + * See [`Pallet::register_foreign_asset`]. + **/ + registerForeignAsset: AugmentedSubmittable<(owner: AccountId32 | string | Uint8Array, location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, metadata: PalletForeignAssetsModuleAssetMetadata | { name?: any; symbol?: any; decimals?: any; minimalBalance?: any } | string | Uint8Array) => SubmittableExtrinsic, [AccountId32, StagingXcmVersionedMultiLocation, PalletForeignAssetsModuleAssetMetadata]>; + /** + * See [`Pallet::update_foreign_asset`]. + **/ + updateForeignAsset: AugmentedSubmittable<(foreignAssetId: u32 | AnyNumber | Uint8Array, location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, metadata: PalletForeignAssetsModuleAssetMetadata | { name?: any; symbol?: any; decimals?: any; minimalBalance?: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, StagingXcmVersionedMultiLocation, PalletForeignAssetsModuleAssetMetadata]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + identity: { + /** + * See [`Pallet::add_registrar`]. + **/ + addRegistrar: AugmentedSubmittable<(account: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::add_sub`]. + **/ + addSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, data: Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Data]>; + /** + * See [`Pallet::cancel_request`]. + **/ + cancelRequest: AugmentedSubmittable<(regIndex: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::clear_identity`]. + **/ + clearIdentity: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::force_insert_identities`]. + **/ + forceInsertIdentities: AugmentedSubmittable<(identities: Vec> | ([AccountId32 | string | Uint8Array, PalletIdentityRegistration | { judgements?: any; deposit?: any; info?: any } | string | Uint8Array])[]) => SubmittableExtrinsic, [Vec>]>; + /** + * See [`Pallet::force_remove_identities`]. + **/ + forceRemoveIdentities: AugmentedSubmittable<(identities: Vec | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * See [`Pallet::force_set_subs`]. + **/ + forceSetSubs: AugmentedSubmittable<(subs: Vec>]>]>> | ([AccountId32 | string | Uint8Array, ITuple<[u128, Vec>]> | [u128 | AnyNumber | Uint8Array, Vec> | ([AccountId32 | string | Uint8Array, Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array])[]]])[]) => SubmittableExtrinsic, [Vec>]>]>>]>; + /** + * See [`Pallet::kill_identity`]. + **/ + killIdentity: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::provide_judgement`]. + **/ + provideJudgement: AugmentedSubmittable<(regIndex: Compact | AnyNumber | Uint8Array, target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, judgement: PalletIdentityJudgement | { Unknown: any } | { FeePaid: any } | { Reasonable: any } | { KnownGood: any } | { OutOfDate: any } | { LowQuality: any } | { Erroneous: any } | string | Uint8Array, identity: H256 | string | Uint8Array) => SubmittableExtrinsic, [Compact, MultiAddress, PalletIdentityJudgement, H256]>; + /** + * See [`Pallet::quit_sub`]. + **/ + quitSub: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::remove_sub`]. + **/ + removeSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::rename_sub`]. + **/ + renameSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, data: Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Data]>; + /** + * See [`Pallet::request_judgement`]. + **/ + requestJudgement: AugmentedSubmittable<(regIndex: Compact | AnyNumber | Uint8Array, maxFee: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact, Compact]>; + /** + * See [`Pallet::set_account_id`]. + **/ + setAccountId: AugmentedSubmittable<(index: Compact | AnyNumber | Uint8Array, updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [Compact, MultiAddress]>; + /** + * See [`Pallet::set_fee`]. + **/ + setFee: AugmentedSubmittable<(index: Compact | AnyNumber | Uint8Array, fee: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact, Compact]>; + /** + * See [`Pallet::set_fields`]. + **/ + setFields: AugmentedSubmittable<(index: Compact | AnyNumber | Uint8Array, fields: PalletIdentityBitFlags) => SubmittableExtrinsic, [Compact, PalletIdentityBitFlags]>; + /** + * See [`Pallet::set_identity`]. + **/ + setIdentity: AugmentedSubmittable<(info: PalletIdentityIdentityInfo | { additional?: any; display?: any; legal?: any; web?: any; riot?: any; email?: any; pgpFingerprint?: any; image?: any; twitter?: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletIdentityIdentityInfo]>; + /** + * See [`Pallet::set_subs`]. + **/ + setSubs: AugmentedSubmittable<(subs: Vec> | ([AccountId32 | string | Uint8Array, Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array])[]) => SubmittableExtrinsic, [Vec>]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + inflation: { + /** + * See [`Pallet::start_inflation`]. + **/ + startInflation: AugmentedSubmittable<(inflationStartRelayBlock: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + maintenance: { + /** + * See [`Pallet::disable`]. + **/ + disable: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::enable`]. + **/ + enable: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + parachainInfo: { + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + parachainSystem: { + /** + * See [`Pallet::authorize_upgrade`]. + **/ + authorizeUpgrade: AugmentedSubmittable<(codeHash: H256 | string | Uint8Array, checkVersion: bool | boolean | Uint8Array) => SubmittableExtrinsic, [H256, bool]>; + /** + * See [`Pallet::enact_authorized_upgrade`]. + **/ + enactAuthorizedUpgrade: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; + /** + * See [`Pallet::set_validation_data`]. + **/ + setValidationData: AugmentedSubmittable<(data: CumulusPrimitivesParachainInherentParachainInherentData | { validationData?: any; relayChainState?: any; downwardMessages?: any; horizontalMessages?: any } | string | Uint8Array) => SubmittableExtrinsic, [CumulusPrimitivesParachainInherentParachainInherentData]>; + /** + * See [`Pallet::sudo_send_upward_message`]. + **/ + sudoSendUpwardMessage: AugmentedSubmittable<(message: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + polkadotXcm: { + /** + * See [`Pallet::execute`]. + **/ + execute: AugmentedSubmittable<(message: StagingXcmVersionedXcm | { V2: any } | { V3: any } | string | Uint8Array, maxWeight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedXcm, SpWeightsWeightV2Weight]>; + /** + * See [`Pallet::force_default_xcm_version`]. + **/ + forceDefaultXcmVersion: AugmentedSubmittable<(maybeXcmVersion: Option | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic, [Option]>; + /** + * See [`Pallet::force_subscribe_version_notify`]. + **/ + forceSubscribeVersionNotify: AugmentedSubmittable<(location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation]>; + /** + * See [`Pallet::force_suspension`]. + **/ + forceSuspension: AugmentedSubmittable<(suspended: bool | boolean | Uint8Array) => SubmittableExtrinsic, [bool]>; + /** + * See [`Pallet::force_unsubscribe_version_notify`]. + **/ + forceUnsubscribeVersionNotify: AugmentedSubmittable<(location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation]>; + /** + * See [`Pallet::force_xcm_version`]. + **/ + forceXcmVersion: AugmentedSubmittable<(location: StagingXcmV3MultiLocation | { parents?: any; interior?: any } | string | Uint8Array, version: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [StagingXcmV3MultiLocation, u32]>; + /** + * See [`Pallet::limited_reserve_transfer_assets`]. + **/ + limitedReserveTransferAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array, weightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32, StagingXcmV3WeightLimit]>; + /** + * See [`Pallet::limited_teleport_assets`]. + **/ + limitedTeleportAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array, weightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32, StagingXcmV3WeightLimit]>; + /** + * See [`Pallet::reserve_transfer_assets`]. + **/ + reserveTransferAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32]>; + /** + * See [`Pallet::send`]. + **/ + send: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, message: StagingXcmVersionedXcm | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation, StagingXcmVersionedXcm]>; + /** + * See [`Pallet::teleport_assets`]. + **/ + teleportAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + preimage: { + /** + * See [`Pallet::note_preimage`]. + **/ + notePreimage: AugmentedSubmittable<(bytes: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; + /** + * See [`Pallet::request_preimage`]. + **/ + requestPreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; + /** + * See [`Pallet::unnote_preimage`]. + **/ + unnotePreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; + /** + * See [`Pallet::unrequest_preimage`]. + **/ + unrequestPreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + scheduler: { + /** + * See [`Pallet::cancel`]. + **/ + cancel: AugmentedSubmittable<(when: u32 | AnyNumber | Uint8Array, index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32]>; + /** + * See [`Pallet::cancel_named`]. + **/ + cancelNamed: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array) => SubmittableExtrinsic, [U8aFixed]>; + /** + * See [`Pallet::schedule`]. + **/ + schedule: AugmentedSubmittable<(when: u32 | AnyNumber | Uint8Array, maybePeriodic: Option> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [u32, Option>, u8, Call]>; + /** + * See [`Pallet::schedule_after`]. + **/ + scheduleAfter: AugmentedSubmittable<(after: u32 | AnyNumber | Uint8Array, maybePeriodic: Option> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [u32, Option>, u8, Call]>; + /** + * See [`Pallet::schedule_named`]. + **/ + scheduleNamed: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array, when: u32 | AnyNumber | Uint8Array, maybePeriodic: Option> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [U8aFixed, u32, Option>, u8, Call]>; + /** + * See [`Pallet::schedule_named_after`]. + **/ + scheduleNamedAfter: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array, after: u32 | AnyNumber | Uint8Array, maybePeriodic: Option> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [U8aFixed, u32, Option>, u8, Call]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + session: { + /** + * See [`Pallet::purge_keys`]. + **/ + purgeKeys: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::set_keys`]. + **/ + setKeys: AugmentedSubmittable<(keys: OpalRuntimeRuntimeCommonSessionKeys | { aura?: any } | string | Uint8Array, proof: Bytes | string | Uint8Array) => SubmittableExtrinsic, [OpalRuntimeRuntimeCommonSessionKeys, Bytes]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + stateTrieMigration: { + /** + * See [`Pallet::continue_migrate`]. + **/ + continueMigrate: AugmentedSubmittable<(limits: PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string | Uint8Array, realSizeUpper: u32 | AnyNumber | Uint8Array, witnessTask: PalletStateTrieMigrationMigrationTask | { progressTop?: any; progressChild?: any; size_?: any; topItems?: any; childItems?: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletStateTrieMigrationMigrationLimits, u32, PalletStateTrieMigrationMigrationTask]>; + /** + * See [`Pallet::control_auto_migration`]. + **/ + controlAutoMigration: AugmentedSubmittable<(maybeConfig: Option | null | Uint8Array | PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string) => SubmittableExtrinsic, [Option]>; + /** + * See [`Pallet::force_set_progress`]. + **/ + forceSetProgress: AugmentedSubmittable<(progressTop: PalletStateTrieMigrationProgress | { ToStart: any } | { LastKey: any } | { Complete: any } | string | Uint8Array, progressChild: PalletStateTrieMigrationProgress | { ToStart: any } | { LastKey: any } | { Complete: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletStateTrieMigrationProgress, PalletStateTrieMigrationProgress]>; + /** + * See [`Pallet::migrate_custom_child`]. + **/ + migrateCustomChild: AugmentedSubmittable<(root: Bytes | string | Uint8Array, childKeys: Vec | (Bytes | string | Uint8Array)[], totalSize: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Bytes, Vec, u32]>; + /** + * See [`Pallet::migrate_custom_top`]. + **/ + migrateCustomTop: AugmentedSubmittable<(keys: Vec | (Bytes | string | Uint8Array)[], witnessSize: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Vec, u32]>; + /** + * See [`Pallet::set_signed_max_limits`]. + **/ + setSignedMaxLimits: AugmentedSubmittable<(limits: PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletStateTrieMigrationMigrationLimits]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + structure: { + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + sudo: { + /** + * See [`Pallet::set_key`]. + **/ + setKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::sudo`]. + **/ + sudo: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [Call]>; + /** + * See [`Pallet::sudo_as`]. + **/ + sudoAs: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Call]>; + /** + * See [`Pallet::sudo_unchecked_weight`]. + **/ + sudoUncheckedWeight: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array, weight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [Call, SpWeightsWeightV2Weight]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + system: { + /** + * See [`Pallet::kill_prefix`]. + **/ + killPrefix: AugmentedSubmittable<(prefix: Bytes | string | Uint8Array, subkeys: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Bytes, u32]>; + /** + * See [`Pallet::kill_storage`]. + **/ + killStorage: AugmentedSubmittable<(keys: Vec | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * See [`Pallet::remark`]. + **/ + remark: AugmentedSubmittable<(remark: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; + /** + * See [`Pallet::remark_with_event`]. + **/ + remarkWithEvent: AugmentedSubmittable<(remark: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; + /** + * See [`Pallet::set_code`]. + **/ + setCode: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; + /** + * See [`Pallet::set_code_without_checks`]. + **/ + setCodeWithoutChecks: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; + /** + * See [`Pallet::set_heap_pages`]. + **/ + setHeapPages: AugmentedSubmittable<(pages: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u64]>; + /** + * See [`Pallet::set_storage`]. + **/ + setStorage: AugmentedSubmittable<(items: Vec> | ([Bytes | string | Uint8Array, Bytes | string | Uint8Array])[]) => SubmittableExtrinsic, [Vec>]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + technicalCommittee: { + /** + * See [`Pallet::close`]. + **/ + close: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, index: Compact | AnyNumber | Uint8Array, proposalWeightBound: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [H256, Compact, SpWeightsWeightV2Weight, Compact]>; + /** + * See [`Pallet::disapprove_proposal`]. + **/ + disapproveProposal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; + /** + * See [`Pallet::execute`]. + **/ + execute: AugmentedSubmittable<(proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Call, Compact]>; + /** + * See [`Pallet::propose`]. + **/ + propose: AugmentedSubmittable<(threshold: Compact | AnyNumber | Uint8Array, proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact, Call, Compact]>; + /** + * See [`Pallet::set_members`]. + **/ + setMembers: AugmentedSubmittable<(newMembers: Vec | (AccountId32 | string | Uint8Array)[], prime: Option | null | Uint8Array | AccountId32 | string, oldCount: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Vec, Option, u32]>; + /** + * See [`Pallet::vote`]. + **/ + vote: AugmentedSubmittable<(proposal: H256 | string | Uint8Array, index: Compact | AnyNumber | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic, [H256, Compact, bool]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + technicalCommitteeMembership: { + /** + * See [`Pallet::add_member`]. + **/ + addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::change_key`]. + **/ + changeKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::clear_prime`]. + **/ + clearPrime: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::remove_member`]. + **/ + removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::reset_members`]. + **/ + resetMembers: AugmentedSubmittable<(members: Vec | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * See [`Pallet::set_prime`]. + **/ + setPrime: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::swap_member`]. + **/ + swapMember: AugmentedSubmittable<(remove: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, add: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, MultiAddress]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + testUtils: { + /** + * See `Pallet::batch_all`. + **/ + batchAll: AugmentedSubmittable<(calls: Vec | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * See `Pallet::enable`. + **/ + enable: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See `Pallet::inc_test_value`. + **/ + incTestValue: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See `Pallet::just_take_fee`. + **/ + justTakeFee: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See `Pallet::set_test_value`. + **/ + setTestValue: AugmentedSubmittable<(value: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See `Pallet::set_test_value_and_rollback`. + **/ + setTestValueAndRollback: AugmentedSubmittable<(value: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + timestamp: { + /** + * See [`Pallet::set`]. + **/ + set: AugmentedSubmittable<(now: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + tokens: { + /** + * See [`Pallet::force_transfer`]. + **/ + forceTransfer: AugmentedSubmittable<(source: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, MultiAddress, PalletForeignAssetsAssetId, Compact]>; + /** + * See [`Pallet::set_balance`]. + **/ + setBalance: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, newFree: Compact | AnyNumber | Uint8Array, newReserved: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetId, Compact, Compact]>; + /** + * See [`Pallet::transfer`]. + **/ + transfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetId, Compact]>; + /** + * See [`Pallet::transfer_all`]. + **/ + transferAll: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, keepAlive: bool | boolean | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetId, bool]>; + /** + * See [`Pallet::transfer_keep_alive`]. + **/ + transferKeepAlive: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetId, Compact]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + treasury: { + /** + * See [`Pallet::approve_proposal`]. + **/ + approveProposal: AugmentedSubmittable<(proposalId: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; + /** + * See [`Pallet::propose_spend`]. + **/ + proposeSpend: AugmentedSubmittable<(value: Compact | AnyNumber | Uint8Array, beneficiary: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [Compact, MultiAddress]>; + /** + * See [`Pallet::reject_proposal`]. + **/ + rejectProposal: AugmentedSubmittable<(proposalId: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; + /** + * See [`Pallet::remove_approval`]. + **/ + removeApproval: AugmentedSubmittable<(proposalId: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; + /** + * See [`Pallet::spend`]. + **/ + spend: AugmentedSubmittable<(amount: Compact | AnyNumber | Uint8Array, beneficiary: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [Compact, MultiAddress]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + unique: { + /** + * See [`Pallet::add_collection_admin`]. + **/ + addCollectionAdmin: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newAdminId: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + /** + * See [`Pallet::add_to_allow_list`]. + **/ + addToAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + /** + * See [`Pallet::approve`]. + **/ + approve: AugmentedSubmittable<(spender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>; + /** + * See [`Pallet::approve_from`]. + **/ + approveFrom: AugmentedSubmittable<(from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, to: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>; + /** + * See [`Pallet::burn_from`]. + **/ + burnFrom: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32, u128]>; + /** + * See [`Pallet::burn_item`]. + **/ + burnItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32, u128]>; + /** + * See [`Pallet::change_collection_owner`]. + **/ + changeCollectionOwner: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newOwner: AccountId32 | string | Uint8Array) => SubmittableExtrinsic, [u32, AccountId32]>; + /** + * See [`Pallet::confirm_sponsorship`]. + **/ + confirmSponsorship: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::create_collection`]. + **/ + createCollection: AugmentedSubmittable<(collectionName: Vec | (u16 | AnyNumber | Uint8Array)[], collectionDescription: Vec | (u16 | AnyNumber | Uint8Array)[], tokenPrefix: Bytes | string | Uint8Array, mode: UpDataStructsCollectionMode | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic, [Vec, Vec, Bytes, UpDataStructsCollectionMode]>; + /** + * See [`Pallet::create_collection_ex`]. + **/ + createCollectionEx: AugmentedSubmittable<(data: UpDataStructsCreateCollectionData | { mode?: any; access?: any; name?: any; description?: any; tokenPrefix?: any; limits?: any; permissions?: any; tokenPropertyPermissions?: any; properties?: any; adminList?: any; pendingSponsor?: any; flags?: any } | string | Uint8Array) => SubmittableExtrinsic, [UpDataStructsCreateCollectionData]>; + /** + * See [`Pallet::create_item`]. + **/ + createItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, data: UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCreateItemData]>; + /** + * See [`Pallet::create_multiple_items`]. + **/ + createMultipleItems: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemsData: Vec | (UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr, Vec]>; + /** + * See [`Pallet::create_multiple_items_ex`]. + **/ + createMultipleItemsEx: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, data: UpDataStructsCreateItemExData | { NFT: any } | { Fungible: any } | { RefungibleMultipleItems: any } | { RefungibleMultipleOwners: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, UpDataStructsCreateItemExData]>; + /** + * See [`Pallet::delete_collection_properties`]. + **/ + deleteCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, Vec]>; + /** + * See [`Pallet::delete_token_properties`]. + **/ + deleteTokenProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, u32, Vec]>; + /** + * See [`Pallet::destroy_collection`]. + **/ + destroyCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::force_repair_collection`]. + **/ + forceRepairCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::force_repair_item`]. + **/ + forceRepairItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32]>; + /** + * See [`Pallet::remove_collection_admin`]. + **/ + removeCollectionAdmin: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, accountId: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + /** + * See [`Pallet::remove_collection_sponsor`]. + **/ + removeCollectionSponsor: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::remove_from_allow_list`]. + **/ + removeFromAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + /** + * See [`Pallet::repartition`]. + **/ + repartition: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32, u128]>; + /** + * See [`Pallet::set_allowance_for_all`]. + **/ + setAllowanceForAll: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, operator: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr, bool]>; + /** + * See [`Pallet::set_collection_limits`]. + **/ + setCollectionLimits: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newLimit: UpDataStructsCollectionLimits | { accountTokenOwnershipLimit?: any; sponsoredDataSize?: any; sponsoredDataRateLimit?: any; tokenLimit?: any; sponsorTransferTimeout?: any; sponsorApproveTimeout?: any; ownerCanTransfer?: any; ownerCanDestroy?: any; transfersEnabled?: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, UpDataStructsCollectionLimits]>; + /** + * See [`Pallet::set_collection_permissions`]. + **/ + setCollectionPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newPermission: UpDataStructsCollectionPermissions | { access?: any; mintMode?: any; nesting?: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, UpDataStructsCollectionPermissions]>; + /** + * See [`Pallet::set_collection_properties`]. + **/ + setCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, properties: Vec | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, Vec]>; + /** + * See [`Pallet::set_collection_sponsor`]. + **/ + setCollectionSponsor: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newSponsor: AccountId32 | string | Uint8Array) => SubmittableExtrinsic, [u32, AccountId32]>; + /** + * See [`Pallet::set_token_properties`]. + **/ + setTokenProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, properties: Vec | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, u32, Vec]>; + /** + * See [`Pallet::set_token_property_permissions`]. + **/ + setTokenPropertyPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyPermissions: Vec | (UpDataStructsPropertyKeyPermission | { key?: any; permission?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, Vec]>; + /** + * See [`Pallet::set_transfers_enabled_flag`]. + **/ + setTransfersEnabledFlag: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, value: bool | boolean | Uint8Array) => SubmittableExtrinsic, [u32, bool]>; + /** + * See [`Pallet::transfer`]. + **/ + transfer: AugmentedSubmittable<(recipient: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>; + /** + * See [`Pallet::transfer_from`]. + **/ + transferFrom: AugmentedSubmittable<(from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, recipient: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + utility: { + /** + * See [`Pallet::as_derivative`]. + **/ + asDerivative: AugmentedSubmittable<(index: u16 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [u16, Call]>; + /** + * See [`Pallet::batch`]. + **/ + batch: AugmentedSubmittable<(calls: Vec | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * See [`Pallet::batch_all`]. + **/ + batchAll: AugmentedSubmittable<(calls: Vec | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * See [`Pallet::dispatch_as`]. + **/ + dispatchAs: AugmentedSubmittable<(asOrigin: OpalRuntimeOriginCaller | { system: any } | { Void: any } | { Council: any } | { TechnicalCommittee: any } | { PolkadotXcm: any } | { CumulusXcm: any } | { Origins: any } | { Ethereum: any } | string | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [OpalRuntimeOriginCaller, Call]>; + /** + * See [`Pallet::force_batch`]. + **/ + forceBatch: AugmentedSubmittable<(calls: Vec | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; + /** + * See [`Pallet::with_weight`]. + **/ + withWeight: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array, weight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [Call, SpWeightsWeightV2Weight]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + vesting: { + /** + * See [`Pallet::claim`]. + **/ + claim: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::claim_for`]. + **/ + claimFor: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; + /** + * See [`Pallet::update_vesting_schedules`]. + **/ + updateVestingSchedules: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, vestingSchedules: Vec | (OrmlVestingVestingSchedule | { start?: any; period?: any; periodCount?: any; perPeriod?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [MultiAddress, Vec]>; + /** + * See [`Pallet::vested_transfer`]. + **/ + vestedTransfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, schedule: OrmlVestingVestingSchedule | { start?: any; period?: any; periodCount?: any; perPeriod?: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, OrmlVestingVestingSchedule]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + xcmpQueue: { + /** + * See [`Pallet::resume_xcm_execution`]. + **/ + resumeXcmExecution: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::service_overweight`]. + **/ + serviceOverweight: AugmentedSubmittable<(index: u64 | AnyNumber | Uint8Array, weightLimit: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [u64, SpWeightsWeightV2Weight]>; + /** + * See [`Pallet::suspend_xcm_execution`]. + **/ + suspendXcmExecution: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * See [`Pallet::update_drop_threshold`]. + **/ + updateDropThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::update_resume_threshold`]. + **/ + updateResumeThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::update_suspend_threshold`]. + **/ + updateSuspendThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * See [`Pallet::update_threshold_weight`]. + **/ + updateThresholdWeight: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [SpWeightsWeightV2Weight]>; + /** + * See [`Pallet::update_weight_restrict_decay`]. + **/ + updateWeightRestrictDecay: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [SpWeightsWeightV2Weight]>; + /** + * See [`Pallet::update_xcmp_max_individual_weight`]. + **/ + updateXcmpMaxIndividualWeight: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [SpWeightsWeightV2Weight]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + xTokens: { + /** + * See [`Pallet::transfer`]. + **/ + transfer: AugmentedSubmittable<(currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletForeignAssetsAssetId, u128, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; + /** + * See [`Pallet::transfer_multiasset`]. + **/ + transferMultiasset: AugmentedSubmittable<(asset: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; + /** + * See [`Pallet::transfer_multiassets`]. + **/ + transferMultiassets: AugmentedSubmittable<(assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeItem: u32 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiAssets, u32, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; + /** + * See [`Pallet::transfer_multiasset_with_fee`]. + **/ + transferMultiassetWithFee: AugmentedSubmittable<(asset: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, fee: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; + /** + * See [`Pallet::transfer_multicurrencies`]. + **/ + transferMulticurrencies: AugmentedSubmittable<(currencies: Vec> | ([PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, u128 | AnyNumber | Uint8Array])[], feeItem: u32 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [Vec>, u32, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; + /** + * See [`Pallet::transfer_with_fee`]. + **/ + transferWithFee: AugmentedSubmittable<(currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array, fee: u128 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletForeignAssetsAssetId, u128, u128, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; + } // AugmentedSubmittables +} // declare module --- /dev/null +++ b/js-packages/types/augment-api.ts @@ -0,0 +1,10 @@ +// Auto-generated via `yarn polkadot-types-from-chain`, do not edit +/* eslint-disable */ + +import './augment-api-consts.js'; +import './augment-api-errors.js'; +import './augment-api-events.js'; +import './augment-api-query.js'; +import './augment-api-tx.js'; +import './augment-api-rpc.js'; +import './augment-api-runtime.js'; --- /dev/null +++ b/js-packages/types/augment-types.ts @@ -0,0 +1,1587 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +// import type lookup before we augment - in some environments +// this is required to allow for ambient/previous definitions +import '@polkadot/types/types/registry'; + +import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemCodeUpgradeAuthorization, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity, CumulusPalletParachainSystemUnincludedSegmentAncestor, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate, CumulusPalletParachainSystemUnincludedSegmentSegmentTracker, CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OpalRuntimeRuntimeCommonSessionKeys, OpalRuntimeRuntimeHoldReason, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesIdAmount, PalletBalancesReasons, PalletBalancesReserveData, PalletCollatorSelectionCall, PalletCollatorSelectionError, PalletCollatorSelectionEvent, PalletCollatorSelectionHoldReason, PalletCollectiveCall, PalletCollectiveError, PalletCollectiveEvent, PalletCollectiveRawOrigin, PalletCollectiveVotes, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletConfigurationEvent, PalletDemocracyCall, PalletDemocracyConviction, PalletDemocracyDelegations, PalletDemocracyError, PalletDemocracyEvent, PalletDemocracyMetadataOwner, PalletDemocracyReferendumInfo, PalletDemocracyReferendumStatus, PalletDemocracyTally, PalletDemocracyVoteAccountVote, PalletDemocracyVotePriorLock, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCodeMetadata, PalletEvmCoderSubstrateError, PalletEvmContractHelpersCall, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletGovOriginsOrigin, PalletIdentityBitFlags, PalletIdentityCall, PalletIdentityError, PalletIdentityEvent, PalletIdentityIdentityField, PalletIdentityIdentityInfo, PalletIdentityJudgement, PalletIdentityRegistrarInfo, PalletIdentityRegistration, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletMembershipCall, PalletMembershipError, PalletMembershipEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletPreimageCall, PalletPreimageError, PalletPreimageEvent, PalletPreimageRequestStatus, PalletRankedCollectiveCall, PalletRankedCollectiveError, PalletRankedCollectiveEvent, PalletRankedCollectiveMemberRecord, PalletRankedCollectiveTally, PalletRankedCollectiveVoteRecord, PalletReferendaCall, PalletReferendaCurve, PalletReferendaDecidingStatus, PalletReferendaDeposit, PalletReferendaError, PalletReferendaEvent, PalletReferendaReferendumInfo, PalletReferendaReferendumStatus, PalletReferendaTrackInfo, PalletRefungibleError, PalletSchedulerCall, PalletSchedulerError, PalletSchedulerEvent, PalletSchedulerScheduled, PalletSessionCall, PalletSessionError, PalletSessionEvent, PalletStateTrieMigrationCall, PalletStateTrieMigrationError, PalletStateTrieMigrationEvent, PalletStateTrieMigrationMigrationCompute, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletStateTrieMigrationProgress, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUtilityCall, PalletUtilityError, PalletUtilityEvent, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PalletXcmQueryStatus, PalletXcmRemoteLockedFungibleRecord, PalletXcmVersionMigrationStage, ParachainInfoCall, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat, PolkadotPrimitivesV5AbridgedHostConfiguration, PolkadotPrimitivesV5AbridgedHrmpChannel, PolkadotPrimitivesV5PersistedValidationData, PolkadotPrimitivesV5UpgradeGoAhead, PolkadotPrimitivesV5UpgradeRestriction, PolkadotPrimitivesVstagingAsyncBackingParams, SpArithmeticArithmeticError, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Public, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionValidityInvalidTransaction, SpRuntimeTransactionValidityTransactionValidityError, SpRuntimeTransactionValidityUnknownTransaction, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, StagingXcmDoubleEncoded, StagingXcmV2BodyId, StagingXcmV2BodyPart, StagingXcmV2Instruction, StagingXcmV2Junction, StagingXcmV2MultiAsset, StagingXcmV2MultiLocation, StagingXcmV2MultiassetAssetId, StagingXcmV2MultiassetAssetInstance, StagingXcmV2MultiassetFungibility, StagingXcmV2MultiassetMultiAssetFilter, StagingXcmV2MultiassetMultiAssets, StagingXcmV2MultiassetWildFungibility, StagingXcmV2MultiassetWildMultiAsset, StagingXcmV2MultilocationJunctions, StagingXcmV2NetworkId, StagingXcmV2OriginKind, StagingXcmV2Response, StagingXcmV2TraitsError, StagingXcmV2WeightLimit, StagingXcmV2Xcm, StagingXcmV3Instruction, StagingXcmV3Junction, StagingXcmV3JunctionBodyId, StagingXcmV3JunctionBodyPart, StagingXcmV3JunctionNetworkId, StagingXcmV3Junctions, StagingXcmV3MaybeErrorCode, StagingXcmV3MultiAsset, StagingXcmV3MultiLocation, StagingXcmV3MultiassetAssetId, StagingXcmV3MultiassetAssetInstance, StagingXcmV3MultiassetFungibility, StagingXcmV3MultiassetMultiAssetFilter, StagingXcmV3MultiassetMultiAssets, StagingXcmV3MultiassetWildFungibility, StagingXcmV3MultiassetWildMultiAsset, StagingXcmV3PalletInfo, StagingXcmV3QueryResponseInfo, StagingXcmV3Response, StagingXcmV3TraitsError, StagingXcmV3TraitsOutcome, StagingXcmV3WeightLimit, StagingXcmV3Xcm, StagingXcmVersionedAssetId, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation, StagingXcmVersionedResponse, StagingXcmVersionedXcm, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo, UpPovEstimateRpcTrieKeyValue } from './default'; +import type { Data, StorageKey } from '@polkadot/types'; +import type { BitVec, Bool, Bytes, F32, F64, I128, I16, I256, I32, I64, I8, ISize, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, f32, f64, i128, i16, i256, i32, i64, i8, isize, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec'; +import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets'; +import type { BlockAttestations, IncludedBlocks, MoreAttestations } from '@polkadot/types/interfaces/attestations'; +import type { RawAuraPreDigest } from '@polkadot/types/interfaces/aura'; +import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author'; +import type { UncleEntryItem } from '@polkadot/types/interfaces/authorship'; +import type { AllowedSlots, BabeAuthorityWeight, BabeBlockWeight, BabeEpochConfiguration, BabeEquivocationProof, BabeGenesisConfiguration, BabeGenesisConfigurationV1, BabeWeight, Epoch, EpochAuthorship, MaybeRandomness, MaybeVrf, NextConfigDescriptor, NextConfigDescriptorV1, OpaqueKeyOwnershipProof, Randomness, RawBabePreDigest, RawBabePreDigestCompat, RawBabePreDigestPrimary, RawBabePreDigestPrimaryTo159, RawBabePreDigestSecondaryPlain, RawBabePreDigestSecondaryTo159, RawBabePreDigestSecondaryVRF, RawBabePreDigestTo159, SlotNumber, VrfData, VrfOutput, VrfProof } from '@polkadot/types/interfaces/babe'; +import type { AccountData, BalanceLock, BalanceLockTo212, BalanceStatus, Reasons, ReserveData, ReserveIdentifier, VestingSchedule, WithdrawReasons } from '@polkadot/types/interfaces/balances'; +import type { BeefyAuthoritySet, BeefyCommitment, BeefyEquivocationProof, BeefyId, BeefyNextAuthoritySet, BeefyPayload, BeefyPayloadId, BeefySignedCommitment, BeefyVoteMessage, MmrRootHash, ValidatorSet, ValidatorSetId } from '@polkadot/types/interfaces/beefy'; +import type { BenchmarkBatch, BenchmarkConfig, BenchmarkList, BenchmarkMetadata, BenchmarkParameter, BenchmarkResult } from '@polkadot/types/interfaces/benchmark'; +import type { CheckInherentsResult, InherentData, InherentIdentifier } from '@polkadot/types/interfaces/blockbuilder'; +import type { BridgeMessageId, BridgedBlockHash, BridgedBlockNumber, BridgedHeader, CallOrigin, ChainId, DeliveredMessages, DispatchFeePayment, InboundLaneData, InboundRelayer, InitializationData, LaneId, MessageData, MessageKey, MessageNonce, MessagesDeliveryProofOf, MessagesProofOf, OperatingMode, OutboundLaneData, OutboundMessageFee, OutboundPayload, Parameter, RelayerId, UnrewardedRelayer, UnrewardedRelayersState } from '@polkadot/types/interfaces/bridges'; +import type { BlockHash } from '@polkadot/types/interfaces/chain'; +import type { PrefixedStorageKey } from '@polkadot/types/interfaces/childstate'; +import type { StatementKind } from '@polkadot/types/interfaces/claims'; +import type { CollectiveOrigin, MemberCount, ProposalIndex, Votes, VotesTo230 } from '@polkadot/types/interfaces/collective'; +import type { AuthorityId, RawVRFOutput } from '@polkadot/types/interfaces/consensus'; +import type { AliveContractInfo, CodeHash, CodeSource, CodeUploadRequest, CodeUploadResult, CodeUploadResultValue, ContractCallFlags, ContractCallRequest, ContractExecResult, ContractExecResultOk, ContractExecResultResult, ContractExecResultSuccessTo255, ContractExecResultSuccessTo260, ContractExecResultTo255, ContractExecResultTo260, ContractExecResultTo267, ContractExecResultU64, ContractInfo, ContractInstantiateResult, ContractInstantiateResultTo267, ContractInstantiateResultTo299, ContractInstantiateResultU64, ContractReturnFlags, ContractStorageKey, DeletedContract, ExecReturnValue, Gas, HostFnWeights, HostFnWeightsTo264, InstantiateRequest, InstantiateRequestV1, InstantiateRequestV2, InstantiateReturnValue, InstantiateReturnValueOk, InstantiateReturnValueTo267, InstructionWeights, Limits, LimitsTo264, PrefabWasmModule, RentProjection, Schedule, ScheduleTo212, ScheduleTo258, ScheduleTo264, SeedOf, StorageDeposit, TombstoneContractInfo, TrieId } from '@polkadot/types/interfaces/contracts'; +import type { ContractConstructorSpecLatest, ContractConstructorSpecV0, ContractConstructorSpecV1, ContractConstructorSpecV2, ContractConstructorSpecV3, ContractConstructorSpecV4, ContractContractSpecV0, ContractContractSpecV1, ContractContractSpecV2, ContractContractSpecV3, ContractContractSpecV4, ContractCryptoHasher, ContractDiscriminant, ContractDisplayName, ContractEnvironmentV4, ContractEventParamSpecLatest, ContractEventParamSpecV0, ContractEventParamSpecV2, ContractEventSpecLatest, ContractEventSpecV0, ContractEventSpecV1, ContractEventSpecV2, ContractLayoutArray, ContractLayoutCell, ContractLayoutEnum, ContractLayoutHash, ContractLayoutHashingStrategy, ContractLayoutKey, ContractLayoutStruct, ContractLayoutStructField, ContractMessageParamSpecLatest, ContractMessageParamSpecV0, ContractMessageParamSpecV2, ContractMessageSpecLatest, ContractMessageSpecV0, ContractMessageSpecV1, ContractMessageSpecV2, ContractMessageSpecV3, ContractMetadata, ContractMetadataLatest, ContractMetadataV0, ContractMetadataV1, ContractMetadataV2, ContractMetadataV3, ContractMetadataV4, ContractProject, ContractProjectContract, ContractProjectInfo, ContractProjectSource, ContractProjectV0, ContractSelector, ContractStorageLayout, ContractTypeSpec } from '@polkadot/types/interfaces/contractsAbi'; +import type { FundIndex, FundInfo, LastContribution, TrieIndex } from '@polkadot/types/interfaces/crowdloan'; +import type { CollationInfo, CollationInfoV1, ConfigData, MessageId, OverweightIndex, PageCounter, PageIndexData } from '@polkadot/types/interfaces/cumulus'; +import type { AccountVote, AccountVoteSplit, AccountVoteStandard, Conviction, Delegations, PreimageStatus, PreimageStatusAvailable, PriorLock, PropIndex, Proposal, ProxyState, ReferendumIndex, ReferendumInfo, ReferendumInfoFinished, ReferendumInfoTo239, ReferendumStatus, Tally, Voting, VotingDelegating, VotingDirect, VotingDirectVote } from '@polkadot/types/interfaces/democracy'; +import type { BlockStats } from '@polkadot/types/interfaces/dev'; +import type { ApprovalFlag, DefunctVoter, Renouncing, SetIndex, Vote, VoteIndex, VoteThreshold, VoterInfo } from '@polkadot/types/interfaces/elections'; +import type { CreatedBlock, ImportedAux } from '@polkadot/types/interfaces/engine'; +import type { BlockV0, BlockV1, BlockV2, EIP1559Transaction, EIP2930Transaction, EthAccessList, EthAccessListItem, EthAccount, EthAddress, EthBlock, EthBloom, EthCallRequest, EthFeeHistory, EthFilter, EthFilterAddress, EthFilterChanges, EthFilterTopic, EthFilterTopicEntry, EthFilterTopicInner, EthHeader, EthLog, EthReceipt, EthReceiptV0, EthReceiptV3, EthRichBlock, EthRichHeader, EthStorageProof, EthSubKind, EthSubParams, EthSubResult, EthSyncInfo, EthSyncStatus, EthTransaction, EthTransactionAction, EthTransactionCondition, EthTransactionRequest, EthTransactionSignature, EthTransactionStatus, EthWork, EthereumAccountId, EthereumAddress, EthereumLookupSource, EthereumSignature, LegacyTransaction, TransactionV0, TransactionV1, TransactionV2 } from '@polkadot/types/interfaces/eth'; +import type { EvmAccount, EvmCallInfo, EvmCallInfoV2, EvmCreateInfo, EvmCreateInfoV2, EvmLog, EvmVicinity, EvmWeightInfo, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed } from '@polkadot/types/interfaces/evm'; +import type { AnySignature, EcdsaSignature, Ed25519Signature, Era, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicSignature, ExtrinsicSignatureV4, ExtrinsicUnknown, ExtrinsicV4, ImmortalEra, MortalEra, MultiSignature, Signature, SignerPayload, Sr25519Signature } from '@polkadot/types/interfaces/extrinsics'; +import type { FungiblesAccessError } from '@polkadot/types/interfaces/fungibles'; +import type { AssetOptions, Owner, PermissionLatest, PermissionVersions, PermissionsV1 } from '@polkadot/types/interfaces/genericAsset'; +import type { ActiveGilt, ActiveGiltsTotal, ActiveIndex, GiltBid } from '@polkadot/types/interfaces/gilt'; +import type { AuthorityIndex, AuthorityList, AuthoritySet, AuthoritySetChange, AuthoritySetChanges, AuthorityWeight, DelayKind, DelayKindBest, EncodedFinalityProofs, ForkTreePendingChange, ForkTreePendingChangeNode, GrandpaCommit, GrandpaEquivocation, GrandpaEquivocationProof, GrandpaEquivocationValue, GrandpaJustification, GrandpaPrecommit, GrandpaPrevote, GrandpaSignedPrecommit, JustificationNotification, KeyOwnerProof, NextAuthority, PendingChange, PendingPause, PendingResume, Precommits, Prevotes, ReportedRoundStates, RoundState, SetId, StoredPendingChange, StoredState } from '@polkadot/types/interfaces/grandpa'; +import type { IdentityFields, IdentityInfo, IdentityInfoAdditional, IdentityInfoTo198, IdentityJudgement, RegistrarIndex, RegistrarInfo, Registration, RegistrationJudgement, RegistrationTo198 } from '@polkadot/types/interfaces/identity'; +import type { AuthIndex, AuthoritySignature, Heartbeat, HeartbeatTo244, OpaqueMultiaddr, OpaqueNetworkState, OpaquePeerId } from '@polkadot/types/interfaces/imOnline'; +import type { CallIndex, LotteryConfig } from '@polkadot/types/interfaces/lottery'; +import type { CustomMetadata15, CustomValueMetadata15, ErrorMetadataLatest, ErrorMetadataV10, ErrorMetadataV11, ErrorMetadataV12, ErrorMetadataV13, ErrorMetadataV14, ErrorMetadataV9, EventMetadataLatest, EventMetadataV10, EventMetadataV11, EventMetadataV12, EventMetadataV13, EventMetadataV14, EventMetadataV9, ExtrinsicMetadataLatest, ExtrinsicMetadataV11, ExtrinsicMetadataV12, ExtrinsicMetadataV13, ExtrinsicMetadataV14, ExtrinsicMetadataV15, FunctionArgumentMetadataLatest, FunctionArgumentMetadataV10, FunctionArgumentMetadataV11, FunctionArgumentMetadataV12, FunctionArgumentMetadataV13, FunctionArgumentMetadataV14, FunctionArgumentMetadataV9, FunctionMetadataLatest, FunctionMetadataV10, FunctionMetadataV11, FunctionMetadataV12, FunctionMetadataV13, FunctionMetadataV14, FunctionMetadataV9, MetadataAll, MetadataLatest, MetadataV10, MetadataV11, MetadataV12, MetadataV13, MetadataV14, MetadataV15, MetadataV9, ModuleConstantMetadataV10, ModuleConstantMetadataV11, ModuleConstantMetadataV12, ModuleConstantMetadataV13, ModuleConstantMetadataV9, ModuleMetadataV10, ModuleMetadataV11, ModuleMetadataV12, ModuleMetadataV13, ModuleMetadataV9, OpaqueMetadata, OuterEnums15, PalletCallMetadataLatest, PalletCallMetadataV14, PalletConstantMetadataLatest, PalletConstantMetadataV14, PalletErrorMetadataLatest, PalletErrorMetadataV14, PalletEventMetadataLatest, PalletEventMetadataV14, PalletMetadataLatest, PalletMetadataV14, PalletMetadataV15, PalletStorageMetadataLatest, PalletStorageMetadataV14, PortableType, PortableTypeV14, RuntimeApiMetadataLatest, RuntimeApiMetadataV15, RuntimeApiMethodMetadataV15, RuntimeApiMethodParamMetadataV15, SignedExtensionMetadataLatest, SignedExtensionMetadataV14, StorageEntryMetadataLatest, StorageEntryMetadataV10, StorageEntryMetadataV11, StorageEntryMetadataV12, StorageEntryMetadataV13, StorageEntryMetadataV14, StorageEntryMetadataV9, StorageEntryModifierLatest, StorageEntryModifierV10, StorageEntryModifierV11, StorageEntryModifierV12, StorageEntryModifierV13, StorageEntryModifierV14, StorageEntryModifierV9, StorageEntryTypeLatest, StorageEntryTypeV10, StorageEntryTypeV11, StorageEntryTypeV12, StorageEntryTypeV13, StorageEntryTypeV14, StorageEntryTypeV9, StorageHasher, StorageHasherV10, StorageHasherV11, StorageHasherV12, StorageHasherV13, StorageHasherV14, StorageHasherV9, StorageMetadataV10, StorageMetadataV11, StorageMetadataV12, StorageMetadataV13, StorageMetadataV9 } from '@polkadot/types/interfaces/metadata'; +import type { MmrBatchProof, MmrEncodableOpaqueLeaf, MmrError, MmrHash, MmrLeafBatchProof, MmrLeafIndex, MmrLeafProof, MmrNodeIndex, MmrProof } from '@polkadot/types/interfaces/mmr'; +import type { NftCollectionId, NftItemId } from '@polkadot/types/interfaces/nfts'; +import type { NpApiError, NpPoolId } from '@polkadot/types/interfaces/nompools'; +import type { StorageKind } from '@polkadot/types/interfaces/offchain'; +import type { DeferredOffenceOf, Kind, OffenceDetails, Offender, OpaqueTimeSlot, ReportIdOf, Reporter } from '@polkadot/types/interfaces/offences'; +import type { AbridgedCandidateReceipt, AbridgedHostConfiguration, AbridgedHrmpChannel, AssignmentId, AssignmentKind, AttestedCandidate, AuctionIndex, AuthorityDiscoveryId, AvailabilityBitfield, AvailabilityBitfieldRecord, BackedCandidate, Bidder, BufferedSessionChange, CandidateCommitments, CandidateDescriptor, CandidateEvent, CandidateHash, CandidateInfo, CandidatePendingAvailability, CandidateReceipt, CollatorId, CollatorSignature, CommittedCandidateReceipt, CoreAssignment, CoreIndex, CoreOccupied, CoreState, DisputeLocation, DisputeProof, DisputeResult, DisputeState, DisputeStatement, DisputeStatementSet, DisputesTimeSlot, DoubleVoteReport, DownwardMessage, ExecutorParam, ExecutorParams, ExecutorParamsHash, ExplicitDisputeStatement, GlobalValidationData, GlobalValidationSchedule, GroupIndex, GroupRotationInfo, HeadData, HostConfiguration, HrmpChannel, HrmpChannelId, HrmpOpenChannelRequest, InboundDownwardMessage, InboundHrmpMessage, InboundHrmpMessages, IncomingParachain, IncomingParachainDeploy, IncomingParachainFixed, InvalidDisputeStatementKind, LeasePeriod, LeasePeriodOf, LocalValidationData, MessageIngestionType, MessageQueueChain, MessagingStateSnapshot, MessagingStateSnapshotEgressEntry, MultiDisputeStatementSet, NewBidder, OccupiedCore, OccupiedCoreAssumption, OldV1SessionInfo, OutboundHrmpMessage, ParaGenesisArgs, ParaId, ParaInfo, ParaLifecycle, ParaPastCodeMeta, ParaScheduling, ParaValidatorIndex, ParachainDispatchOrigin, ParachainInherentData, ParachainProposal, ParachainsInherentData, ParathreadClaim, ParathreadClaimQueue, ParathreadEntry, PendingSlashes, PersistedValidationData, PvfCheckStatement, PvfExecTimeoutKind, PvfPrepTimeoutKind, QueuedParathread, RegisteredParachainInfo, RelayBlockNumber, RelayChainBlockNumber, RelayChainHash, RelayHash, Remark, ReplacementTimes, Retriable, ScheduledCore, Scheduling, ScrapedOnChainVotes, ServiceQuality, SessionInfo, SessionInfoValidatorGroup, SignedAvailabilityBitfield, SignedAvailabilityBitfields, SigningContext, SlashingOffenceKind, SlotRange, SlotRange10, Statement, SubId, SystemInherentData, TransientValidationData, UpgradeGoAhead, UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode, ValidationCodeHash, ValidationData, ValidationDataType, ValidationFunctionParams, ValidatorSignature, ValidityAttestation, VecInboundHrmpMessage, WinnersData, WinnersData10, WinnersDataTuple, WinnersDataTuple10, WinningData, WinningData10, WinningDataEntry } from '@polkadot/types/interfaces/parachains'; +import type { FeeDetails, InclusionFee, RuntimeDispatchInfo, RuntimeDispatchInfoV1, RuntimeDispatchInfoV2 } from '@polkadot/types/interfaces/payment'; +import type { Approvals } from '@polkadot/types/interfaces/poll'; +import type { ProxyAnnouncement, ProxyDefinition, ProxyType } from '@polkadot/types/interfaces/proxy'; +import type { AccountStatus, AccountValidity } from '@polkadot/types/interfaces/purchase'; +import type { ActiveRecovery, RecoveryConfig } from '@polkadot/types/interfaces/recovery'; +import type { RpcMethods } from '@polkadot/types/interfaces/rpc'; +import type { AccountId, AccountId20, AccountId32, AccountId33, AccountIdOf, AccountIndex, Address, AssetId, Balance, BalanceOf, Block, BlockNumber, BlockNumberFor, BlockNumberOf, Call, CallHash, CallHashOf, ChangesTrieConfiguration, ChangesTrieSignal, CodecHash, Consensus, ConsensusEngineId, CrateVersion, Digest, DigestItem, EncodedJustification, ExtrinsicsWeight, Fixed128, Fixed64, FixedI128, FixedI64, FixedU128, FixedU64, H1024, H128, H160, H2048, H256, H32, H512, H64, Hash, Header, HeaderPartial, I32F32, Index, IndicesLookupSource, Justification, Justifications, KeyTypeId, KeyValue, LockIdentifier, LookupSource, LookupTarget, ModuleId, Moment, MultiAddress, MultiSigner, OpaqueCall, Origin, OriginCaller, PalletId, PalletVersion, PalletsOrigin, Pays, PerU16, Perbill, Percent, Permill, Perquintill, Phantom, PhantomData, PreRuntime, Releases, RuntimeCall, RuntimeDbWeight, RuntimeEvent, Seal, SealV0, SignedBlock, SignedBlockWithJustification, SignedBlockWithJustifications, Slot, SlotDuration, StorageData, StorageInfo, StorageProof, TransactionInfo, TransactionLongevity, TransactionPriority, TransactionStorageProof, TransactionTag, U32F32, ValidatorId, ValidatorIdOf, Weight, WeightMultiplier, WeightV0, WeightV1, WeightV2 } from '@polkadot/types/interfaces/runtime'; +import type { Si0Field, Si0LookupTypeId, Si0Path, Si0Type, Si0TypeDef, Si0TypeDefArray, Si0TypeDefBitSequence, Si0TypeDefCompact, Si0TypeDefComposite, Si0TypeDefPhantom, Si0TypeDefPrimitive, Si0TypeDefSequence, Si0TypeDefTuple, Si0TypeDefVariant, Si0TypeParameter, Si0Variant, Si1Field, Si1LookupTypeId, Si1Path, Si1Type, Si1TypeDef, Si1TypeDefArray, Si1TypeDefBitSequence, Si1TypeDefCompact, Si1TypeDefComposite, Si1TypeDefPrimitive, Si1TypeDefSequence, Si1TypeDefTuple, Si1TypeDefVariant, Si1TypeParameter, Si1Variant, SiField, SiLookupTypeId, SiPath, SiType, SiTypeDef, SiTypeDefArray, SiTypeDefBitSequence, SiTypeDefCompact, SiTypeDefComposite, SiTypeDefPrimitive, SiTypeDefSequence, SiTypeDefTuple, SiTypeDefVariant, SiTypeParameter, SiVariant } from '@polkadot/types/interfaces/scaleInfo'; +import type { Period, Priority, SchedulePeriod, SchedulePriority, Scheduled, ScheduledTo254, TaskAddress } from '@polkadot/types/interfaces/scheduler'; +import type { BeefyKey, FullIdentification, IdentificationTuple, Keys, MembershipProof, SessionIndex, SessionKeys1, SessionKeys10, SessionKeys10B, SessionKeys2, SessionKeys3, SessionKeys4, SessionKeys5, SessionKeys6, SessionKeys6B, SessionKeys7, SessionKeys7B, SessionKeys8, SessionKeys8B, SessionKeys9, SessionKeys9B, ValidatorCount } from '@polkadot/types/interfaces/session'; +import type { Bid, BidKind, SocietyJudgement, SocietyVote, StrikeCount, VouchingStatus } from '@polkadot/types/interfaces/society'; +import type { ActiveEraInfo, CompactAssignments, CompactAssignmentsTo257, CompactAssignmentsTo265, CompactAssignmentsWith16, CompactAssignmentsWith24, CompactScore, CompactScoreCompact, ElectionCompute, ElectionPhase, ElectionResult, ElectionScore, ElectionSize, ElectionStatus, EraIndex, EraPoints, EraRewardPoints, EraRewards, Exposure, ExtendedBalance, Forcing, IndividualExposure, KeyType, MomentOf, Nominations, NominatorIndex, NominatorIndexCompact, OffchainAccuracy, OffchainAccuracyCompact, PhragmenScore, Points, RawSolution, RawSolutionTo265, RawSolutionWith16, RawSolutionWith24, ReadySolution, RewardDestination, RewardPoint, RoundSnapshot, SeatHolder, SignedSubmission, SignedSubmissionOf, SignedSubmissionTo276, SlashJournalEntry, SlashingSpans, SlashingSpansTo204, SolutionOrSnapshotSize, SolutionSupport, SolutionSupports, SpanIndex, SpanRecord, StakingLedger, StakingLedgerTo223, StakingLedgerTo240, SubmissionIndicesOf, Supports, UnappliedSlash, UnappliedSlashOther, UnlockChunk, ValidatorIndex, ValidatorIndexCompact, ValidatorPrefs, ValidatorPrefsTo145, ValidatorPrefsTo196, ValidatorPrefsWithBlocked, ValidatorPrefsWithCommission, VoteWeight, Voter } from '@polkadot/types/interfaces/staking'; +import type { ApiId, BlockTrace, BlockTraceEvent, BlockTraceEventData, BlockTraceSpan, KeyValueOption, MigrationStatusResult, ReadProof, RuntimeVersion, RuntimeVersionApi, RuntimeVersionPartial, RuntimeVersionPre3, RuntimeVersionPre4, SpecVersion, StorageChangeSet, TraceBlockResponse, TraceError } from '@polkadot/types/interfaces/state'; +import type { WeightToFeeCoefficient } from '@polkadot/types/interfaces/support'; +import type { AccountInfo, AccountInfoWithDualRefCount, AccountInfoWithProviders, AccountInfoWithRefCount, AccountInfoWithRefCountU8, AccountInfoWithTripleRefCount, ApplyExtrinsicResult, ApplyExtrinsicResultPre6, ArithmeticError, BlockLength, BlockWeights, ChainProperties, ChainType, ConsumedWeight, DigestOf, DispatchClass, DispatchError, DispatchErrorModule, DispatchErrorModulePre6, DispatchErrorModuleU8, DispatchErrorModuleU8a, DispatchErrorPre6, DispatchErrorPre6First, DispatchErrorTo198, DispatchInfo, DispatchInfoTo190, DispatchInfoTo244, DispatchOutcome, DispatchOutcomePre6, DispatchResult, DispatchResultOf, DispatchResultTo198, Event, EventId, EventIndex, EventRecord, Health, InvalidTransaction, Key, LastRuntimeUpgradeInfo, NetworkState, NetworkStatePeerset, NetworkStatePeersetInfo, NodeRole, NotConnectedPeer, Peer, PeerEndpoint, PeerEndpointAddr, PeerInfo, PeerPing, PerDispatchClassU32, PerDispatchClassWeight, PerDispatchClassWeightsPerClass, Phase, RawOrigin, RefCount, RefCountTo259, SyncState, SystemOrigin, TokenError, TransactionValidityError, TransactionalError, UnknownTransaction, WeightPerClass } from '@polkadot/types/interfaces/system'; +import type { Bounty, BountyIndex, BountyStatus, BountyStatusActive, BountyStatusCuratorProposed, BountyStatusPendingPayout, OpenTip, OpenTipFinderTo225, OpenTipTip, OpenTipTo225, TreasuryProposal } from '@polkadot/types/interfaces/treasury'; +import type { Multiplier } from '@polkadot/types/interfaces/txpayment'; +import type { TransactionSource, TransactionValidity, ValidTransaction } from '@polkadot/types/interfaces/txqueue'; +import type { ClassDetails, ClassId, ClassMetadata, DepositBalance, DepositBalanceOf, DestroyWitness, InstanceDetails, InstanceId, InstanceMetadata } from '@polkadot/types/interfaces/uniques'; +import type { Multisig, Timepoint } from '@polkadot/types/interfaces/utility'; +import type { VestingInfo } from '@polkadot/types/interfaces/vesting'; +import type { AssetInstance, AssetInstanceV0, AssetInstanceV1, AssetInstanceV2, BodyId, BodyPart, DoubleEncodedCall, Fungibility, FungibilityV0, FungibilityV1, FungibilityV2, InboundStatus, InstructionV2, InteriorMultiLocation, Junction, JunctionV0, JunctionV1, JunctionV2, Junctions, JunctionsV1, JunctionsV2, MultiAsset, MultiAssetFilter, MultiAssetFilterV1, MultiAssetFilterV2, MultiAssetV0, MultiAssetV1, MultiAssetV2, MultiAssets, MultiAssetsV1, MultiAssetsV2, MultiLocation, MultiLocationV0, MultiLocationV1, MultiLocationV2, NetworkId, OriginKindV0, OriginKindV1, OriginKindV2, OutboundStatus, Outcome, QueryId, QueryStatus, QueueConfigData, Response, ResponseV0, ResponseV1, ResponseV2, ResponseV2Error, ResponseV2Result, VersionMigrationStage, VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation, VersionedResponse, VersionedXcm, WeightLimitV2, WildFungibility, WildFungibilityV0, WildFungibilityV1, WildFungibilityV2, WildMultiAsset, WildMultiAssetV1, WildMultiAssetV2, Xcm, XcmAssetId, XcmError, XcmErrorV0, XcmErrorV1, XcmErrorV2, XcmOrder, XcmOrderV0, XcmOrderV1, XcmOrderV2, XcmOrigin, XcmOriginKind, XcmV0, XcmV1, XcmV2, XcmVersion, XcmpMessageFormat } from '@polkadot/types/interfaces/xcm'; + +declare module '@polkadot/types/types/registry' { + interface InterfaceTypes { + AbridgedCandidateReceipt: AbridgedCandidateReceipt; + AbridgedHostConfiguration: AbridgedHostConfiguration; + AbridgedHrmpChannel: AbridgedHrmpChannel; + AccountData: AccountData; + AccountId: AccountId; + AccountId20: AccountId20; + AccountId32: AccountId32; + AccountId33: AccountId33; + AccountIdOf: AccountIdOf; + AccountIndex: AccountIndex; + AccountInfo: AccountInfo; + AccountInfoWithDualRefCount: AccountInfoWithDualRefCount; + AccountInfoWithProviders: AccountInfoWithProviders; + AccountInfoWithRefCount: AccountInfoWithRefCount; + AccountInfoWithRefCountU8: AccountInfoWithRefCountU8; + AccountInfoWithTripleRefCount: AccountInfoWithTripleRefCount; + AccountStatus: AccountStatus; + AccountValidity: AccountValidity; + AccountVote: AccountVote; + AccountVoteSplit: AccountVoteSplit; + AccountVoteStandard: AccountVoteStandard; + ActiveEraInfo: ActiveEraInfo; + ActiveGilt: ActiveGilt; + ActiveGiltsTotal: ActiveGiltsTotal; + ActiveIndex: ActiveIndex; + ActiveRecovery: ActiveRecovery; + Address: Address; + AliveContractInfo: AliveContractInfo; + AllowedSlots: AllowedSlots; + AnySignature: AnySignature; + ApiId: ApiId; + ApplyExtrinsicResult: ApplyExtrinsicResult; + ApplyExtrinsicResultPre6: ApplyExtrinsicResultPre6; + ApprovalFlag: ApprovalFlag; + Approvals: Approvals; + ArithmeticError: ArithmeticError; + AssetApproval: AssetApproval; + AssetApprovalKey: AssetApprovalKey; + AssetBalance: AssetBalance; + AssetDestroyWitness: AssetDestroyWitness; + AssetDetails: AssetDetails; + AssetId: AssetId; + AssetInstance: AssetInstance; + AssetInstanceV0: AssetInstanceV0; + AssetInstanceV1: AssetInstanceV1; + AssetInstanceV2: AssetInstanceV2; + AssetMetadata: AssetMetadata; + AssetOptions: AssetOptions; + AssignmentId: AssignmentId; + AssignmentKind: AssignmentKind; + AttestedCandidate: AttestedCandidate; + AuctionIndex: AuctionIndex; + AuthIndex: AuthIndex; + AuthorityDiscoveryId: AuthorityDiscoveryId; + AuthorityId: AuthorityId; + AuthorityIndex: AuthorityIndex; + AuthorityList: AuthorityList; + AuthoritySet: AuthoritySet; + AuthoritySetChange: AuthoritySetChange; + AuthoritySetChanges: AuthoritySetChanges; + AuthoritySignature: AuthoritySignature; + AuthorityWeight: AuthorityWeight; + AvailabilityBitfield: AvailabilityBitfield; + AvailabilityBitfieldRecord: AvailabilityBitfieldRecord; + BabeAuthorityWeight: BabeAuthorityWeight; + BabeBlockWeight: BabeBlockWeight; + BabeEpochConfiguration: BabeEpochConfiguration; + BabeEquivocationProof: BabeEquivocationProof; + BabeGenesisConfiguration: BabeGenesisConfiguration; + BabeGenesisConfigurationV1: BabeGenesisConfigurationV1; + BabeWeight: BabeWeight; + BackedCandidate: BackedCandidate; + Balance: Balance; + BalanceLock: BalanceLock; + BalanceLockTo212: BalanceLockTo212; + BalanceOf: BalanceOf; + BalanceStatus: BalanceStatus; + BeefyAuthoritySet: BeefyAuthoritySet; + BeefyCommitment: BeefyCommitment; + BeefyEquivocationProof: BeefyEquivocationProof; + BeefyId: BeefyId; + BeefyKey: BeefyKey; + BeefyNextAuthoritySet: BeefyNextAuthoritySet; + BeefyPayload: BeefyPayload; + BeefyPayloadId: BeefyPayloadId; + BeefySignedCommitment: BeefySignedCommitment; + BeefyVoteMessage: BeefyVoteMessage; + BenchmarkBatch: BenchmarkBatch; + BenchmarkConfig: BenchmarkConfig; + BenchmarkList: BenchmarkList; + BenchmarkMetadata: BenchmarkMetadata; + BenchmarkParameter: BenchmarkParameter; + BenchmarkResult: BenchmarkResult; + Bid: Bid; + Bidder: Bidder; + BidKind: BidKind; + BitVec: BitVec; + Block: Block; + BlockAttestations: BlockAttestations; + BlockHash: BlockHash; + BlockLength: BlockLength; + BlockNumber: BlockNumber; + BlockNumberFor: BlockNumberFor; + BlockNumberOf: BlockNumberOf; + BlockStats: BlockStats; + BlockTrace: BlockTrace; + BlockTraceEvent: BlockTraceEvent; + BlockTraceEventData: BlockTraceEventData; + BlockTraceSpan: BlockTraceSpan; + BlockV0: BlockV0; + BlockV1: BlockV1; + BlockV2: BlockV2; + BlockWeights: BlockWeights; + BodyId: BodyId; + BodyPart: BodyPart; + bool: bool; + Bool: Bool; + Bounty: Bounty; + BountyIndex: BountyIndex; + BountyStatus: BountyStatus; + BountyStatusActive: BountyStatusActive; + BountyStatusCuratorProposed: BountyStatusCuratorProposed; + BountyStatusPendingPayout: BountyStatusPendingPayout; + BridgedBlockHash: BridgedBlockHash; + BridgedBlockNumber: BridgedBlockNumber; + BridgedHeader: BridgedHeader; + BridgeMessageId: BridgeMessageId; + BufferedSessionChange: BufferedSessionChange; + Bytes: Bytes; + Call: Call; + CallHash: CallHash; + CallHashOf: CallHashOf; + CallIndex: CallIndex; + CallOrigin: CallOrigin; + CandidateCommitments: CandidateCommitments; + CandidateDescriptor: CandidateDescriptor; + CandidateEvent: CandidateEvent; + CandidateHash: CandidateHash; + CandidateInfo: CandidateInfo; + CandidatePendingAvailability: CandidatePendingAvailability; + CandidateReceipt: CandidateReceipt; + ChainId: ChainId; + ChainProperties: ChainProperties; + ChainType: ChainType; + ChangesTrieConfiguration: ChangesTrieConfiguration; + ChangesTrieSignal: ChangesTrieSignal; + CheckInherentsResult: CheckInherentsResult; + ClassDetails: ClassDetails; + ClassId: ClassId; + ClassMetadata: ClassMetadata; + CodecHash: CodecHash; + CodeHash: CodeHash; + CodeSource: CodeSource; + CodeUploadRequest: CodeUploadRequest; + CodeUploadResult: CodeUploadResult; + CodeUploadResultValue: CodeUploadResultValue; + CollationInfo: CollationInfo; + CollationInfoV1: CollationInfoV1; + CollatorId: CollatorId; + CollatorSignature: CollatorSignature; + CollectiveOrigin: CollectiveOrigin; + CommittedCandidateReceipt: CommittedCandidateReceipt; + CompactAssignments: CompactAssignments; + CompactAssignmentsTo257: CompactAssignmentsTo257; + CompactAssignmentsTo265: CompactAssignmentsTo265; + CompactAssignmentsWith16: CompactAssignmentsWith16; + CompactAssignmentsWith24: CompactAssignmentsWith24; + CompactScore: CompactScore; + CompactScoreCompact: CompactScoreCompact; + ConfigData: ConfigData; + Consensus: Consensus; + ConsensusEngineId: ConsensusEngineId; + ConsumedWeight: ConsumedWeight; + ContractCallFlags: ContractCallFlags; + ContractCallRequest: ContractCallRequest; + ContractConstructorSpecLatest: ContractConstructorSpecLatest; + ContractConstructorSpecV0: ContractConstructorSpecV0; + ContractConstructorSpecV1: ContractConstructorSpecV1; + ContractConstructorSpecV2: ContractConstructorSpecV2; + ContractConstructorSpecV3: ContractConstructorSpecV3; + ContractConstructorSpecV4: ContractConstructorSpecV4; + ContractContractSpecV0: ContractContractSpecV0; + ContractContractSpecV1: ContractContractSpecV1; + ContractContractSpecV2: ContractContractSpecV2; + ContractContractSpecV3: ContractContractSpecV3; + ContractContractSpecV4: ContractContractSpecV4; + ContractCryptoHasher: ContractCryptoHasher; + ContractDiscriminant: ContractDiscriminant; + ContractDisplayName: ContractDisplayName; + ContractEnvironmentV4: ContractEnvironmentV4; + ContractEventParamSpecLatest: ContractEventParamSpecLatest; + ContractEventParamSpecV0: ContractEventParamSpecV0; + ContractEventParamSpecV2: ContractEventParamSpecV2; + ContractEventSpecLatest: ContractEventSpecLatest; + ContractEventSpecV0: ContractEventSpecV0; + ContractEventSpecV1: ContractEventSpecV1; + ContractEventSpecV2: ContractEventSpecV2; + ContractExecResult: ContractExecResult; + ContractExecResultOk: ContractExecResultOk; + ContractExecResultResult: ContractExecResultResult; + ContractExecResultSuccessTo255: ContractExecResultSuccessTo255; + ContractExecResultSuccessTo260: ContractExecResultSuccessTo260; + ContractExecResultTo255: ContractExecResultTo255; + ContractExecResultTo260: ContractExecResultTo260; + ContractExecResultTo267: ContractExecResultTo267; + ContractExecResultU64: ContractExecResultU64; + ContractInfo: ContractInfo; + ContractInstantiateResult: ContractInstantiateResult; + ContractInstantiateResultTo267: ContractInstantiateResultTo267; + ContractInstantiateResultTo299: ContractInstantiateResultTo299; + ContractInstantiateResultU64: ContractInstantiateResultU64; + ContractLayoutArray: ContractLayoutArray; + ContractLayoutCell: ContractLayoutCell; + ContractLayoutEnum: ContractLayoutEnum; + ContractLayoutHash: ContractLayoutHash; + ContractLayoutHashingStrategy: ContractLayoutHashingStrategy; + ContractLayoutKey: ContractLayoutKey; + ContractLayoutStruct: ContractLayoutStruct; + ContractLayoutStructField: ContractLayoutStructField; + ContractMessageParamSpecLatest: ContractMessageParamSpecLatest; + ContractMessageParamSpecV0: ContractMessageParamSpecV0; + ContractMessageParamSpecV2: ContractMessageParamSpecV2; + ContractMessageSpecLatest: ContractMessageSpecLatest; + ContractMessageSpecV0: ContractMessageSpecV0; + ContractMessageSpecV1: ContractMessageSpecV1; + ContractMessageSpecV2: ContractMessageSpecV2; + ContractMessageSpecV3: ContractMessageSpecV3; + ContractMetadata: ContractMetadata; + ContractMetadataLatest: ContractMetadataLatest; + ContractMetadataV0: ContractMetadataV0; + ContractMetadataV1: ContractMetadataV1; + ContractMetadataV2: ContractMetadataV2; + ContractMetadataV3: ContractMetadataV3; + ContractMetadataV4: ContractMetadataV4; + ContractProject: ContractProject; + ContractProjectContract: ContractProjectContract; + ContractProjectInfo: ContractProjectInfo; + ContractProjectSource: ContractProjectSource; + ContractProjectV0: ContractProjectV0; + ContractReturnFlags: ContractReturnFlags; + ContractSelector: ContractSelector; + ContractStorageKey: ContractStorageKey; + ContractStorageLayout: ContractStorageLayout; + ContractTypeSpec: ContractTypeSpec; + Conviction: Conviction; + CoreAssignment: CoreAssignment; + CoreIndex: CoreIndex; + CoreOccupied: CoreOccupied; + CoreState: CoreState; + CrateVersion: CrateVersion; + CreatedBlock: CreatedBlock; + CumulusPalletDmpQueueCall: CumulusPalletDmpQueueCall; + CumulusPalletDmpQueueConfigData: CumulusPalletDmpQueueConfigData; + CumulusPalletDmpQueueError: CumulusPalletDmpQueueError; + CumulusPalletDmpQueueEvent: CumulusPalletDmpQueueEvent; + CumulusPalletDmpQueuePageIndexData: CumulusPalletDmpQueuePageIndexData; + CumulusPalletParachainSystemCall: CumulusPalletParachainSystemCall; + CumulusPalletParachainSystemCodeUpgradeAuthorization: CumulusPalletParachainSystemCodeUpgradeAuthorization; + CumulusPalletParachainSystemError: CumulusPalletParachainSystemError; + CumulusPalletParachainSystemEvent: CumulusPalletParachainSystemEvent; + CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot; + CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity; + CumulusPalletParachainSystemUnincludedSegmentAncestor: CumulusPalletParachainSystemUnincludedSegmentAncestor; + CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate: CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate; + CumulusPalletParachainSystemUnincludedSegmentSegmentTracker: CumulusPalletParachainSystemUnincludedSegmentSegmentTracker; + CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth; + CumulusPalletXcmCall: CumulusPalletXcmCall; + CumulusPalletXcmError: CumulusPalletXcmError; + CumulusPalletXcmEvent: CumulusPalletXcmEvent; + CumulusPalletXcmOrigin: CumulusPalletXcmOrigin; + CumulusPalletXcmpQueueCall: CumulusPalletXcmpQueueCall; + CumulusPalletXcmpQueueError: CumulusPalletXcmpQueueError; + CumulusPalletXcmpQueueEvent: CumulusPalletXcmpQueueEvent; + CumulusPalletXcmpQueueInboundChannelDetails: CumulusPalletXcmpQueueInboundChannelDetails; + CumulusPalletXcmpQueueInboundState: CumulusPalletXcmpQueueInboundState; + CumulusPalletXcmpQueueOutboundChannelDetails: CumulusPalletXcmpQueueOutboundChannelDetails; + CumulusPalletXcmpQueueOutboundState: CumulusPalletXcmpQueueOutboundState; + CumulusPalletXcmpQueueQueueConfigData: CumulusPalletXcmpQueueQueueConfigData; + CumulusPrimitivesParachainInherentParachainInherentData: CumulusPrimitivesParachainInherentParachainInherentData; + CustomMetadata15: CustomMetadata15; + CustomValueMetadata15: CustomValueMetadata15; + Data: Data; + DeferredOffenceOf: DeferredOffenceOf; + DefunctVoter: DefunctVoter; + DelayKind: DelayKind; + DelayKindBest: DelayKindBest; + Delegations: Delegations; + DeletedContract: DeletedContract; + DeliveredMessages: DeliveredMessages; + DepositBalance: DepositBalance; + DepositBalanceOf: DepositBalanceOf; + DestroyWitness: DestroyWitness; + Digest: Digest; + DigestItem: DigestItem; + DigestOf: DigestOf; + DispatchClass: DispatchClass; + DispatchError: DispatchError; + DispatchErrorModule: DispatchErrorModule; + DispatchErrorModulePre6: DispatchErrorModulePre6; + DispatchErrorModuleU8: DispatchErrorModuleU8; + DispatchErrorModuleU8a: DispatchErrorModuleU8a; + DispatchErrorPre6: DispatchErrorPre6; + DispatchErrorPre6First: DispatchErrorPre6First; + DispatchErrorTo198: DispatchErrorTo198; + DispatchFeePayment: DispatchFeePayment; + DispatchInfo: DispatchInfo; + DispatchInfoTo190: DispatchInfoTo190; + DispatchInfoTo244: DispatchInfoTo244; + DispatchOutcome: DispatchOutcome; + DispatchOutcomePre6: DispatchOutcomePre6; + DispatchResult: DispatchResult; + DispatchResultOf: DispatchResultOf; + DispatchResultTo198: DispatchResultTo198; + DisputeLocation: DisputeLocation; + DisputeProof: DisputeProof; + DisputeResult: DisputeResult; + DisputeState: DisputeState; + DisputeStatement: DisputeStatement; + DisputeStatementSet: DisputeStatementSet; + DisputesTimeSlot: DisputesTimeSlot; + DoubleEncodedCall: DoubleEncodedCall; + DoubleVoteReport: DoubleVoteReport; + DownwardMessage: DownwardMessage; + EcdsaSignature: EcdsaSignature; + Ed25519Signature: Ed25519Signature; + EIP1559Transaction: EIP1559Transaction; + EIP2930Transaction: EIP2930Transaction; + ElectionCompute: ElectionCompute; + ElectionPhase: ElectionPhase; + ElectionResult: ElectionResult; + ElectionScore: ElectionScore; + ElectionSize: ElectionSize; + ElectionStatus: ElectionStatus; + EncodedFinalityProofs: EncodedFinalityProofs; + EncodedJustification: EncodedJustification; + Epoch: Epoch; + EpochAuthorship: EpochAuthorship; + Era: Era; + EraIndex: EraIndex; + EraPoints: EraPoints; + EraRewardPoints: EraRewardPoints; + EraRewards: EraRewards; + ErrorMetadataLatest: ErrorMetadataLatest; + ErrorMetadataV10: ErrorMetadataV10; + ErrorMetadataV11: ErrorMetadataV11; + ErrorMetadataV12: ErrorMetadataV12; + ErrorMetadataV13: ErrorMetadataV13; + ErrorMetadataV14: ErrorMetadataV14; + ErrorMetadataV9: ErrorMetadataV9; + EthAccessList: EthAccessList; + EthAccessListItem: EthAccessListItem; + EthAccount: EthAccount; + EthAddress: EthAddress; + EthBlock: EthBlock; + EthBloom: EthBloom; + EthbloomBloom: EthbloomBloom; + EthCallRequest: EthCallRequest; + EthereumAccountId: EthereumAccountId; + EthereumAddress: EthereumAddress; + EthereumBlock: EthereumBlock; + EthereumHeader: EthereumHeader; + EthereumLog: EthereumLog; + EthereumLookupSource: EthereumLookupSource; + EthereumReceiptEip658ReceiptData: EthereumReceiptEip658ReceiptData; + EthereumReceiptReceiptV3: EthereumReceiptReceiptV3; + EthereumSignature: EthereumSignature; + EthereumTransactionAccessListItem: EthereumTransactionAccessListItem; + EthereumTransactionEip1559Transaction: EthereumTransactionEip1559Transaction; + EthereumTransactionEip2930Transaction: EthereumTransactionEip2930Transaction; + EthereumTransactionLegacyTransaction: EthereumTransactionLegacyTransaction; + EthereumTransactionTransactionAction: EthereumTransactionTransactionAction; + EthereumTransactionTransactionSignature: EthereumTransactionTransactionSignature; + EthereumTransactionTransactionV2: EthereumTransactionTransactionV2; + EthereumTypesHashH64: EthereumTypesHashH64; + EthFeeHistory: EthFeeHistory; + EthFilter: EthFilter; + EthFilterAddress: EthFilterAddress; + EthFilterChanges: EthFilterChanges; + EthFilterTopic: EthFilterTopic; + EthFilterTopicEntry: EthFilterTopicEntry; + EthFilterTopicInner: EthFilterTopicInner; + EthHeader: EthHeader; + EthLog: EthLog; + EthReceipt: EthReceipt; + EthReceiptV0: EthReceiptV0; + EthReceiptV3: EthReceiptV3; + EthRichBlock: EthRichBlock; + EthRichHeader: EthRichHeader; + EthStorageProof: EthStorageProof; + EthSubKind: EthSubKind; + EthSubParams: EthSubParams; + EthSubResult: EthSubResult; + EthSyncInfo: EthSyncInfo; + EthSyncStatus: EthSyncStatus; + EthTransaction: EthTransaction; + EthTransactionAction: EthTransactionAction; + EthTransactionCondition: EthTransactionCondition; + EthTransactionRequest: EthTransactionRequest; + EthTransactionSignature: EthTransactionSignature; + EthTransactionStatus: EthTransactionStatus; + EthWork: EthWork; + Event: Event; + EventId: EventId; + EventIndex: EventIndex; + EventMetadataLatest: EventMetadataLatest; + EventMetadataV10: EventMetadataV10; + EventMetadataV11: EventMetadataV11; + EventMetadataV12: EventMetadataV12; + EventMetadataV13: EventMetadataV13; + EventMetadataV14: EventMetadataV14; + EventMetadataV9: EventMetadataV9; + EventRecord: EventRecord; + EvmAccount: EvmAccount; + EvmCallInfo: EvmCallInfo; + EvmCallInfoV2: EvmCallInfoV2; + EvmCoreErrorExitError: EvmCoreErrorExitError; + EvmCoreErrorExitFatal: EvmCoreErrorExitFatal; + EvmCoreErrorExitReason: EvmCoreErrorExitReason; + EvmCoreErrorExitRevert: EvmCoreErrorExitRevert; + EvmCoreErrorExitSucceed: EvmCoreErrorExitSucceed; + EvmCreateInfo: EvmCreateInfo; + EvmCreateInfoV2: EvmCreateInfoV2; + EvmLog: EvmLog; + EvmVicinity: EvmVicinity; + EvmWeightInfo: EvmWeightInfo; + ExecReturnValue: ExecReturnValue; + ExecutorParam: ExecutorParam; + ExecutorParams: ExecutorParams; + ExecutorParamsHash: ExecutorParamsHash; + ExitError: ExitError; + ExitFatal: ExitFatal; + ExitReason: ExitReason; + ExitRevert: ExitRevert; + ExitSucceed: ExitSucceed; + ExplicitDisputeStatement: ExplicitDisputeStatement; + Exposure: Exposure; + ExtendedBalance: ExtendedBalance; + Extrinsic: Extrinsic; + ExtrinsicEra: ExtrinsicEra; + ExtrinsicMetadataLatest: ExtrinsicMetadataLatest; + ExtrinsicMetadataV11: ExtrinsicMetadataV11; + ExtrinsicMetadataV12: ExtrinsicMetadataV12; + ExtrinsicMetadataV13: ExtrinsicMetadataV13; + ExtrinsicMetadataV14: ExtrinsicMetadataV14; + ExtrinsicMetadataV15: ExtrinsicMetadataV15; + ExtrinsicOrHash: ExtrinsicOrHash; + ExtrinsicPayload: ExtrinsicPayload; + ExtrinsicPayloadUnknown: ExtrinsicPayloadUnknown; + ExtrinsicPayloadV4: ExtrinsicPayloadV4; + ExtrinsicSignature: ExtrinsicSignature; + ExtrinsicSignatureV4: ExtrinsicSignatureV4; + ExtrinsicStatus: ExtrinsicStatus; + ExtrinsicsWeight: ExtrinsicsWeight; + ExtrinsicUnknown: ExtrinsicUnknown; + ExtrinsicV4: ExtrinsicV4; + f32: f32; + F32: F32; + f64: f64; + F64: F64; + FeeDetails: FeeDetails; + Fixed128: Fixed128; + Fixed64: Fixed64; + FixedI128: FixedI128; + FixedI64: FixedI64; + FixedU128: FixedU128; + FixedU64: FixedU64; + Forcing: Forcing; + ForkTreePendingChange: ForkTreePendingChange; + ForkTreePendingChangeNode: ForkTreePendingChangeNode; + FpRpcTransactionStatus: FpRpcTransactionStatus; + FrameSupportDispatchDispatchClass: FrameSupportDispatchDispatchClass; + FrameSupportDispatchDispatchInfo: FrameSupportDispatchDispatchInfo; + FrameSupportDispatchPays: FrameSupportDispatchPays; + FrameSupportDispatchPerDispatchClassU32: FrameSupportDispatchPerDispatchClassU32; + FrameSupportDispatchPerDispatchClassWeight: FrameSupportDispatchPerDispatchClassWeight; + FrameSupportDispatchPerDispatchClassWeightsPerClass: FrameSupportDispatchPerDispatchClassWeightsPerClass; + FrameSupportDispatchRawOrigin: FrameSupportDispatchRawOrigin; + FrameSupportPalletId: FrameSupportPalletId; + FrameSupportPreimagesBounded: FrameSupportPreimagesBounded; + FrameSupportScheduleDispatchTime: FrameSupportScheduleDispatchTime; + FrameSupportTokensMiscBalanceStatus: FrameSupportTokensMiscBalanceStatus; + FrameSystemAccountInfo: FrameSystemAccountInfo; + FrameSystemCall: FrameSystemCall; + FrameSystemError: FrameSystemError; + FrameSystemEvent: FrameSystemEvent; + FrameSystemEventRecord: FrameSystemEventRecord; + FrameSystemExtensionsCheckGenesis: FrameSystemExtensionsCheckGenesis; + FrameSystemExtensionsCheckNonce: FrameSystemExtensionsCheckNonce; + FrameSystemExtensionsCheckSpecVersion: FrameSystemExtensionsCheckSpecVersion; + FrameSystemExtensionsCheckTxVersion: FrameSystemExtensionsCheckTxVersion; + FrameSystemExtensionsCheckWeight: FrameSystemExtensionsCheckWeight; + FrameSystemLastRuntimeUpgradeInfo: FrameSystemLastRuntimeUpgradeInfo; + FrameSystemLimitsBlockLength: FrameSystemLimitsBlockLength; + FrameSystemLimitsBlockWeights: FrameSystemLimitsBlockWeights; + FrameSystemLimitsWeightsPerClass: FrameSystemLimitsWeightsPerClass; + FrameSystemPhase: FrameSystemPhase; + FullIdentification: FullIdentification; + FunctionArgumentMetadataLatest: FunctionArgumentMetadataLatest; + FunctionArgumentMetadataV10: FunctionArgumentMetadataV10; + FunctionArgumentMetadataV11: FunctionArgumentMetadataV11; + FunctionArgumentMetadataV12: FunctionArgumentMetadataV12; + FunctionArgumentMetadataV13: FunctionArgumentMetadataV13; + FunctionArgumentMetadataV14: FunctionArgumentMetadataV14; + FunctionArgumentMetadataV9: FunctionArgumentMetadataV9; + FunctionMetadataLatest: FunctionMetadataLatest; + FunctionMetadataV10: FunctionMetadataV10; + FunctionMetadataV11: FunctionMetadataV11; + FunctionMetadataV12: FunctionMetadataV12; + FunctionMetadataV13: FunctionMetadataV13; + FunctionMetadataV14: FunctionMetadataV14; + FunctionMetadataV9: FunctionMetadataV9; + FundIndex: FundIndex; + FundInfo: FundInfo; + Fungibility: Fungibility; + FungibilityV0: FungibilityV0; + FungibilityV1: FungibilityV1; + FungibilityV2: FungibilityV2; + FungiblesAccessError: FungiblesAccessError; + Gas: Gas; + GiltBid: GiltBid; + GlobalValidationData: GlobalValidationData; + GlobalValidationSchedule: GlobalValidationSchedule; + GrandpaCommit: GrandpaCommit; + GrandpaEquivocation: GrandpaEquivocation; + GrandpaEquivocationProof: GrandpaEquivocationProof; + GrandpaEquivocationValue: GrandpaEquivocationValue; + GrandpaJustification: GrandpaJustification; + GrandpaPrecommit: GrandpaPrecommit; + GrandpaPrevote: GrandpaPrevote; + GrandpaSignedPrecommit: GrandpaSignedPrecommit; + GroupIndex: GroupIndex; + GroupRotationInfo: GroupRotationInfo; + H1024: H1024; + H128: H128; + H160: H160; + H2048: H2048; + H256: H256; + H32: H32; + H512: H512; + H64: H64; + Hash: Hash; + HeadData: HeadData; + Header: Header; + HeaderPartial: HeaderPartial; + Health: Health; + Heartbeat: Heartbeat; + HeartbeatTo244: HeartbeatTo244; + HostConfiguration: HostConfiguration; + HostFnWeights: HostFnWeights; + HostFnWeightsTo264: HostFnWeightsTo264; + HrmpChannel: HrmpChannel; + HrmpChannelId: HrmpChannelId; + HrmpOpenChannelRequest: HrmpOpenChannelRequest; + i128: i128; + I128: I128; + i16: i16; + I16: I16; + i256: i256; + I256: I256; + i32: i32; + I32: I32; + I32F32: I32F32; + i64: i64; + I64: I64; + i8: i8; + I8: I8; + IdentificationTuple: IdentificationTuple; + IdentityFields: IdentityFields; + IdentityInfo: IdentityInfo; + IdentityInfoAdditional: IdentityInfoAdditional; + IdentityInfoTo198: IdentityInfoTo198; + IdentityJudgement: IdentityJudgement; + ImmortalEra: ImmortalEra; + ImportedAux: ImportedAux; + InboundDownwardMessage: InboundDownwardMessage; + InboundHrmpMessage: InboundHrmpMessage; + InboundHrmpMessages: InboundHrmpMessages; + InboundLaneData: InboundLaneData; + InboundRelayer: InboundRelayer; + InboundStatus: InboundStatus; + IncludedBlocks: IncludedBlocks; + InclusionFee: InclusionFee; + IncomingParachain: IncomingParachain; + IncomingParachainDeploy: IncomingParachainDeploy; + IncomingParachainFixed: IncomingParachainFixed; + Index: Index; + IndicesLookupSource: IndicesLookupSource; + IndividualExposure: IndividualExposure; + InherentData: InherentData; + InherentIdentifier: InherentIdentifier; + InitializationData: InitializationData; + InstanceDetails: InstanceDetails; + InstanceId: InstanceId; + InstanceMetadata: InstanceMetadata; + InstantiateRequest: InstantiateRequest; + InstantiateRequestV1: InstantiateRequestV1; + InstantiateRequestV2: InstantiateRequestV2; + InstantiateReturnValue: InstantiateReturnValue; + InstantiateReturnValueOk: InstantiateReturnValueOk; + InstantiateReturnValueTo267: InstantiateReturnValueTo267; + InstructionV2: InstructionV2; + InstructionWeights: InstructionWeights; + InteriorMultiLocation: InteriorMultiLocation; + InvalidDisputeStatementKind: InvalidDisputeStatementKind; + InvalidTransaction: InvalidTransaction; + isize: isize; + ISize: ISize; + Json: Json; + Junction: Junction; + Junctions: Junctions; + JunctionsV1: JunctionsV1; + JunctionsV2: JunctionsV2; + JunctionV0: JunctionV0; + JunctionV1: JunctionV1; + JunctionV2: JunctionV2; + Justification: Justification; + JustificationNotification: JustificationNotification; + Justifications: Justifications; + Key: Key; + KeyOwnerProof: KeyOwnerProof; + Keys: Keys; + KeyType: KeyType; + KeyTypeId: KeyTypeId; + KeyValue: KeyValue; + KeyValueOption: KeyValueOption; + Kind: Kind; + LaneId: LaneId; + LastContribution: LastContribution; + LastRuntimeUpgradeInfo: LastRuntimeUpgradeInfo; + LeasePeriod: LeasePeriod; + LeasePeriodOf: LeasePeriodOf; + LegacyTransaction: LegacyTransaction; + Limits: Limits; + LimitsTo264: LimitsTo264; + LocalValidationData: LocalValidationData; + LockIdentifier: LockIdentifier; + LookupSource: LookupSource; + LookupTarget: LookupTarget; + LotteryConfig: LotteryConfig; + MaybeRandomness: MaybeRandomness; + MaybeVrf: MaybeVrf; + MemberCount: MemberCount; + MembershipProof: MembershipProof; + MessageData: MessageData; + MessageId: MessageId; + MessageIngestionType: MessageIngestionType; + MessageKey: MessageKey; + MessageNonce: MessageNonce; + MessageQueueChain: MessageQueueChain; + MessagesDeliveryProofOf: MessagesDeliveryProofOf; + MessagesProofOf: MessagesProofOf; + MessagingStateSnapshot: MessagingStateSnapshot; + MessagingStateSnapshotEgressEntry: MessagingStateSnapshotEgressEntry; + MetadataAll: MetadataAll; + MetadataLatest: MetadataLatest; + MetadataV10: MetadataV10; + MetadataV11: MetadataV11; + MetadataV12: MetadataV12; + MetadataV13: MetadataV13; + MetadataV14: MetadataV14; + MetadataV15: MetadataV15; + MetadataV9: MetadataV9; + MigrationStatusResult: MigrationStatusResult; + MmrBatchProof: MmrBatchProof; + MmrEncodableOpaqueLeaf: MmrEncodableOpaqueLeaf; + MmrError: MmrError; + MmrHash: MmrHash; + MmrLeafBatchProof: MmrLeafBatchProof; + MmrLeafIndex: MmrLeafIndex; + MmrLeafProof: MmrLeafProof; + MmrNodeIndex: MmrNodeIndex; + MmrProof: MmrProof; + MmrRootHash: MmrRootHash; + ModuleConstantMetadataV10: ModuleConstantMetadataV10; + ModuleConstantMetadataV11: ModuleConstantMetadataV11; + ModuleConstantMetadataV12: ModuleConstantMetadataV12; + ModuleConstantMetadataV13: ModuleConstantMetadataV13; + ModuleConstantMetadataV9: ModuleConstantMetadataV9; + ModuleId: ModuleId; + ModuleMetadataV10: ModuleMetadataV10; + ModuleMetadataV11: ModuleMetadataV11; + ModuleMetadataV12: ModuleMetadataV12; + ModuleMetadataV13: ModuleMetadataV13; + ModuleMetadataV9: ModuleMetadataV9; + Moment: Moment; + MomentOf: MomentOf; + MoreAttestations: MoreAttestations; + MortalEra: MortalEra; + MultiAddress: MultiAddress; + MultiAsset: MultiAsset; + MultiAssetFilter: MultiAssetFilter; + MultiAssetFilterV1: MultiAssetFilterV1; + MultiAssetFilterV2: MultiAssetFilterV2; + MultiAssets: MultiAssets; + MultiAssetsV1: MultiAssetsV1; + MultiAssetsV2: MultiAssetsV2; + MultiAssetV0: MultiAssetV0; + MultiAssetV1: MultiAssetV1; + MultiAssetV2: MultiAssetV2; + MultiDisputeStatementSet: MultiDisputeStatementSet; + MultiLocation: MultiLocation; + MultiLocationV0: MultiLocationV0; + MultiLocationV1: MultiLocationV1; + MultiLocationV2: MultiLocationV2; + Multiplier: Multiplier; + Multisig: Multisig; + MultiSignature: MultiSignature; + MultiSigner: MultiSigner; + NetworkId: NetworkId; + NetworkState: NetworkState; + NetworkStatePeerset: NetworkStatePeerset; + NetworkStatePeersetInfo: NetworkStatePeersetInfo; + NewBidder: NewBidder; + NextAuthority: NextAuthority; + NextConfigDescriptor: NextConfigDescriptor; + NextConfigDescriptorV1: NextConfigDescriptorV1; + NftCollectionId: NftCollectionId; + NftItemId: NftItemId; + NodeRole: NodeRole; + Nominations: Nominations; + NominatorIndex: NominatorIndex; + NominatorIndexCompact: NominatorIndexCompact; + NotConnectedPeer: NotConnectedPeer; + NpApiError: NpApiError; + NpPoolId: NpPoolId; + Null: Null; + OccupiedCore: OccupiedCore; + OccupiedCoreAssumption: OccupiedCoreAssumption; + OffchainAccuracy: OffchainAccuracy; + OffchainAccuracyCompact: OffchainAccuracyCompact; + OffenceDetails: OffenceDetails; + Offender: Offender; + OldV1SessionInfo: OldV1SessionInfo; + OpalRuntimeOriginCaller: OpalRuntimeOriginCaller; + OpalRuntimeRuntime: OpalRuntimeRuntime; + OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls: OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls; + OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance; + OpalRuntimeRuntimeCommonSessionKeys: OpalRuntimeRuntimeCommonSessionKeys; + OpalRuntimeRuntimeHoldReason: OpalRuntimeRuntimeHoldReason; + OpaqueCall: OpaqueCall; + OpaqueKeyOwnershipProof: OpaqueKeyOwnershipProof; + OpaqueMetadata: OpaqueMetadata; + OpaqueMultiaddr: OpaqueMultiaddr; + OpaqueNetworkState: OpaqueNetworkState; + OpaquePeerId: OpaquePeerId; + OpaqueTimeSlot: OpaqueTimeSlot; + OpenTip: OpenTip; + OpenTipFinderTo225: OpenTipFinderTo225; + OpenTipTip: OpenTipTip; + OpenTipTo225: OpenTipTo225; + OperatingMode: OperatingMode; + OptionBool: OptionBool; + Origin: Origin; + OriginCaller: OriginCaller; + OriginKindV0: OriginKindV0; + OriginKindV1: OriginKindV1; + OriginKindV2: OriginKindV2; + OrmlTokensAccountData: OrmlTokensAccountData; + OrmlTokensBalanceLock: OrmlTokensBalanceLock; + OrmlTokensModuleCall: OrmlTokensModuleCall; + OrmlTokensModuleError: OrmlTokensModuleError; + OrmlTokensModuleEvent: OrmlTokensModuleEvent; + OrmlTokensReserveData: OrmlTokensReserveData; + OrmlVestingModuleCall: OrmlVestingModuleCall; + OrmlVestingModuleError: OrmlVestingModuleError; + OrmlVestingModuleEvent: OrmlVestingModuleEvent; + OrmlVestingVestingSchedule: OrmlVestingVestingSchedule; + OrmlXtokensModuleCall: OrmlXtokensModuleCall; + OrmlXtokensModuleError: OrmlXtokensModuleError; + OrmlXtokensModuleEvent: OrmlXtokensModuleEvent; + OutboundHrmpMessage: OutboundHrmpMessage; + OutboundLaneData: OutboundLaneData; + OutboundMessageFee: OutboundMessageFee; + OutboundPayload: OutboundPayload; + OutboundStatus: OutboundStatus; + Outcome: Outcome; + OuterEnums15: OuterEnums15; + OverweightIndex: OverweightIndex; + Owner: Owner; + PageCounter: PageCounter; + PageIndexData: PageIndexData; + PalletAppPromotionCall: PalletAppPromotionCall; + PalletAppPromotionError: PalletAppPromotionError; + PalletAppPromotionEvent: PalletAppPromotionEvent; + PalletBalancesAccountData: PalletBalancesAccountData; + PalletBalancesBalanceLock: PalletBalancesBalanceLock; + PalletBalancesCall: PalletBalancesCall; + PalletBalancesError: PalletBalancesError; + PalletBalancesEvent: PalletBalancesEvent; + PalletBalancesIdAmount: PalletBalancesIdAmount; + PalletBalancesReasons: PalletBalancesReasons; + PalletBalancesReserveData: PalletBalancesReserveData; + PalletCallMetadataLatest: PalletCallMetadataLatest; + PalletCallMetadataV14: PalletCallMetadataV14; + PalletCollatorSelectionCall: PalletCollatorSelectionCall; + PalletCollatorSelectionError: PalletCollatorSelectionError; + PalletCollatorSelectionEvent: PalletCollatorSelectionEvent; + PalletCollatorSelectionHoldReason: PalletCollatorSelectionHoldReason; + PalletCollectiveCall: PalletCollectiveCall; + PalletCollectiveError: PalletCollectiveError; + PalletCollectiveEvent: PalletCollectiveEvent; + PalletCollectiveRawOrigin: PalletCollectiveRawOrigin; + PalletCollectiveVotes: PalletCollectiveVotes; + PalletCommonError: PalletCommonError; + PalletCommonEvent: PalletCommonEvent; + PalletConfigurationAppPromotionConfiguration: PalletConfigurationAppPromotionConfiguration; + PalletConfigurationCall: PalletConfigurationCall; + PalletConfigurationError: PalletConfigurationError; + PalletConfigurationEvent: PalletConfigurationEvent; + PalletConstantMetadataLatest: PalletConstantMetadataLatest; + PalletConstantMetadataV14: PalletConstantMetadataV14; + PalletDemocracyCall: PalletDemocracyCall; + PalletDemocracyConviction: PalletDemocracyConviction; + PalletDemocracyDelegations: PalletDemocracyDelegations; + PalletDemocracyError: PalletDemocracyError; + PalletDemocracyEvent: PalletDemocracyEvent; + PalletDemocracyMetadataOwner: PalletDemocracyMetadataOwner; + PalletDemocracyReferendumInfo: PalletDemocracyReferendumInfo; + PalletDemocracyReferendumStatus: PalletDemocracyReferendumStatus; + PalletDemocracyTally: PalletDemocracyTally; + PalletDemocracyVoteAccountVote: PalletDemocracyVoteAccountVote; + PalletDemocracyVotePriorLock: PalletDemocracyVotePriorLock; + PalletDemocracyVoteThreshold: PalletDemocracyVoteThreshold; + PalletDemocracyVoteVoting: PalletDemocracyVoteVoting; + PalletErrorMetadataLatest: PalletErrorMetadataLatest; + PalletErrorMetadataV14: PalletErrorMetadataV14; + PalletEthereumCall: PalletEthereumCall; + PalletEthereumError: PalletEthereumError; + PalletEthereumEvent: PalletEthereumEvent; + PalletEthereumFakeTransactionFinalizer: PalletEthereumFakeTransactionFinalizer; + PalletEthereumRawOrigin: PalletEthereumRawOrigin; + PalletEventMetadataLatest: PalletEventMetadataLatest; + PalletEventMetadataV14: PalletEventMetadataV14; + PalletEvmAccountBasicCrossAccountIdRepr: PalletEvmAccountBasicCrossAccountIdRepr; + PalletEvmCall: PalletEvmCall; + PalletEvmCodeMetadata: PalletEvmCodeMetadata; + PalletEvmCoderSubstrateError: PalletEvmCoderSubstrateError; + PalletEvmContractHelpersCall: PalletEvmContractHelpersCall; + PalletEvmContractHelpersError: PalletEvmContractHelpersError; + PalletEvmContractHelpersEvent: PalletEvmContractHelpersEvent; + PalletEvmContractHelpersSponsoringModeT: PalletEvmContractHelpersSponsoringModeT; + PalletEvmError: PalletEvmError; + PalletEvmEvent: PalletEvmEvent; + PalletEvmMigrationCall: PalletEvmMigrationCall; + PalletEvmMigrationError: PalletEvmMigrationError; + PalletEvmMigrationEvent: PalletEvmMigrationEvent; + PalletForeignAssetsAssetId: PalletForeignAssetsAssetId; + PalletForeignAssetsModuleAssetMetadata: PalletForeignAssetsModuleAssetMetadata; + PalletForeignAssetsModuleCall: PalletForeignAssetsModuleCall; + PalletForeignAssetsModuleError: PalletForeignAssetsModuleError; + PalletForeignAssetsModuleEvent: PalletForeignAssetsModuleEvent; + PalletForeignAssetsNativeCurrency: PalletForeignAssetsNativeCurrency; + PalletFungibleError: PalletFungibleError; + PalletGovOriginsOrigin: PalletGovOriginsOrigin; + PalletId: PalletId; + PalletIdentityBitFlags: PalletIdentityBitFlags; + PalletIdentityCall: PalletIdentityCall; + PalletIdentityError: PalletIdentityError; + PalletIdentityEvent: PalletIdentityEvent; + PalletIdentityIdentityField: PalletIdentityIdentityField; + PalletIdentityIdentityInfo: PalletIdentityIdentityInfo; + PalletIdentityJudgement: PalletIdentityJudgement; + PalletIdentityRegistrarInfo: PalletIdentityRegistrarInfo; + PalletIdentityRegistration: PalletIdentityRegistration; + PalletInflationCall: PalletInflationCall; + PalletMaintenanceCall: PalletMaintenanceCall; + PalletMaintenanceError: PalletMaintenanceError; + PalletMaintenanceEvent: PalletMaintenanceEvent; + PalletMembershipCall: PalletMembershipCall; + PalletMembershipError: PalletMembershipError; + PalletMembershipEvent: PalletMembershipEvent; + PalletMetadataLatest: PalletMetadataLatest; + PalletMetadataV14: PalletMetadataV14; + PalletMetadataV15: PalletMetadataV15; + PalletNonfungibleError: PalletNonfungibleError; + PalletNonfungibleItemData: PalletNonfungibleItemData; + PalletPreimageCall: PalletPreimageCall; + PalletPreimageError: PalletPreimageError; + PalletPreimageEvent: PalletPreimageEvent; + PalletPreimageRequestStatus: PalletPreimageRequestStatus; + PalletRankedCollectiveCall: PalletRankedCollectiveCall; + PalletRankedCollectiveError: PalletRankedCollectiveError; + PalletRankedCollectiveEvent: PalletRankedCollectiveEvent; + PalletRankedCollectiveMemberRecord: PalletRankedCollectiveMemberRecord; + PalletRankedCollectiveTally: PalletRankedCollectiveTally; + PalletRankedCollectiveVoteRecord: PalletRankedCollectiveVoteRecord; + PalletReferendaCall: PalletReferendaCall; + PalletReferendaCurve: PalletReferendaCurve; + PalletReferendaDecidingStatus: PalletReferendaDecidingStatus; + PalletReferendaDeposit: PalletReferendaDeposit; + PalletReferendaError: PalletReferendaError; + PalletReferendaEvent: PalletReferendaEvent; + PalletReferendaReferendumInfo: PalletReferendaReferendumInfo; + PalletReferendaReferendumStatus: PalletReferendaReferendumStatus; + PalletReferendaTrackInfo: PalletReferendaTrackInfo; + PalletRefungibleError: PalletRefungibleError; + PalletSchedulerCall: PalletSchedulerCall; + PalletSchedulerError: PalletSchedulerError; + PalletSchedulerEvent: PalletSchedulerEvent; + PalletSchedulerScheduled: PalletSchedulerScheduled; + PalletSessionCall: PalletSessionCall; + PalletSessionError: PalletSessionError; + PalletSessionEvent: PalletSessionEvent; + PalletsOrigin: PalletsOrigin; + PalletStateTrieMigrationCall: PalletStateTrieMigrationCall; + PalletStateTrieMigrationError: PalletStateTrieMigrationError; + PalletStateTrieMigrationEvent: PalletStateTrieMigrationEvent; + PalletStateTrieMigrationMigrationCompute: PalletStateTrieMigrationMigrationCompute; + PalletStateTrieMigrationMigrationLimits: PalletStateTrieMigrationMigrationLimits; + PalletStateTrieMigrationMigrationTask: PalletStateTrieMigrationMigrationTask; + PalletStateTrieMigrationProgress: PalletStateTrieMigrationProgress; + PalletStorageMetadataLatest: PalletStorageMetadataLatest; + PalletStorageMetadataV14: PalletStorageMetadataV14; + PalletStructureCall: PalletStructureCall; + PalletStructureError: PalletStructureError; + PalletStructureEvent: PalletStructureEvent; + PalletSudoCall: PalletSudoCall; + PalletSudoError: PalletSudoError; + PalletSudoEvent: PalletSudoEvent; + PalletTemplateTransactionPaymentChargeTransactionPayment: PalletTemplateTransactionPaymentChargeTransactionPayment; + PalletTestUtilsCall: PalletTestUtilsCall; + PalletTestUtilsError: PalletTestUtilsError; + PalletTestUtilsEvent: PalletTestUtilsEvent; + PalletTimestampCall: PalletTimestampCall; + PalletTransactionPaymentEvent: PalletTransactionPaymentEvent; + PalletTransactionPaymentReleases: PalletTransactionPaymentReleases; + PalletTreasuryCall: PalletTreasuryCall; + PalletTreasuryError: PalletTreasuryError; + PalletTreasuryEvent: PalletTreasuryEvent; + PalletTreasuryProposal: PalletTreasuryProposal; + PalletUniqueCall: PalletUniqueCall; + PalletUniqueError: PalletUniqueError; + PalletUtilityCall: PalletUtilityCall; + PalletUtilityError: PalletUtilityError; + PalletUtilityEvent: PalletUtilityEvent; + PalletVersion: PalletVersion; + PalletXcmCall: PalletXcmCall; + PalletXcmError: PalletXcmError; + PalletXcmEvent: PalletXcmEvent; + PalletXcmOrigin: PalletXcmOrigin; + PalletXcmQueryStatus: PalletXcmQueryStatus; + PalletXcmRemoteLockedFungibleRecord: PalletXcmRemoteLockedFungibleRecord; + PalletXcmVersionMigrationStage: PalletXcmVersionMigrationStage; + ParachainDispatchOrigin: ParachainDispatchOrigin; + ParachainInfoCall: ParachainInfoCall; + ParachainInherentData: ParachainInherentData; + ParachainProposal: ParachainProposal; + ParachainsInherentData: ParachainsInherentData; + ParaGenesisArgs: ParaGenesisArgs; + ParaId: ParaId; + ParaInfo: ParaInfo; + ParaLifecycle: ParaLifecycle; + Parameter: Parameter; + ParaPastCodeMeta: ParaPastCodeMeta; + ParaScheduling: ParaScheduling; + ParathreadClaim: ParathreadClaim; + ParathreadClaimQueue: ParathreadClaimQueue; + ParathreadEntry: ParathreadEntry; + ParaValidatorIndex: ParaValidatorIndex; + Pays: Pays; + Peer: Peer; + PeerEndpoint: PeerEndpoint; + PeerEndpointAddr: PeerEndpointAddr; + PeerInfo: PeerInfo; + PeerPing: PeerPing; + PendingChange: PendingChange; + PendingPause: PendingPause; + PendingResume: PendingResume; + PendingSlashes: PendingSlashes; + Perbill: Perbill; + Percent: Percent; + PerDispatchClassU32: PerDispatchClassU32; + PerDispatchClassWeight: PerDispatchClassWeight; + PerDispatchClassWeightsPerClass: PerDispatchClassWeightsPerClass; + Period: Period; + Permill: Permill; + PermissionLatest: PermissionLatest; + PermissionsV1: PermissionsV1; + PermissionVersions: PermissionVersions; + Perquintill: Perquintill; + PersistedValidationData: PersistedValidationData; + PerU16: PerU16; + Phantom: Phantom; + PhantomData: PhantomData; + PhantomTypeUpDataStructs: PhantomTypeUpDataStructs; + Phase: Phase; + PhragmenScore: PhragmenScore; + Points: Points; + PolkadotCorePrimitivesInboundDownwardMessage: PolkadotCorePrimitivesInboundDownwardMessage; + PolkadotCorePrimitivesInboundHrmpMessage: PolkadotCorePrimitivesInboundHrmpMessage; + PolkadotCorePrimitivesOutboundHrmpMessage: PolkadotCorePrimitivesOutboundHrmpMessage; + PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat: PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat; + PolkadotPrimitivesV5AbridgedHostConfiguration: PolkadotPrimitivesV5AbridgedHostConfiguration; + PolkadotPrimitivesV5AbridgedHrmpChannel: PolkadotPrimitivesV5AbridgedHrmpChannel; + PolkadotPrimitivesV5PersistedValidationData: PolkadotPrimitivesV5PersistedValidationData; + PolkadotPrimitivesV5UpgradeGoAhead: PolkadotPrimitivesV5UpgradeGoAhead; + PolkadotPrimitivesV5UpgradeRestriction: PolkadotPrimitivesV5UpgradeRestriction; + PolkadotPrimitivesVstagingAsyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams; + PortableType: PortableType; + PortableTypeV14: PortableTypeV14; + Precommits: Precommits; + PrefabWasmModule: PrefabWasmModule; + PrefixedStorageKey: PrefixedStorageKey; + PreimageStatus: PreimageStatus; + PreimageStatusAvailable: PreimageStatusAvailable; + PreRuntime: PreRuntime; + Prevotes: Prevotes; + Priority: Priority; + PriorLock: PriorLock; + PropIndex: PropIndex; + Proposal: Proposal; + ProposalIndex: ProposalIndex; + ProxyAnnouncement: ProxyAnnouncement; + ProxyDefinition: ProxyDefinition; + ProxyState: ProxyState; + ProxyType: ProxyType; + PvfCheckStatement: PvfCheckStatement; + PvfExecTimeoutKind: PvfExecTimeoutKind; + PvfPrepTimeoutKind: PvfPrepTimeoutKind; + QueryId: QueryId; + QueryStatus: QueryStatus; + QueueConfigData: QueueConfigData; + QueuedParathread: QueuedParathread; + Randomness: Randomness; + Raw: Raw; + RawAuraPreDigest: RawAuraPreDigest; + RawBabePreDigest: RawBabePreDigest; + RawBabePreDigestCompat: RawBabePreDigestCompat; + RawBabePreDigestPrimary: RawBabePreDigestPrimary; + RawBabePreDigestPrimaryTo159: RawBabePreDigestPrimaryTo159; + RawBabePreDigestSecondaryPlain: RawBabePreDigestSecondaryPlain; + RawBabePreDigestSecondaryTo159: RawBabePreDigestSecondaryTo159; + RawBabePreDigestSecondaryVRF: RawBabePreDigestSecondaryVRF; + RawBabePreDigestTo159: RawBabePreDigestTo159; + RawOrigin: RawOrigin; + RawSolution: RawSolution; + RawSolutionTo265: RawSolutionTo265; + RawSolutionWith16: RawSolutionWith16; + RawSolutionWith24: RawSolutionWith24; + RawVRFOutput: RawVRFOutput; + ReadProof: ReadProof; + ReadySolution: ReadySolution; + Reasons: Reasons; + RecoveryConfig: RecoveryConfig; + RefCount: RefCount; + RefCountTo259: RefCountTo259; + ReferendumIndex: ReferendumIndex; + ReferendumInfo: ReferendumInfo; + ReferendumInfoFinished: ReferendumInfoFinished; + ReferendumInfoTo239: ReferendumInfoTo239; + ReferendumStatus: ReferendumStatus; + RegisteredParachainInfo: RegisteredParachainInfo; + RegistrarIndex: RegistrarIndex; + RegistrarInfo: RegistrarInfo; + Registration: Registration; + RegistrationJudgement: RegistrationJudgement; + RegistrationTo198: RegistrationTo198; + RelayBlockNumber: RelayBlockNumber; + RelayChainBlockNumber: RelayChainBlockNumber; + RelayChainHash: RelayChainHash; + RelayerId: RelayerId; + RelayHash: RelayHash; + Releases: Releases; + Remark: Remark; + Renouncing: Renouncing; + RentProjection: RentProjection; + ReplacementTimes: ReplacementTimes; + ReportedRoundStates: ReportedRoundStates; + Reporter: Reporter; + ReportIdOf: ReportIdOf; + ReserveData: ReserveData; + ReserveIdentifier: ReserveIdentifier; + Response: Response; + ResponseV0: ResponseV0; + ResponseV1: ResponseV1; + ResponseV2: ResponseV2; + ResponseV2Error: ResponseV2Error; + ResponseV2Result: ResponseV2Result; + Retriable: Retriable; + RewardDestination: RewardDestination; + RewardPoint: RewardPoint; + RoundSnapshot: RoundSnapshot; + RoundState: RoundState; + RpcMethods: RpcMethods; + RuntimeApiMetadataLatest: RuntimeApiMetadataLatest; + RuntimeApiMetadataV15: RuntimeApiMetadataV15; + RuntimeApiMethodMetadataV15: RuntimeApiMethodMetadataV15; + RuntimeApiMethodParamMetadataV15: RuntimeApiMethodParamMetadataV15; + RuntimeCall: RuntimeCall; + RuntimeDbWeight: RuntimeDbWeight; + RuntimeDispatchInfo: RuntimeDispatchInfo; + RuntimeDispatchInfoV1: RuntimeDispatchInfoV1; + RuntimeDispatchInfoV2: RuntimeDispatchInfoV2; + RuntimeEvent: RuntimeEvent; + RuntimeVersion: RuntimeVersion; + RuntimeVersionApi: RuntimeVersionApi; + RuntimeVersionPartial: RuntimeVersionPartial; + RuntimeVersionPre3: RuntimeVersionPre3; + RuntimeVersionPre4: RuntimeVersionPre4; + Schedule: Schedule; + Scheduled: Scheduled; + ScheduledCore: ScheduledCore; + ScheduledTo254: ScheduledTo254; + SchedulePeriod: SchedulePeriod; + SchedulePriority: SchedulePriority; + ScheduleTo212: ScheduleTo212; + ScheduleTo258: ScheduleTo258; + ScheduleTo264: ScheduleTo264; + Scheduling: Scheduling; + ScrapedOnChainVotes: ScrapedOnChainVotes; + Seal: Seal; + SealV0: SealV0; + SeatHolder: SeatHolder; + SeedOf: SeedOf; + ServiceQuality: ServiceQuality; + SessionIndex: SessionIndex; + SessionInfo: SessionInfo; + SessionInfoValidatorGroup: SessionInfoValidatorGroup; + SessionKeys1: SessionKeys1; + SessionKeys10: SessionKeys10; + SessionKeys10B: SessionKeys10B; + SessionKeys2: SessionKeys2; + SessionKeys3: SessionKeys3; + SessionKeys4: SessionKeys4; + SessionKeys5: SessionKeys5; + SessionKeys6: SessionKeys6; + SessionKeys6B: SessionKeys6B; + SessionKeys7: SessionKeys7; + SessionKeys7B: SessionKeys7B; + SessionKeys8: SessionKeys8; + SessionKeys8B: SessionKeys8B; + SessionKeys9: SessionKeys9; + SessionKeys9B: SessionKeys9B; + SetId: SetId; + SetIndex: SetIndex; + Si0Field: Si0Field; + Si0LookupTypeId: Si0LookupTypeId; + Si0Path: Si0Path; + Si0Type: Si0Type; + Si0TypeDef: Si0TypeDef; + Si0TypeDefArray: Si0TypeDefArray; + Si0TypeDefBitSequence: Si0TypeDefBitSequence; + Si0TypeDefCompact: Si0TypeDefCompact; + Si0TypeDefComposite: Si0TypeDefComposite; + Si0TypeDefPhantom: Si0TypeDefPhantom; + Si0TypeDefPrimitive: Si0TypeDefPrimitive; + Si0TypeDefSequence: Si0TypeDefSequence; + Si0TypeDefTuple: Si0TypeDefTuple; + Si0TypeDefVariant: Si0TypeDefVariant; + Si0TypeParameter: Si0TypeParameter; + Si0Variant: Si0Variant; + Si1Field: Si1Field; + Si1LookupTypeId: Si1LookupTypeId; + Si1Path: Si1Path; + Si1Type: Si1Type; + Si1TypeDef: Si1TypeDef; + Si1TypeDefArray: Si1TypeDefArray; + Si1TypeDefBitSequence: Si1TypeDefBitSequence; + Si1TypeDefCompact: Si1TypeDefCompact; + Si1TypeDefComposite: Si1TypeDefComposite; + Si1TypeDefPrimitive: Si1TypeDefPrimitive; + Si1TypeDefSequence: Si1TypeDefSequence; + Si1TypeDefTuple: Si1TypeDefTuple; + Si1TypeDefVariant: Si1TypeDefVariant; + Si1TypeParameter: Si1TypeParameter; + Si1Variant: Si1Variant; + SiField: SiField; + Signature: Signature; + SignedAvailabilityBitfield: SignedAvailabilityBitfield; + SignedAvailabilityBitfields: SignedAvailabilityBitfields; + SignedBlock: SignedBlock; + SignedBlockWithJustification: SignedBlockWithJustification; + SignedBlockWithJustifications: SignedBlockWithJustifications; + SignedExtensionMetadataLatest: SignedExtensionMetadataLatest; + SignedExtensionMetadataV14: SignedExtensionMetadataV14; + SignedSubmission: SignedSubmission; + SignedSubmissionOf: SignedSubmissionOf; + SignedSubmissionTo276: SignedSubmissionTo276; + SignerPayload: SignerPayload; + SigningContext: SigningContext; + SiLookupTypeId: SiLookupTypeId; + SiPath: SiPath; + SiType: SiType; + SiTypeDef: SiTypeDef; + SiTypeDefArray: SiTypeDefArray; + SiTypeDefBitSequence: SiTypeDefBitSequence; + SiTypeDefCompact: SiTypeDefCompact; + SiTypeDefComposite: SiTypeDefComposite; + SiTypeDefPrimitive: SiTypeDefPrimitive; + SiTypeDefSequence: SiTypeDefSequence; + SiTypeDefTuple: SiTypeDefTuple; + SiTypeDefVariant: SiTypeDefVariant; + SiTypeParameter: SiTypeParameter; + SiVariant: SiVariant; + SlashingOffenceKind: SlashingOffenceKind; + SlashingSpans: SlashingSpans; + SlashingSpansTo204: SlashingSpansTo204; + SlashJournalEntry: SlashJournalEntry; + Slot: Slot; + SlotDuration: SlotDuration; + SlotNumber: SlotNumber; + SlotRange: SlotRange; + SlotRange10: SlotRange10; + SocietyJudgement: SocietyJudgement; + SocietyVote: SocietyVote; + SolutionOrSnapshotSize: SolutionOrSnapshotSize; + SolutionSupport: SolutionSupport; + SolutionSupports: SolutionSupports; + SpanIndex: SpanIndex; + SpanRecord: SpanRecord; + SpArithmeticArithmeticError: SpArithmeticArithmeticError; + SpConsensusAuraSr25519AppSr25519Public: SpConsensusAuraSr25519AppSr25519Public; + SpCoreCryptoKeyTypeId: SpCoreCryptoKeyTypeId; + SpCoreEcdsaSignature: SpCoreEcdsaSignature; + SpCoreEd25519Signature: SpCoreEd25519Signature; + SpCoreSr25519Public: SpCoreSr25519Public; + SpCoreSr25519Signature: SpCoreSr25519Signature; + SpCoreVoid: SpCoreVoid; + SpecVersion: SpecVersion; + SpRuntimeDigest: SpRuntimeDigest; + SpRuntimeDigestDigestItem: SpRuntimeDigestDigestItem; + SpRuntimeDispatchError: SpRuntimeDispatchError; + SpRuntimeModuleError: SpRuntimeModuleError; + SpRuntimeMultiSignature: SpRuntimeMultiSignature; + SpRuntimeTokenError: SpRuntimeTokenError; + SpRuntimeTransactionalError: SpRuntimeTransactionalError; + SpRuntimeTransactionValidityInvalidTransaction: SpRuntimeTransactionValidityInvalidTransaction; + SpRuntimeTransactionValidityTransactionValidityError: SpRuntimeTransactionValidityTransactionValidityError; + SpRuntimeTransactionValidityUnknownTransaction: SpRuntimeTransactionValidityUnknownTransaction; + SpTrieStorageProof: SpTrieStorageProof; + SpVersionRuntimeVersion: SpVersionRuntimeVersion; + SpWeightsRuntimeDbWeight: SpWeightsRuntimeDbWeight; + SpWeightsWeightV2Weight: SpWeightsWeightV2Weight; + Sr25519Signature: Sr25519Signature; + StagingXcmDoubleEncoded: StagingXcmDoubleEncoded; + StagingXcmV2BodyId: StagingXcmV2BodyId; + StagingXcmV2BodyPart: StagingXcmV2BodyPart; + StagingXcmV2Instruction: StagingXcmV2Instruction; + StagingXcmV2Junction: StagingXcmV2Junction; + StagingXcmV2MultiAsset: StagingXcmV2MultiAsset; + StagingXcmV2MultiassetAssetId: StagingXcmV2MultiassetAssetId; + StagingXcmV2MultiassetAssetInstance: StagingXcmV2MultiassetAssetInstance; + StagingXcmV2MultiassetFungibility: StagingXcmV2MultiassetFungibility; + StagingXcmV2MultiassetMultiAssetFilter: StagingXcmV2MultiassetMultiAssetFilter; + StagingXcmV2MultiassetMultiAssets: StagingXcmV2MultiassetMultiAssets; + StagingXcmV2MultiassetWildFungibility: StagingXcmV2MultiassetWildFungibility; + StagingXcmV2MultiassetWildMultiAsset: StagingXcmV2MultiassetWildMultiAsset; + StagingXcmV2MultiLocation: StagingXcmV2MultiLocation; + StagingXcmV2MultilocationJunctions: StagingXcmV2MultilocationJunctions; + StagingXcmV2NetworkId: StagingXcmV2NetworkId; + StagingXcmV2OriginKind: StagingXcmV2OriginKind; + StagingXcmV2Response: StagingXcmV2Response; + StagingXcmV2TraitsError: StagingXcmV2TraitsError; + StagingXcmV2WeightLimit: StagingXcmV2WeightLimit; + StagingXcmV2Xcm: StagingXcmV2Xcm; + StagingXcmV3Instruction: StagingXcmV3Instruction; + StagingXcmV3Junction: StagingXcmV3Junction; + StagingXcmV3JunctionBodyId: StagingXcmV3JunctionBodyId; + StagingXcmV3JunctionBodyPart: StagingXcmV3JunctionBodyPart; + StagingXcmV3JunctionNetworkId: StagingXcmV3JunctionNetworkId; + StagingXcmV3Junctions: StagingXcmV3Junctions; + StagingXcmV3MaybeErrorCode: StagingXcmV3MaybeErrorCode; + StagingXcmV3MultiAsset: StagingXcmV3MultiAsset; + StagingXcmV3MultiassetAssetId: StagingXcmV3MultiassetAssetId; + StagingXcmV3MultiassetAssetInstance: StagingXcmV3MultiassetAssetInstance; + StagingXcmV3MultiassetFungibility: StagingXcmV3MultiassetFungibility; + StagingXcmV3MultiassetMultiAssetFilter: StagingXcmV3MultiassetMultiAssetFilter; + StagingXcmV3MultiassetMultiAssets: StagingXcmV3MultiassetMultiAssets; + StagingXcmV3MultiassetWildFungibility: StagingXcmV3MultiassetWildFungibility; + StagingXcmV3MultiassetWildMultiAsset: StagingXcmV3MultiassetWildMultiAsset; + StagingXcmV3MultiLocation: StagingXcmV3MultiLocation; + StagingXcmV3PalletInfo: StagingXcmV3PalletInfo; + StagingXcmV3QueryResponseInfo: StagingXcmV3QueryResponseInfo; + StagingXcmV3Response: StagingXcmV3Response; + StagingXcmV3TraitsError: StagingXcmV3TraitsError; + StagingXcmV3TraitsOutcome: StagingXcmV3TraitsOutcome; + StagingXcmV3WeightLimit: StagingXcmV3WeightLimit; + StagingXcmV3Xcm: StagingXcmV3Xcm; + StagingXcmVersionedAssetId: StagingXcmVersionedAssetId; + StagingXcmVersionedMultiAsset: StagingXcmVersionedMultiAsset; + StagingXcmVersionedMultiAssets: StagingXcmVersionedMultiAssets; + StagingXcmVersionedMultiLocation: StagingXcmVersionedMultiLocation; + StagingXcmVersionedResponse: StagingXcmVersionedResponse; + StagingXcmVersionedXcm: StagingXcmVersionedXcm; + StakingLedger: StakingLedger; + StakingLedgerTo223: StakingLedgerTo223; + StakingLedgerTo240: StakingLedgerTo240; + Statement: Statement; + StatementKind: StatementKind; + StorageChangeSet: StorageChangeSet; + StorageData: StorageData; + StorageDeposit: StorageDeposit; + StorageEntryMetadataLatest: StorageEntryMetadataLatest; + StorageEntryMetadataV10: StorageEntryMetadataV10; + StorageEntryMetadataV11: StorageEntryMetadataV11; + StorageEntryMetadataV12: StorageEntryMetadataV12; + StorageEntryMetadataV13: StorageEntryMetadataV13; + StorageEntryMetadataV14: StorageEntryMetadataV14; + StorageEntryMetadataV9: StorageEntryMetadataV9; + StorageEntryModifierLatest: StorageEntryModifierLatest; + StorageEntryModifierV10: StorageEntryModifierV10; + StorageEntryModifierV11: StorageEntryModifierV11; + StorageEntryModifierV12: StorageEntryModifierV12; + StorageEntryModifierV13: StorageEntryModifierV13; + StorageEntryModifierV14: StorageEntryModifierV14; + StorageEntryModifierV9: StorageEntryModifierV9; + StorageEntryTypeLatest: StorageEntryTypeLatest; + StorageEntryTypeV10: StorageEntryTypeV10; + StorageEntryTypeV11: StorageEntryTypeV11; + StorageEntryTypeV12: StorageEntryTypeV12; + StorageEntryTypeV13: StorageEntryTypeV13; + StorageEntryTypeV14: StorageEntryTypeV14; + StorageEntryTypeV9: StorageEntryTypeV9; + StorageHasher: StorageHasher; + StorageHasherV10: StorageHasherV10; + StorageHasherV11: StorageHasherV11; + StorageHasherV12: StorageHasherV12; + StorageHasherV13: StorageHasherV13; + StorageHasherV14: StorageHasherV14; + StorageHasherV9: StorageHasherV9; + StorageInfo: StorageInfo; + StorageKey: StorageKey; + StorageKind: StorageKind; + StorageMetadataV10: StorageMetadataV10; + StorageMetadataV11: StorageMetadataV11; + StorageMetadataV12: StorageMetadataV12; + StorageMetadataV13: StorageMetadataV13; + StorageMetadataV9: StorageMetadataV9; + StorageProof: StorageProof; + StoredPendingChange: StoredPendingChange; + StoredState: StoredState; + StrikeCount: StrikeCount; + SubId: SubId; + SubmissionIndicesOf: SubmissionIndicesOf; + Supports: Supports; + SyncState: SyncState; + SystemInherentData: SystemInherentData; + SystemOrigin: SystemOrigin; + Tally: Tally; + TaskAddress: TaskAddress; + TAssetBalance: TAssetBalance; + TAssetDepositBalance: TAssetDepositBalance; + Text: Text; + Timepoint: Timepoint; + TokenError: TokenError; + TombstoneContractInfo: TombstoneContractInfo; + TraceBlockResponse: TraceBlockResponse; + TraceError: TraceError; + TransactionalError: TransactionalError; + TransactionInfo: TransactionInfo; + TransactionLongevity: TransactionLongevity; + TransactionPriority: TransactionPriority; + TransactionSource: TransactionSource; + TransactionStorageProof: TransactionStorageProof; + TransactionTag: TransactionTag; + TransactionV0: TransactionV0; + TransactionV1: TransactionV1; + TransactionV2: TransactionV2; + TransactionValidity: TransactionValidity; + TransactionValidityError: TransactionValidityError; + TransientValidationData: TransientValidationData; + TreasuryProposal: TreasuryProposal; + TrieId: TrieId; + TrieIndex: TrieIndex; + Type: Type; + u128: u128; + U128: U128; + u16: u16; + U16: U16; + u256: u256; + U256: U256; + u32: u32; + U32: U32; + U32F32: U32F32; + u64: u64; + U64: U64; + u8: u8; + U8: U8; + UnappliedSlash: UnappliedSlash; + UnappliedSlashOther: UnappliedSlashOther; + UncleEntryItem: UncleEntryItem; + UnknownTransaction: UnknownTransaction; + UnlockChunk: UnlockChunk; + UnrewardedRelayer: UnrewardedRelayer; + UnrewardedRelayersState: UnrewardedRelayersState; + UpDataStructsAccessMode: UpDataStructsAccessMode; + UpDataStructsCollection: UpDataStructsCollection; + UpDataStructsCollectionLimits: UpDataStructsCollectionLimits; + UpDataStructsCollectionMode: UpDataStructsCollectionMode; + UpDataStructsCollectionPermissions: UpDataStructsCollectionPermissions; + UpDataStructsCollectionStats: UpDataStructsCollectionStats; + UpDataStructsCreateCollectionData: UpDataStructsCreateCollectionData; + UpDataStructsCreateFungibleData: UpDataStructsCreateFungibleData; + UpDataStructsCreateItemData: UpDataStructsCreateItemData; + UpDataStructsCreateItemExData: UpDataStructsCreateItemExData; + UpDataStructsCreateNftData: UpDataStructsCreateNftData; + UpDataStructsCreateNftExData: UpDataStructsCreateNftExData; + UpDataStructsCreateReFungibleData: UpDataStructsCreateReFungibleData; + UpDataStructsCreateRefungibleExMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners; + UpDataStructsCreateRefungibleExSingleOwner: UpDataStructsCreateRefungibleExSingleOwner; + UpDataStructsNestingPermissions: UpDataStructsNestingPermissions; + UpDataStructsOwnerRestrictedSet: UpDataStructsOwnerRestrictedSet; + UpDataStructsProperties: UpDataStructsProperties; + UpDataStructsPropertiesMapBoundedVec: UpDataStructsPropertiesMapBoundedVec; + UpDataStructsPropertiesMapPropertyPermission: UpDataStructsPropertiesMapPropertyPermission; + UpDataStructsProperty: UpDataStructsProperty; + UpDataStructsPropertyKeyPermission: UpDataStructsPropertyKeyPermission; + UpDataStructsPropertyPermission: UpDataStructsPropertyPermission; + UpDataStructsPropertyScope: UpDataStructsPropertyScope; + UpDataStructsRpcCollection: UpDataStructsRpcCollection; + UpDataStructsRpcCollectionFlags: UpDataStructsRpcCollectionFlags; + UpDataStructsSponsoringRateLimit: UpDataStructsSponsoringRateLimit; + UpDataStructsSponsorshipStateAccountId32: UpDataStructsSponsorshipStateAccountId32; + UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: UpDataStructsSponsorshipStateBasicCrossAccountIdRepr; + UpDataStructsTokenChild: UpDataStructsTokenChild; + UpDataStructsTokenData: UpDataStructsTokenData; + UpgradeGoAhead: UpgradeGoAhead; + UpgradeRestriction: UpgradeRestriction; + UpPovEstimateRpcPovInfo: UpPovEstimateRpcPovInfo; + UpPovEstimateRpcTrieKeyValue: UpPovEstimateRpcTrieKeyValue; + UpwardMessage: UpwardMessage; + usize: usize; + USize: USize; + ValidationCode: ValidationCode; + ValidationCodeHash: ValidationCodeHash; + ValidationData: ValidationData; + ValidationDataType: ValidationDataType; + ValidationFunctionParams: ValidationFunctionParams; + ValidatorCount: ValidatorCount; + ValidatorId: ValidatorId; + ValidatorIdOf: ValidatorIdOf; + ValidatorIndex: ValidatorIndex; + ValidatorIndexCompact: ValidatorIndexCompact; + ValidatorPrefs: ValidatorPrefs; + ValidatorPrefsTo145: ValidatorPrefsTo145; + ValidatorPrefsTo196: ValidatorPrefsTo196; + ValidatorPrefsWithBlocked: ValidatorPrefsWithBlocked; + ValidatorPrefsWithCommission: ValidatorPrefsWithCommission; + ValidatorSet: ValidatorSet; + ValidatorSetId: ValidatorSetId; + ValidatorSignature: ValidatorSignature; + ValidDisputeStatementKind: ValidDisputeStatementKind; + ValidityAttestation: ValidityAttestation; + ValidTransaction: ValidTransaction; + VecInboundHrmpMessage: VecInboundHrmpMessage; + VersionedMultiAsset: VersionedMultiAsset; + VersionedMultiAssets: VersionedMultiAssets; + VersionedMultiLocation: VersionedMultiLocation; + VersionedResponse: VersionedResponse; + VersionedXcm: VersionedXcm; + VersionMigrationStage: VersionMigrationStage; + VestingInfo: VestingInfo; + VestingSchedule: VestingSchedule; + Vote: Vote; + VoteIndex: VoteIndex; + Voter: Voter; + VoterInfo: VoterInfo; + Votes: Votes; + VotesTo230: VotesTo230; + VoteThreshold: VoteThreshold; + VoteWeight: VoteWeight; + Voting: Voting; + VotingDelegating: VotingDelegating; + VotingDirect: VotingDirect; + VotingDirectVote: VotingDirectVote; + VouchingStatus: VouchingStatus; + VrfData: VrfData; + VrfOutput: VrfOutput; + VrfProof: VrfProof; + Weight: Weight; + WeightLimitV2: WeightLimitV2; + WeightMultiplier: WeightMultiplier; + WeightPerClass: WeightPerClass; + WeightToFeeCoefficient: WeightToFeeCoefficient; + WeightV0: WeightV0; + WeightV1: WeightV1; + WeightV2: WeightV2; + WildFungibility: WildFungibility; + WildFungibilityV0: WildFungibilityV0; + WildFungibilityV1: WildFungibilityV1; + WildFungibilityV2: WildFungibilityV2; + WildMultiAsset: WildMultiAsset; + WildMultiAssetV1: WildMultiAssetV1; + WildMultiAssetV2: WildMultiAssetV2; + WinnersData: WinnersData; + WinnersData10: WinnersData10; + WinnersDataTuple: WinnersDataTuple; + WinnersDataTuple10: WinnersDataTuple10; + WinningData: WinningData; + WinningData10: WinningData10; + WinningDataEntry: WinningDataEntry; + WithdrawReasons: WithdrawReasons; + Xcm: Xcm; + XcmAssetId: XcmAssetId; + XcmError: XcmError; + XcmErrorV0: XcmErrorV0; + XcmErrorV1: XcmErrorV1; + XcmErrorV2: XcmErrorV2; + XcmOrder: XcmOrder; + XcmOrderV0: XcmOrderV0; + XcmOrderV1: XcmOrderV1; + XcmOrderV2: XcmOrderV2; + XcmOrigin: XcmOrigin; + XcmOriginKind: XcmOriginKind; + XcmpMessageFormat: XcmpMessageFormat; + XcmV0: XcmV0; + XcmV1: XcmV1; + XcmV2: XcmV2; + XcmVersion: XcmVersion; + } // InterfaceTypes +} // declare module --- /dev/null +++ b/js-packages/types/default/definitions.ts @@ -0,0 +1,6 @@ +import types from '../lookup.js'; + +export default { + types, + rpc: {}, +}; --- /dev/null +++ b/js-packages/types/default/index.ts @@ -0,0 +1,4 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +export * from './types.js'; --- /dev/null +++ b/js-packages/types/default/types.ts @@ -0,0 +1,5537 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +import type { Data } from '@polkadot/types'; +import type { BTreeMap, BTreeSet, Bytes, Compact, Enum, Null, Option, Result, Struct, Text, U256, U8aFixed, Vec, bool, i64, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; +import type { ITuple } from '@polkadot/types-codec/types'; +import type { Vote } from '@polkadot/types/interfaces/elections'; +import type { AccountId32, Call, H160, H256, MultiAddress, Perbill } from '@polkadot/types/interfaces/runtime'; +import type { Event } from '@polkadot/types/interfaces/system'; + +/** @name CumulusPalletDmpQueueCall */ +export interface CumulusPalletDmpQueueCall extends Enum { + readonly isServiceOverweight: boolean; + readonly asServiceOverweight: { + readonly index: u64; + readonly weightLimit: SpWeightsWeightV2Weight; + } & Struct; + readonly type: 'ServiceOverweight'; +} + +/** @name CumulusPalletDmpQueueConfigData */ +export interface CumulusPalletDmpQueueConfigData extends Struct { + readonly maxIndividual: SpWeightsWeightV2Weight; +} + +/** @name CumulusPalletDmpQueueError */ +export interface CumulusPalletDmpQueueError extends Enum { + readonly isUnknown: boolean; + readonly isOverLimit: boolean; + readonly type: 'Unknown' | 'OverLimit'; +} + +/** @name CumulusPalletDmpQueueEvent */ +export interface CumulusPalletDmpQueueEvent extends Enum { + readonly isInvalidFormat: boolean; + readonly asInvalidFormat: { + readonly messageHash: U8aFixed; + } & Struct; + readonly isUnsupportedVersion: boolean; + readonly asUnsupportedVersion: { + readonly messageHash: U8aFixed; + } & Struct; + readonly isExecutedDownward: boolean; + readonly asExecutedDownward: { + readonly messageHash: U8aFixed; + readonly messageId: U8aFixed; + readonly outcome: StagingXcmV3TraitsOutcome; + } & Struct; + readonly isWeightExhausted: boolean; + readonly asWeightExhausted: { + readonly messageHash: U8aFixed; + readonly messageId: U8aFixed; + readonly remainingWeight: SpWeightsWeightV2Weight; + readonly requiredWeight: SpWeightsWeightV2Weight; + } & Struct; + readonly isOverweightEnqueued: boolean; + readonly asOverweightEnqueued: { + readonly messageHash: U8aFixed; + readonly messageId: U8aFixed; + readonly overweightIndex: u64; + readonly requiredWeight: SpWeightsWeightV2Weight; + } & Struct; + readonly isOverweightServiced: boolean; + readonly asOverweightServiced: { + readonly overweightIndex: u64; + readonly weightUsed: SpWeightsWeightV2Weight; + } & Struct; + readonly isMaxMessagesExhausted: boolean; + readonly asMaxMessagesExhausted: { + readonly messageHash: U8aFixed; + } & Struct; + readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced' | 'MaxMessagesExhausted'; +} + +/** @name CumulusPalletDmpQueuePageIndexData */ +export interface CumulusPalletDmpQueuePageIndexData extends Struct { + readonly beginUsed: u32; + readonly endUsed: u32; + readonly overweightCount: u64; +} + +/** @name CumulusPalletParachainSystemCall */ +export interface CumulusPalletParachainSystemCall extends Enum { + readonly isSetValidationData: boolean; + readonly asSetValidationData: { + readonly data: CumulusPrimitivesParachainInherentParachainInherentData; + } & Struct; + readonly isSudoSendUpwardMessage: boolean; + readonly asSudoSendUpwardMessage: { + readonly message: Bytes; + } & Struct; + readonly isAuthorizeUpgrade: boolean; + readonly asAuthorizeUpgrade: { + readonly codeHash: H256; + readonly checkVersion: bool; + } & Struct; + readonly isEnactAuthorizedUpgrade: boolean; + readonly asEnactAuthorizedUpgrade: { + readonly code: Bytes; + } & Struct; + readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade'; +} + +/** @name CumulusPalletParachainSystemCodeUpgradeAuthorization */ +export interface CumulusPalletParachainSystemCodeUpgradeAuthorization extends Struct { + readonly codeHash: H256; + readonly checkVersion: bool; +} + +/** @name CumulusPalletParachainSystemError */ +export interface CumulusPalletParachainSystemError extends Enum { + readonly isOverlappingUpgrades: boolean; + readonly isProhibitedByPolkadot: boolean; + readonly isTooBig: boolean; + readonly isValidationDataNotAvailable: boolean; + readonly isHostConfigurationNotAvailable: boolean; + readonly isNotScheduled: boolean; + readonly isNothingAuthorized: boolean; + readonly isUnauthorized: boolean; + readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized'; +} + +/** @name CumulusPalletParachainSystemEvent */ +export interface CumulusPalletParachainSystemEvent extends Enum { + readonly isValidationFunctionStored: boolean; + readonly isValidationFunctionApplied: boolean; + readonly asValidationFunctionApplied: { + readonly relayChainBlockNum: u32; + } & Struct; + readonly isValidationFunctionDiscarded: boolean; + readonly isUpgradeAuthorized: boolean; + readonly asUpgradeAuthorized: { + readonly codeHash: H256; + } & Struct; + readonly isDownwardMessagesReceived: boolean; + readonly asDownwardMessagesReceived: { + readonly count: u32; + } & Struct; + readonly isDownwardMessagesProcessed: boolean; + readonly asDownwardMessagesProcessed: { + readonly weightUsed: SpWeightsWeightV2Weight; + readonly dmqHead: H256; + } & Struct; + readonly isUpwardMessageSent: boolean; + readonly asUpwardMessageSent: { + readonly messageHash: Option; + } & Struct; + readonly type: 'ValidationFunctionStored' | 'ValidationFunctionApplied' | 'ValidationFunctionDiscarded' | 'UpgradeAuthorized' | 'DownwardMessagesReceived' | 'DownwardMessagesProcessed' | 'UpwardMessageSent'; +} + +/** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot */ +export interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct { + readonly dmqMqcHead: H256; + readonly relayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity; + readonly ingressChannels: Vec>; + readonly egressChannels: Vec>; +} + +/** @name CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity */ +export interface CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity extends Struct { + readonly remainingCount: u32; + readonly remainingSize: u32; +} + +/** @name CumulusPalletParachainSystemUnincludedSegmentAncestor */ +export interface CumulusPalletParachainSystemUnincludedSegmentAncestor extends Struct { + readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth; + readonly paraHeadHash: Option; + readonly consumedGoAheadSignal: Option; +} + +/** @name CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate */ +export interface CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate extends Struct { + readonly msgCount: u32; + readonly totalBytes: u32; +} + +/** @name CumulusPalletParachainSystemUnincludedSegmentSegmentTracker */ +export interface CumulusPalletParachainSystemUnincludedSegmentSegmentTracker extends Struct { + readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth; + readonly hrmpWatermark: Option; + readonly consumedGoAheadSignal: Option; +} + +/** @name CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth */ +export interface CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth extends Struct { + readonly umpMsgCount: u32; + readonly umpTotalBytes: u32; + readonly hrmpOutgoing: BTreeMap; +} + +/** @name CumulusPalletXcmCall */ +export interface CumulusPalletXcmCall extends Null {} + +/** @name CumulusPalletXcmError */ +export interface CumulusPalletXcmError extends Null {} + +/** @name CumulusPalletXcmEvent */ +export interface CumulusPalletXcmEvent extends Enum { + readonly isInvalidFormat: boolean; + readonly asInvalidFormat: U8aFixed; + readonly isUnsupportedVersion: boolean; + readonly asUnsupportedVersion: U8aFixed; + readonly isExecutedDownward: boolean; + readonly asExecutedDownward: ITuple<[U8aFixed, StagingXcmV3TraitsOutcome]>; + readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward'; +} + +/** @name CumulusPalletXcmOrigin */ +export interface CumulusPalletXcmOrigin extends Enum { + readonly isRelay: boolean; + readonly isSiblingParachain: boolean; + readonly asSiblingParachain: u32; + readonly type: 'Relay' | 'SiblingParachain'; +} + +/** @name CumulusPalletXcmpQueueCall */ +export interface CumulusPalletXcmpQueueCall extends Enum { + readonly isServiceOverweight: boolean; + readonly asServiceOverweight: { + readonly index: u64; + readonly weightLimit: SpWeightsWeightV2Weight; + } & Struct; + readonly isSuspendXcmExecution: boolean; + readonly isResumeXcmExecution: boolean; + readonly isUpdateSuspendThreshold: boolean; + readonly asUpdateSuspendThreshold: { + readonly new_: u32; + } & Struct; + readonly isUpdateDropThreshold: boolean; + readonly asUpdateDropThreshold: { + readonly new_: u32; + } & Struct; + readonly isUpdateResumeThreshold: boolean; + readonly asUpdateResumeThreshold: { + readonly new_: u32; + } & Struct; + readonly isUpdateThresholdWeight: boolean; + readonly asUpdateThresholdWeight: { + readonly new_: SpWeightsWeightV2Weight; + } & Struct; + readonly isUpdateWeightRestrictDecay: boolean; + readonly asUpdateWeightRestrictDecay: { + readonly new_: SpWeightsWeightV2Weight; + } & Struct; + readonly isUpdateXcmpMaxIndividualWeight: boolean; + readonly asUpdateXcmpMaxIndividualWeight: { + readonly new_: SpWeightsWeightV2Weight; + } & Struct; + readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight'; +} + +/** @name CumulusPalletXcmpQueueError */ +export interface CumulusPalletXcmpQueueError extends Enum { + readonly isFailedToSend: boolean; + readonly isBadXcmOrigin: boolean; + readonly isBadXcm: boolean; + readonly isBadOverweightIndex: boolean; + readonly isWeightOverLimit: boolean; + readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit'; +} + +/** @name CumulusPalletXcmpQueueEvent */ +export interface CumulusPalletXcmpQueueEvent extends Enum { + readonly isSuccess: boolean; + readonly asSuccess: { + readonly messageHash: U8aFixed; + readonly messageId: U8aFixed; + readonly weight: SpWeightsWeightV2Weight; + } & Struct; + readonly isFail: boolean; + readonly asFail: { + readonly messageHash: U8aFixed; + readonly messageId: U8aFixed; + readonly error: StagingXcmV3TraitsError; + readonly weight: SpWeightsWeightV2Weight; + } & Struct; + readonly isBadVersion: boolean; + readonly asBadVersion: { + readonly messageHash: U8aFixed; + } & Struct; + readonly isBadFormat: boolean; + readonly asBadFormat: { + readonly messageHash: U8aFixed; + } & Struct; + readonly isXcmpMessageSent: boolean; + readonly asXcmpMessageSent: { + readonly messageHash: U8aFixed; + } & Struct; + readonly isOverweightEnqueued: boolean; + readonly asOverweightEnqueued: { + readonly sender: u32; + readonly sentAt: u32; + readonly index: u64; + readonly required: SpWeightsWeightV2Weight; + } & Struct; + readonly isOverweightServiced: boolean; + readonly asOverweightServiced: { + readonly index: u64; + readonly used: SpWeightsWeightV2Weight; + } & Struct; + readonly type: 'Success' | 'Fail' | 'BadVersion' | 'BadFormat' | 'XcmpMessageSent' | 'OverweightEnqueued' | 'OverweightServiced'; +} + +/** @name CumulusPalletXcmpQueueInboundChannelDetails */ +export interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct { + readonly sender: u32; + readonly state: CumulusPalletXcmpQueueInboundState; + readonly messageMetadata: Vec>; +} + +/** @name CumulusPalletXcmpQueueInboundState */ +export interface CumulusPalletXcmpQueueInboundState extends Enum { + readonly isOk: boolean; + readonly isSuspended: boolean; + readonly type: 'Ok' | 'Suspended'; +} + +/** @name CumulusPalletXcmpQueueOutboundChannelDetails */ +export interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct { + readonly recipient: u32; + readonly state: CumulusPalletXcmpQueueOutboundState; + readonly signalsExist: bool; + readonly firstIndex: u16; + readonly lastIndex: u16; +} + +/** @name CumulusPalletXcmpQueueOutboundState */ +export interface CumulusPalletXcmpQueueOutboundState extends Enum { + readonly isOk: boolean; + readonly isSuspended: boolean; + readonly type: 'Ok' | 'Suspended'; +} + +/** @name CumulusPalletXcmpQueueQueueConfigData */ +export interface CumulusPalletXcmpQueueQueueConfigData extends Struct { + readonly suspendThreshold: u32; + readonly dropThreshold: u32; + readonly resumeThreshold: u32; + readonly thresholdWeight: SpWeightsWeightV2Weight; + readonly weightRestrictDecay: SpWeightsWeightV2Weight; + readonly xcmpMaxIndividualWeight: SpWeightsWeightV2Weight; +} + +/** @name CumulusPrimitivesParachainInherentParachainInherentData */ +export interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct { + readonly validationData: PolkadotPrimitivesV5PersistedValidationData; + readonly relayChainState: SpTrieStorageProof; + readonly downwardMessages: Vec; + readonly horizontalMessages: BTreeMap>; +} + +/** @name EthbloomBloom */ +export interface EthbloomBloom extends U8aFixed {} + +/** @name EthereumBlock */ +export interface EthereumBlock extends Struct { + readonly header: EthereumHeader; + readonly transactions: Vec; + readonly ommers: Vec; +} + +/** @name EthereumHeader */ +export interface EthereumHeader extends Struct { + readonly parentHash: H256; + readonly ommersHash: H256; + readonly beneficiary: H160; + readonly stateRoot: H256; + readonly transactionsRoot: H256; + readonly receiptsRoot: H256; + readonly logsBloom: EthbloomBloom; + readonly difficulty: U256; + readonly number: U256; + readonly gasLimit: U256; + readonly gasUsed: U256; + readonly timestamp: u64; + readonly extraData: Bytes; + readonly mixHash: H256; + readonly nonce: EthereumTypesHashH64; +} + +/** @name EthereumLog */ +export interface EthereumLog extends Struct { + readonly address: H160; + readonly topics: Vec; + readonly data: Bytes; +} + +/** @name EthereumReceiptEip658ReceiptData */ +export interface EthereumReceiptEip658ReceiptData extends Struct { + readonly statusCode: u8; + readonly usedGas: U256; + readonly logsBloom: EthbloomBloom; + readonly logs: Vec; +} + +/** @name EthereumReceiptReceiptV3 */ +export interface EthereumReceiptReceiptV3 extends Enum { + readonly isLegacy: boolean; + readonly asLegacy: EthereumReceiptEip658ReceiptData; + readonly isEip2930: boolean; + readonly asEip2930: EthereumReceiptEip658ReceiptData; + readonly isEip1559: boolean; + readonly asEip1559: EthereumReceiptEip658ReceiptData; + readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; +} + +/** @name EthereumTransactionAccessListItem */ +export interface EthereumTransactionAccessListItem extends Struct { + readonly address: H160; + readonly storageKeys: Vec; +} + +/** @name EthereumTransactionEip1559Transaction */ +export interface EthereumTransactionEip1559Transaction extends Struct { + readonly chainId: u64; + readonly nonce: U256; + readonly maxPriorityFeePerGas: U256; + readonly maxFeePerGas: U256; + readonly gasLimit: U256; + readonly action: EthereumTransactionTransactionAction; + readonly value: U256; + readonly input: Bytes; + readonly accessList: Vec; + readonly oddYParity: bool; + readonly r: H256; + readonly s: H256; +} + +/** @name EthereumTransactionEip2930Transaction */ +export interface EthereumTransactionEip2930Transaction extends Struct { + readonly chainId: u64; + readonly nonce: U256; + readonly gasPrice: U256; + readonly gasLimit: U256; + readonly action: EthereumTransactionTransactionAction; + readonly value: U256; + readonly input: Bytes; + readonly accessList: Vec; + readonly oddYParity: bool; + readonly r: H256; + readonly s: H256; +} + +/** @name EthereumTransactionLegacyTransaction */ +export interface EthereumTransactionLegacyTransaction extends Struct { + readonly nonce: U256; + readonly gasPrice: U256; + readonly gasLimit: U256; + readonly action: EthereumTransactionTransactionAction; + readonly value: U256; + readonly input: Bytes; + readonly signature: EthereumTransactionTransactionSignature; +} + +/** @name EthereumTransactionTransactionAction */ +export interface EthereumTransactionTransactionAction extends Enum { + readonly isCall: boolean; + readonly asCall: H160; + readonly isCreate: boolean; + readonly type: 'Call' | 'Create'; +} + +/** @name EthereumTransactionTransactionSignature */ +export interface EthereumTransactionTransactionSignature extends Struct { + readonly v: u64; + readonly r: H256; + readonly s: H256; +} + +/** @name EthereumTransactionTransactionV2 */ +export interface EthereumTransactionTransactionV2 extends Enum { + readonly isLegacy: boolean; + readonly asLegacy: EthereumTransactionLegacyTransaction; + readonly isEip2930: boolean; + readonly asEip2930: EthereumTransactionEip2930Transaction; + readonly isEip1559: boolean; + readonly asEip1559: EthereumTransactionEip1559Transaction; + readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; +} + +/** @name EthereumTypesHashH64 */ +export interface EthereumTypesHashH64 extends U8aFixed {} + +/** @name EvmCoreErrorExitError */ +export interface EvmCoreErrorExitError extends Enum { + readonly isStackUnderflow: boolean; + readonly isStackOverflow: boolean; + readonly isInvalidJump: boolean; + readonly isInvalidRange: boolean; + readonly isDesignatedInvalid: boolean; + readonly isCallTooDeep: boolean; + readonly isCreateCollision: boolean; + readonly isCreateContractLimit: boolean; + readonly isOutOfOffset: boolean; + readonly isOutOfGas: boolean; + readonly isOutOfFund: boolean; + readonly isPcUnderflow: boolean; + readonly isCreateEmpty: boolean; + readonly isOther: boolean; + readonly asOther: Text; + readonly isMaxNonce: boolean; + readonly isInvalidCode: boolean; + readonly asInvalidCode: u8; + readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'MaxNonce' | 'InvalidCode'; +} + +/** @name EvmCoreErrorExitFatal */ +export interface EvmCoreErrorExitFatal extends Enum { + readonly isNotSupported: boolean; + readonly isUnhandledInterrupt: boolean; + readonly isCallErrorAsFatal: boolean; + readonly asCallErrorAsFatal: EvmCoreErrorExitError; + readonly isOther: boolean; + readonly asOther: Text; + readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other'; +} + +/** @name EvmCoreErrorExitReason */ +export interface EvmCoreErrorExitReason extends Enum { + readonly isSucceed: boolean; + readonly asSucceed: EvmCoreErrorExitSucceed; + readonly isError: boolean; + readonly asError: EvmCoreErrorExitError; + readonly isRevert: boolean; + readonly asRevert: EvmCoreErrorExitRevert; + readonly isFatal: boolean; + readonly asFatal: EvmCoreErrorExitFatal; + readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal'; +} + +/** @name EvmCoreErrorExitRevert */ +export interface EvmCoreErrorExitRevert extends Enum { + readonly isReverted: boolean; + readonly type: 'Reverted'; +} + +/** @name EvmCoreErrorExitSucceed */ +export interface EvmCoreErrorExitSucceed extends Enum { + readonly isStopped: boolean; + readonly isReturned: boolean; + readonly isSuicided: boolean; + readonly type: 'Stopped' | 'Returned' | 'Suicided'; +} + +/** @name FpRpcTransactionStatus */ +export interface FpRpcTransactionStatus extends Struct { + readonly transactionHash: H256; + readonly transactionIndex: u32; + readonly from: H160; + readonly to: Option; + readonly contractAddress: Option; + readonly logs: Vec; + readonly logsBloom: EthbloomBloom; +} + +/** @name FrameSupportDispatchDispatchClass */ +export interface FrameSupportDispatchDispatchClass extends Enum { + readonly isNormal: boolean; + readonly isOperational: boolean; + readonly isMandatory: boolean; + readonly type: 'Normal' | 'Operational' | 'Mandatory'; +} + +/** @name FrameSupportDispatchDispatchInfo */ +export interface FrameSupportDispatchDispatchInfo extends Struct { + readonly weight: SpWeightsWeightV2Weight; + readonly class: FrameSupportDispatchDispatchClass; + readonly paysFee: FrameSupportDispatchPays; +} + +/** @name FrameSupportDispatchPays */ +export interface FrameSupportDispatchPays extends Enum { + readonly isYes: boolean; + readonly isNo: boolean; + readonly type: 'Yes' | 'No'; +} + +/** @name FrameSupportDispatchPerDispatchClassU32 */ +export interface FrameSupportDispatchPerDispatchClassU32 extends Struct { + readonly normal: u32; + readonly operational: u32; + readonly mandatory: u32; +} + +/** @name FrameSupportDispatchPerDispatchClassWeight */ +export interface FrameSupportDispatchPerDispatchClassWeight extends Struct { + readonly normal: SpWeightsWeightV2Weight; + readonly operational: SpWeightsWeightV2Weight; + readonly mandatory: SpWeightsWeightV2Weight; +} + +/** @name FrameSupportDispatchPerDispatchClassWeightsPerClass */ +export interface FrameSupportDispatchPerDispatchClassWeightsPerClass extends Struct { + readonly normal: FrameSystemLimitsWeightsPerClass; + readonly operational: FrameSystemLimitsWeightsPerClass; + readonly mandatory: FrameSystemLimitsWeightsPerClass; +} + +/** @name FrameSupportDispatchRawOrigin */ +export interface FrameSupportDispatchRawOrigin extends Enum { + readonly isRoot: boolean; + readonly isSigned: boolean; + readonly asSigned: AccountId32; + readonly isNone: boolean; + readonly type: 'Root' | 'Signed' | 'None'; +} + +/** @name FrameSupportPalletId */ +export interface FrameSupportPalletId extends U8aFixed {} + +/** @name FrameSupportPreimagesBounded */ +export interface FrameSupportPreimagesBounded extends Enum { + readonly isLegacy: boolean; + readonly asLegacy: { + readonly hash_: H256; + } & Struct; + readonly isInline: boolean; + readonly asInline: Bytes; + readonly isLookup: boolean; + readonly asLookup: { + readonly hash_: H256; + readonly len: u32; + } & Struct; + readonly type: 'Legacy' | 'Inline' | 'Lookup'; +} + +/** @name FrameSupportScheduleDispatchTime */ +export interface FrameSupportScheduleDispatchTime extends Enum { + readonly isAt: boolean; + readonly asAt: u32; + readonly isAfter: boolean; + readonly asAfter: u32; + readonly type: 'At' | 'After'; +} + +/** @name FrameSupportTokensMiscBalanceStatus */ +export interface FrameSupportTokensMiscBalanceStatus extends Enum { + readonly isFree: boolean; + readonly isReserved: boolean; + readonly type: 'Free' | 'Reserved'; +} + +/** @name FrameSystemAccountInfo */ +export interface FrameSystemAccountInfo extends Struct { + readonly nonce: u32; + readonly consumers: u32; + readonly providers: u32; + readonly sufficients: u32; + readonly data: PalletBalancesAccountData; +} + +/** @name FrameSystemCall */ +export interface FrameSystemCall extends Enum { + readonly isRemark: boolean; + readonly asRemark: { + readonly remark: Bytes; + } & Struct; + readonly isSetHeapPages: boolean; + readonly asSetHeapPages: { + readonly pages: u64; + } & Struct; + readonly isSetCode: boolean; + readonly asSetCode: { + readonly code: Bytes; + } & Struct; + readonly isSetCodeWithoutChecks: boolean; + readonly asSetCodeWithoutChecks: { + readonly code: Bytes; + } & Struct; + readonly isSetStorage: boolean; + readonly asSetStorage: { + readonly items: Vec>; + } & Struct; + readonly isKillStorage: boolean; + readonly asKillStorage: { + readonly keys_: Vec; + } & Struct; + readonly isKillPrefix: boolean; + readonly asKillPrefix: { + readonly prefix: Bytes; + readonly subkeys: u32; + } & Struct; + readonly isRemarkWithEvent: boolean; + readonly asRemarkWithEvent: { + readonly remark: Bytes; + } & Struct; + readonly type: 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent'; +} + +/** @name FrameSystemError */ +export interface FrameSystemError extends Enum { + readonly isInvalidSpecName: boolean; + readonly isSpecVersionNeedsToIncrease: boolean; + readonly isFailedToExtractRuntimeVersion: boolean; + readonly isNonDefaultComposite: boolean; + readonly isNonZeroRefCount: boolean; + readonly isCallFiltered: boolean; + readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered'; +} + +/** @name FrameSystemEvent */ +export interface FrameSystemEvent extends Enum { + readonly isExtrinsicSuccess: boolean; + readonly asExtrinsicSuccess: { + readonly dispatchInfo: FrameSupportDispatchDispatchInfo; + } & Struct; + readonly isExtrinsicFailed: boolean; + readonly asExtrinsicFailed: { + readonly dispatchError: SpRuntimeDispatchError; + readonly dispatchInfo: FrameSupportDispatchDispatchInfo; + } & Struct; + readonly isCodeUpdated: boolean; + readonly isNewAccount: boolean; + readonly asNewAccount: { + readonly account: AccountId32; + } & Struct; + readonly isKilledAccount: boolean; + readonly asKilledAccount: { + readonly account: AccountId32; + } & Struct; + readonly isRemarked: boolean; + readonly asRemarked: { + readonly sender: AccountId32; + readonly hash_: H256; + } & Struct; + readonly type: 'ExtrinsicSuccess' | 'ExtrinsicFailed' | 'CodeUpdated' | 'NewAccount' | 'KilledAccount' | 'Remarked'; +} + +/** @name FrameSystemEventRecord */ +export interface FrameSystemEventRecord extends Struct { + readonly phase: FrameSystemPhase; + readonly event: Event; + readonly topics: Vec; +} + +/** @name FrameSystemExtensionsCheckGenesis */ +export interface FrameSystemExtensionsCheckGenesis extends Null {} + +/** @name FrameSystemExtensionsCheckNonce */ +export interface FrameSystemExtensionsCheckNonce extends Compact {} + +/** @name FrameSystemExtensionsCheckSpecVersion */ +export interface FrameSystemExtensionsCheckSpecVersion extends Null {} + +/** @name FrameSystemExtensionsCheckTxVersion */ +export interface FrameSystemExtensionsCheckTxVersion extends Null {} + +/** @name FrameSystemExtensionsCheckWeight */ +export interface FrameSystemExtensionsCheckWeight extends Null {} + +/** @name FrameSystemLastRuntimeUpgradeInfo */ +export interface FrameSystemLastRuntimeUpgradeInfo extends Struct { + readonly specVersion: Compact; + readonly specName: Text; +} + +/** @name FrameSystemLimitsBlockLength */ +export interface FrameSystemLimitsBlockLength extends Struct { + readonly max: FrameSupportDispatchPerDispatchClassU32; +} + +/** @name FrameSystemLimitsBlockWeights */ +export interface FrameSystemLimitsBlockWeights extends Struct { + readonly baseBlock: SpWeightsWeightV2Weight; + readonly maxBlock: SpWeightsWeightV2Weight; + readonly perClass: FrameSupportDispatchPerDispatchClassWeightsPerClass; +} + +/** @name FrameSystemLimitsWeightsPerClass */ +export interface FrameSystemLimitsWeightsPerClass extends Struct { + readonly baseExtrinsic: SpWeightsWeightV2Weight; + readonly maxExtrinsic: Option; + readonly maxTotal: Option; + readonly reserved: Option; +} + +/** @name FrameSystemPhase */ +export interface FrameSystemPhase extends Enum { + readonly isApplyExtrinsic: boolean; + readonly asApplyExtrinsic: u32; + readonly isFinalization: boolean; + readonly isInitialization: boolean; + readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization'; +} + +/** @name OpalRuntimeOriginCaller */ +export interface OpalRuntimeOriginCaller extends Enum { + readonly isSystem: boolean; + readonly asSystem: FrameSupportDispatchRawOrigin; + readonly isVoid: boolean; + readonly asVoid: SpCoreVoid; + readonly isCouncil: boolean; + readonly asCouncil: PalletCollectiveRawOrigin; + readonly isTechnicalCommittee: boolean; + readonly asTechnicalCommittee: PalletCollectiveRawOrigin; + readonly isPolkadotXcm: boolean; + readonly asPolkadotXcm: PalletXcmOrigin; + readonly isCumulusXcm: boolean; + readonly asCumulusXcm: CumulusPalletXcmOrigin; + readonly isOrigins: boolean; + readonly asOrigins: PalletGovOriginsOrigin; + readonly isEthereum: boolean; + readonly asEthereum: PalletEthereumRawOrigin; + readonly type: 'System' | 'Void' | 'Council' | 'TechnicalCommittee' | 'PolkadotXcm' | 'CumulusXcm' | 'Origins' | 'Ethereum'; +} + +/** @name OpalRuntimeRuntime */ +export interface OpalRuntimeRuntime extends Null {} + +/** @name OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls */ +export interface OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls extends Null {} + +/** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance */ +export interface OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance extends Null {} + +/** @name OpalRuntimeRuntimeCommonSessionKeys */ +export interface OpalRuntimeRuntimeCommonSessionKeys extends Struct { + readonly aura: SpConsensusAuraSr25519AppSr25519Public; +} + +/** @name OpalRuntimeRuntimeHoldReason */ +export interface OpalRuntimeRuntimeHoldReason extends Enum { + readonly isCollatorSelection: boolean; + readonly asCollatorSelection: PalletCollatorSelectionHoldReason; + readonly type: 'CollatorSelection'; +} + +/** @name OrmlTokensAccountData */ +export interface OrmlTokensAccountData extends Struct { + readonly free: u128; + readonly reserved: u128; + readonly frozen: u128; +} + +/** @name OrmlTokensBalanceLock */ +export interface OrmlTokensBalanceLock extends Struct { + readonly id: U8aFixed; + readonly amount: u128; +} + +/** @name OrmlTokensModuleCall */ +export interface OrmlTokensModuleCall extends Enum { + readonly isTransfer: boolean; + readonly asTransfer: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: Compact; + } & Struct; + readonly isTransferAll: boolean; + readonly asTransferAll: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetId; + readonly keepAlive: bool; + } & Struct; + readonly isTransferKeepAlive: boolean; + readonly asTransferKeepAlive: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: Compact; + } & Struct; + readonly isForceTransfer: boolean; + readonly asForceTransfer: { + readonly source: MultiAddress; + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: Compact; + } & Struct; + readonly isSetBalance: boolean; + readonly asSetBalance: { + readonly who: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetId; + readonly newFree: Compact; + readonly newReserved: Compact; + } & Struct; + readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance'; +} + +/** @name OrmlTokensModuleError */ +export interface OrmlTokensModuleError extends Enum { + readonly isBalanceTooLow: boolean; + readonly isAmountIntoBalanceFailed: boolean; + readonly isLiquidityRestrictions: boolean; + readonly isMaxLocksExceeded: boolean; + readonly isKeepAlive: boolean; + readonly isExistentialDeposit: boolean; + readonly isDeadAccount: boolean; + readonly isTooManyReserves: boolean; + readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves'; +} + +/** @name OrmlTokensModuleEvent */ +export interface OrmlTokensModuleEvent extends Enum { + readonly isEndowed: boolean; + readonly asEndowed: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isDustLost: boolean; + readonly asDustLost: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isTransfer: boolean; + readonly asTransfer: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + } & Struct; + readonly isReserved: boolean; + readonly asReserved: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUnreserved: boolean; + readonly asUnreserved: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isReserveRepatriated: boolean; + readonly asReserveRepatriated: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + readonly status: FrameSupportTokensMiscBalanceStatus; + } & Struct; + readonly isBalanceSet: boolean; + readonly asBalanceSet: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly free: u128; + readonly reserved: u128; + } & Struct; + readonly isTotalIssuanceSet: boolean; + readonly asTotalIssuanceSet: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: u128; + } & Struct; + readonly isWithdrawn: boolean; + readonly asWithdrawn: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isSlashed: boolean; + readonly asSlashed: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly freeAmount: u128; + readonly reservedAmount: u128; + } & Struct; + readonly isDeposited: boolean; + readonly asDeposited: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isLockSet: boolean; + readonly asLockSet: { + readonly lockId: U8aFixed; + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isLockRemoved: boolean; + readonly asLockRemoved: { + readonly lockId: U8aFixed; + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + } & Struct; + readonly isLocked: boolean; + readonly asLocked: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUnlocked: boolean; + readonly asUnlocked: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isIssued: boolean; + readonly asIssued: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: u128; + } & Struct; + readonly isRescinded: boolean; + readonly asRescinded: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: u128; + } & Struct; + readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'BalanceSet' | 'TotalIssuanceSet' | 'Withdrawn' | 'Slashed' | 'Deposited' | 'LockSet' | 'LockRemoved' | 'Locked' | 'Unlocked' | 'Issued' | 'Rescinded'; +} + +/** @name OrmlTokensReserveData */ +export interface OrmlTokensReserveData extends Struct { + readonly id: Null; + readonly amount: u128; +} + +/** @name OrmlVestingModuleCall */ +export interface OrmlVestingModuleCall extends Enum { + readonly isClaim: boolean; + readonly isVestedTransfer: boolean; + readonly asVestedTransfer: { + readonly dest: MultiAddress; + readonly schedule: OrmlVestingVestingSchedule; + } & Struct; + readonly isUpdateVestingSchedules: boolean; + readonly asUpdateVestingSchedules: { + readonly who: MultiAddress; + readonly vestingSchedules: Vec; + } & Struct; + readonly isClaimFor: boolean; + readonly asClaimFor: { + readonly dest: MultiAddress; + } & Struct; + readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor'; +} + +/** @name OrmlVestingModuleError */ +export interface OrmlVestingModuleError extends Enum { + readonly isZeroVestingPeriod: boolean; + readonly isZeroVestingPeriodCount: boolean; + readonly isInsufficientBalanceToLock: boolean; + readonly isTooManyVestingSchedules: boolean; + readonly isAmountLow: boolean; + readonly isMaxVestingSchedulesExceeded: boolean; + readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded'; +} + +/** @name OrmlVestingModuleEvent */ +export interface OrmlVestingModuleEvent extends Enum { + readonly isVestingScheduleAdded: boolean; + readonly asVestingScheduleAdded: { + readonly from: AccountId32; + readonly to: AccountId32; + readonly vestingSchedule: OrmlVestingVestingSchedule; + } & Struct; + readonly isClaimed: boolean; + readonly asClaimed: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isVestingSchedulesUpdated: boolean; + readonly asVestingSchedulesUpdated: { + readonly who: AccountId32; + } & Struct; + readonly type: 'VestingScheduleAdded' | 'Claimed' | 'VestingSchedulesUpdated'; +} + +/** @name OrmlVestingVestingSchedule */ +export interface OrmlVestingVestingSchedule extends Struct { + readonly start: u32; + readonly period: u32; + readonly periodCount: u32; + readonly perPeriod: Compact; +} + +/** @name OrmlXtokensModuleCall */ +export interface OrmlXtokensModuleCall extends Enum { + readonly isTransfer: boolean; + readonly asTransfer: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: u128; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isTransferMultiasset: boolean; + readonly asTransferMultiasset: { + readonly asset: StagingXcmVersionedMultiAsset; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isTransferWithFee: boolean; + readonly asTransferWithFee: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: u128; + readonly fee: u128; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isTransferMultiassetWithFee: boolean; + readonly asTransferMultiassetWithFee: { + readonly asset: StagingXcmVersionedMultiAsset; + readonly fee: StagingXcmVersionedMultiAsset; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isTransferMulticurrencies: boolean; + readonly asTransferMulticurrencies: { + readonly currencies: Vec>; + readonly feeItem: u32; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isTransferMultiassets: boolean; + readonly asTransferMultiassets: { + readonly assets: StagingXcmVersionedMultiAssets; + readonly feeItem: u32; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets'; +} + +/** @name OrmlXtokensModuleError */ +export interface OrmlXtokensModuleError extends Enum { + readonly isAssetHasNoReserve: boolean; + readonly isNotCrossChainTransfer: boolean; + readonly isInvalidDest: boolean; + readonly isNotCrossChainTransferableCurrency: boolean; + readonly isUnweighableMessage: boolean; + readonly isXcmExecutionFailed: boolean; + readonly isCannotReanchor: boolean; + readonly isInvalidAncestry: boolean; + readonly isInvalidAsset: boolean; + readonly isDestinationNotInvertible: boolean; + readonly isBadVersion: boolean; + readonly isDistinctReserveForAssetAndFee: boolean; + readonly isZeroFee: boolean; + readonly isZeroAmount: boolean; + readonly isTooManyAssetsBeingSent: boolean; + readonly isAssetIndexNonExistent: boolean; + readonly isFeeNotEnough: boolean; + readonly isNotSupportedMultiLocation: boolean; + readonly isMinXcmFeeNotDefined: boolean; + readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined'; +} + +/** @name OrmlXtokensModuleEvent */ +export interface OrmlXtokensModuleEvent extends Enum { + readonly isTransferredMultiAssets: boolean; + readonly asTransferredMultiAssets: { + readonly sender: AccountId32; + readonly assets: StagingXcmV3MultiassetMultiAssets; + readonly fee: StagingXcmV3MultiAsset; + readonly dest: StagingXcmV3MultiLocation; + } & Struct; + readonly type: 'TransferredMultiAssets'; +} + +/** @name PalletAppPromotionCall */ +export interface PalletAppPromotionCall extends Enum { + readonly isSetAdminAddress: boolean; + readonly asSetAdminAddress: { + readonly admin: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isStake: boolean; + readonly asStake: { + readonly amount: u128; + } & Struct; + readonly isUnstakeAll: boolean; + readonly isSponsorCollection: boolean; + readonly asSponsorCollection: { + readonly collectionId: u32; + } & Struct; + readonly isStopSponsoringCollection: boolean; + readonly asStopSponsoringCollection: { + readonly collectionId: u32; + } & Struct; + readonly isSponsorContract: boolean; + readonly asSponsorContract: { + readonly contractId: H160; + } & Struct; + readonly isStopSponsoringContract: boolean; + readonly asStopSponsoringContract: { + readonly contractId: H160; + } & Struct; + readonly isPayoutStakers: boolean; + readonly asPayoutStakers: { + readonly stakersNumber: Option; + } & Struct; + readonly isUnstakePartial: boolean; + readonly asUnstakePartial: { + readonly amount: u128; + } & Struct; + readonly isForceUnstake: boolean; + readonly asForceUnstake: { + readonly pendingBlocks: Vec; + } & Struct; + readonly type: 'SetAdminAddress' | 'Stake' | 'UnstakeAll' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers' | 'UnstakePartial' | 'ForceUnstake'; +} + +/** @name PalletAppPromotionError */ +export interface PalletAppPromotionError extends Enum { + readonly isAdminNotSet: boolean; + readonly isNoPermission: boolean; + readonly isNotSufficientFunds: boolean; + readonly isPendingForBlockOverflow: boolean; + readonly isSponsorNotSet: boolean; + readonly isInsufficientStakedBalance: boolean; + readonly isInconsistencyState: boolean; + readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'InsufficientStakedBalance' | 'InconsistencyState'; +} + +/** @name PalletAppPromotionEvent */ +export interface PalletAppPromotionEvent extends Enum { + readonly isStakingRecalculation: boolean; + readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>; + readonly isStake: boolean; + readonly asStake: ITuple<[AccountId32, u128]>; + readonly isUnstake: boolean; + readonly asUnstake: ITuple<[AccountId32, u128]>; + readonly isSetAdmin: boolean; + readonly asSetAdmin: AccountId32; + readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin'; +} + +/** @name PalletBalancesAccountData */ +export interface PalletBalancesAccountData extends Struct { + readonly free: u128; + readonly reserved: u128; + readonly frozen: u128; + readonly flags: u128; +} + +/** @name PalletBalancesBalanceLock */ +export interface PalletBalancesBalanceLock extends Struct { + readonly id: U8aFixed; + readonly amount: u128; + readonly reasons: PalletBalancesReasons; +} + +/** @name PalletBalancesCall */ +export interface PalletBalancesCall extends Enum { + readonly isTransferAllowDeath: boolean; + readonly asTransferAllowDeath: { + readonly dest: MultiAddress; + readonly value: Compact; + } & Struct; + readonly isSetBalanceDeprecated: boolean; + readonly asSetBalanceDeprecated: { + readonly who: MultiAddress; + readonly newFree: Compact; + readonly oldReserved: Compact; + } & Struct; + readonly isForceTransfer: boolean; + readonly asForceTransfer: { + readonly source: MultiAddress; + readonly dest: MultiAddress; + readonly value: Compact; + } & Struct; + readonly isTransferKeepAlive: boolean; + readonly asTransferKeepAlive: { + readonly dest: MultiAddress; + readonly value: Compact; + } & Struct; + readonly isTransferAll: boolean; + readonly asTransferAll: { + readonly dest: MultiAddress; + readonly keepAlive: bool; + } & Struct; + readonly isForceUnreserve: boolean; + readonly asForceUnreserve: { + readonly who: MultiAddress; + readonly amount: u128; + } & Struct; + readonly isUpgradeAccounts: boolean; + readonly asUpgradeAccounts: { + readonly who: Vec; + } & Struct; + readonly isTransfer: boolean; + readonly asTransfer: { + readonly dest: MultiAddress; + readonly value: Compact; + } & Struct; + readonly isForceSetBalance: boolean; + readonly asForceSetBalance: { + readonly who: MultiAddress; + readonly newFree: Compact; + } & Struct; + readonly type: 'TransferAllowDeath' | 'SetBalanceDeprecated' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve' | 'UpgradeAccounts' | 'Transfer' | 'ForceSetBalance'; +} + +/** @name PalletBalancesError */ +export interface PalletBalancesError extends Enum { + readonly isVestingBalance: boolean; + readonly isLiquidityRestrictions: boolean; + readonly isInsufficientBalance: boolean; + readonly isExistentialDeposit: boolean; + readonly isExpendability: boolean; + readonly isExistingVestingSchedule: boolean; + readonly isDeadAccount: boolean; + readonly isTooManyReserves: boolean; + readonly isTooManyHolds: boolean; + readonly isTooManyFreezes: boolean; + readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'Expendability' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves' | 'TooManyHolds' | 'TooManyFreezes'; +} + +/** @name PalletBalancesEvent */ +export interface PalletBalancesEvent extends Enum { + readonly isEndowed: boolean; + readonly asEndowed: { + readonly account: AccountId32; + readonly freeBalance: u128; + } & Struct; + readonly isDustLost: boolean; + readonly asDustLost: { + readonly account: AccountId32; + readonly amount: u128; + } & Struct; + readonly isTransfer: boolean; + readonly asTransfer: { + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + } & Struct; + readonly isBalanceSet: boolean; + readonly asBalanceSet: { + readonly who: AccountId32; + readonly free: u128; + } & Struct; + readonly isReserved: boolean; + readonly asReserved: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUnreserved: boolean; + readonly asUnreserved: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isReserveRepatriated: boolean; + readonly asReserveRepatriated: { + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + readonly destinationStatus: FrameSupportTokensMiscBalanceStatus; + } & Struct; + readonly isDeposit: boolean; + readonly asDeposit: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isWithdraw: boolean; + readonly asWithdraw: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isSlashed: boolean; + readonly asSlashed: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isMinted: boolean; + readonly asMinted: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isBurned: boolean; + readonly asBurned: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isSuspended: boolean; + readonly asSuspended: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isRestored: boolean; + readonly asRestored: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUpgraded: boolean; + readonly asUpgraded: { + readonly who: AccountId32; + } & Struct; + readonly isIssued: boolean; + readonly asIssued: { + readonly amount: u128; + } & Struct; + readonly isRescinded: boolean; + readonly asRescinded: { + readonly amount: u128; + } & Struct; + readonly isLocked: boolean; + readonly asLocked: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUnlocked: boolean; + readonly asUnlocked: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isFrozen: boolean; + readonly asFrozen: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isThawed: boolean; + readonly asThawed: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'BalanceSet' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'Deposit' | 'Withdraw' | 'Slashed' | 'Minted' | 'Burned' | 'Suspended' | 'Restored' | 'Upgraded' | 'Issued' | 'Rescinded' | 'Locked' | 'Unlocked' | 'Frozen' | 'Thawed'; +} + +/** @name PalletBalancesIdAmount */ +export interface PalletBalancesIdAmount extends Struct { + readonly id: U8aFixed; + readonly amount: u128; +} + +/** @name PalletBalancesReasons */ +export interface PalletBalancesReasons extends Enum { + readonly isFee: boolean; + readonly isMisc: boolean; + readonly isAll: boolean; + readonly type: 'Fee' | 'Misc' | 'All'; +} + +/** @name PalletBalancesReserveData */ +export interface PalletBalancesReserveData extends Struct { + readonly id: U8aFixed; + readonly amount: u128; +} + +/** @name PalletCollatorSelectionCall */ +export interface PalletCollatorSelectionCall extends Enum { + readonly isAddInvulnerable: boolean; + readonly asAddInvulnerable: { + readonly new_: AccountId32; + } & Struct; + readonly isRemoveInvulnerable: boolean; + readonly asRemoveInvulnerable: { + readonly who: AccountId32; + } & Struct; + readonly isGetLicense: boolean; + readonly isOnboard: boolean; + readonly isOffboard: boolean; + readonly isReleaseLicense: boolean; + readonly isForceReleaseLicense: boolean; + readonly asForceReleaseLicense: { + readonly who: AccountId32; + } & Struct; + readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceReleaseLicense'; +} + +/** @name PalletCollatorSelectionError */ +export interface PalletCollatorSelectionError extends Enum { + readonly isTooManyCandidates: boolean; + readonly isUnknown: boolean; + readonly isPermission: boolean; + readonly isAlreadyHoldingLicense: boolean; + readonly isNoLicense: boolean; + readonly isAlreadyCandidate: boolean; + readonly isNotCandidate: boolean; + readonly isTooManyInvulnerables: boolean; + readonly isTooFewInvulnerables: boolean; + readonly isAlreadyInvulnerable: boolean; + readonly isNotInvulnerable: boolean; + readonly isNoAssociatedValidatorId: boolean; + readonly isValidatorNotRegistered: boolean; + readonly type: 'TooManyCandidates' | 'Unknown' | 'Permission' | 'AlreadyHoldingLicense' | 'NoLicense' | 'AlreadyCandidate' | 'NotCandidate' | 'TooManyInvulnerables' | 'TooFewInvulnerables' | 'AlreadyInvulnerable' | 'NotInvulnerable' | 'NoAssociatedValidatorId' | 'ValidatorNotRegistered'; +} + +/** @name PalletCollatorSelectionEvent */ +export interface PalletCollatorSelectionEvent extends Enum { + readonly isInvulnerableAdded: boolean; + readonly asInvulnerableAdded: { + readonly invulnerable: AccountId32; + } & Struct; + readonly isInvulnerableRemoved: boolean; + readonly asInvulnerableRemoved: { + readonly invulnerable: AccountId32; + } & Struct; + readonly isLicenseObtained: boolean; + readonly asLicenseObtained: { + readonly accountId: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isLicenseReleased: boolean; + readonly asLicenseReleased: { + readonly accountId: AccountId32; + readonly depositReturned: u128; + } & Struct; + readonly isCandidateAdded: boolean; + readonly asCandidateAdded: { + readonly accountId: AccountId32; + } & Struct; + readonly isCandidateRemoved: boolean; + readonly asCandidateRemoved: { + readonly accountId: AccountId32; + } & Struct; + readonly type: 'InvulnerableAdded' | 'InvulnerableRemoved' | 'LicenseObtained' | 'LicenseReleased' | 'CandidateAdded' | 'CandidateRemoved'; +} + +/** @name PalletCollatorSelectionHoldReason */ +export interface PalletCollatorSelectionHoldReason extends Enum { + readonly isLicenseBond: boolean; + readonly type: 'LicenseBond'; +} + +/** @name PalletCollectiveCall */ +export interface PalletCollectiveCall extends Enum { + readonly isSetMembers: boolean; + readonly asSetMembers: { + readonly newMembers: Vec; + readonly prime: Option; + readonly oldCount: u32; + } & Struct; + readonly isExecute: boolean; + readonly asExecute: { + readonly proposal: Call; + readonly lengthBound: Compact; + } & Struct; + readonly isPropose: boolean; + readonly asPropose: { + readonly threshold: Compact; + readonly proposal: Call; + readonly lengthBound: Compact; + } & Struct; + readonly isVote: boolean; + readonly asVote: { + readonly proposal: H256; + readonly index: Compact; + readonly approve: bool; + } & Struct; + readonly isDisapproveProposal: boolean; + readonly asDisapproveProposal: { + readonly proposalHash: H256; + } & Struct; + readonly isClose: boolean; + readonly asClose: { + readonly proposalHash: H256; + readonly index: Compact; + readonly proposalWeightBound: SpWeightsWeightV2Weight; + readonly lengthBound: Compact; + } & Struct; + readonly type: 'SetMembers' | 'Execute' | 'Propose' | 'Vote' | 'DisapproveProposal' | 'Close'; +} + +/** @name PalletCollectiveError */ +export interface PalletCollectiveError extends Enum { + readonly isNotMember: boolean; + readonly isDuplicateProposal: boolean; + readonly isProposalMissing: boolean; + readonly isWrongIndex: boolean; + readonly isDuplicateVote: boolean; + readonly isAlreadyInitialized: boolean; + readonly isTooEarly: boolean; + readonly isTooManyProposals: boolean; + readonly isWrongProposalWeight: boolean; + readonly isWrongProposalLength: boolean; + readonly isPrimeAccountNotMember: boolean; + readonly type: 'NotMember' | 'DuplicateProposal' | 'ProposalMissing' | 'WrongIndex' | 'DuplicateVote' | 'AlreadyInitialized' | 'TooEarly' | 'TooManyProposals' | 'WrongProposalWeight' | 'WrongProposalLength' | 'PrimeAccountNotMember'; +} + +/** @name PalletCollectiveEvent */ +export interface PalletCollectiveEvent extends Enum { + readonly isProposed: boolean; + readonly asProposed: { + readonly account: AccountId32; + readonly proposalIndex: u32; + readonly proposalHash: H256; + readonly threshold: u32; + } & Struct; + readonly isVoted: boolean; + readonly asVoted: { + readonly account: AccountId32; + readonly proposalHash: H256; + readonly voted: bool; + readonly yes: u32; + readonly no: u32; + } & Struct; + readonly isApproved: boolean; + readonly asApproved: { + readonly proposalHash: H256; + } & Struct; + readonly isDisapproved: boolean; + readonly asDisapproved: { + readonly proposalHash: H256; + } & Struct; + readonly isExecuted: boolean; + readonly asExecuted: { + readonly proposalHash: H256; + readonly result: Result; + } & Struct; + readonly isMemberExecuted: boolean; + readonly asMemberExecuted: { + readonly proposalHash: H256; + readonly result: Result; + } & Struct; + readonly isClosed: boolean; + readonly asClosed: { + readonly proposalHash: H256; + readonly yes: u32; + readonly no: u32; + } & Struct; + readonly type: 'Proposed' | 'Voted' | 'Approved' | 'Disapproved' | 'Executed' | 'MemberExecuted' | 'Closed'; +} + +/** @name PalletCollectiveRawOrigin */ +export interface PalletCollectiveRawOrigin extends Enum { + readonly isMembers: boolean; + readonly asMembers: ITuple<[u32, u32]>; + readonly isMember: boolean; + readonly asMember: AccountId32; + readonly isPhantom: boolean; + readonly type: 'Members' | 'Member' | 'Phantom'; +} + +/** @name PalletCollectiveVotes */ +export interface PalletCollectiveVotes extends Struct { + readonly index: u32; + readonly threshold: u32; + readonly ayes: Vec; + readonly nays: Vec; + readonly end: u32; +} + +/** @name PalletCommonError */ +export interface PalletCommonError extends Enum { + readonly isCollectionNotFound: boolean; + readonly isMustBeTokenOwner: boolean; + readonly isNoPermission: boolean; + readonly isCantDestroyNotEmptyCollection: boolean; + readonly isPublicMintingNotAllowed: boolean; + readonly isAddressNotInAllowlist: boolean; + readonly isCollectionNameLimitExceeded: boolean; + readonly isCollectionDescriptionLimitExceeded: boolean; + readonly isCollectionTokenPrefixLimitExceeded: boolean; + readonly isTotalCollectionsLimitExceeded: boolean; + readonly isCollectionAdminCountExceeded: boolean; + readonly isCollectionLimitBoundsExceeded: boolean; + readonly isOwnerPermissionsCantBeReverted: boolean; + readonly isTransferNotAllowed: boolean; + readonly isAccountTokenLimitExceeded: boolean; + readonly isCollectionTokenLimitExceeded: boolean; + readonly isMetadataFlagFrozen: boolean; + readonly isTokenNotFound: boolean; + readonly isTokenValueTooLow: boolean; + readonly isApprovedValueTooLow: boolean; + readonly isCantApproveMoreThanOwned: boolean; + readonly isAddressIsNotEthMirror: boolean; + readonly isAddressIsZero: boolean; + readonly isUnsupportedOperation: boolean; + readonly isNotSufficientFounds: boolean; + readonly isUserIsNotAllowedToNest: boolean; + readonly isSourceCollectionIsNotAllowedToNest: boolean; + readonly isCollectionFieldSizeExceeded: boolean; + readonly isNoSpaceForProperty: boolean; + readonly isPropertyLimitReached: boolean; + readonly isPropertyKeyIsTooLong: boolean; + readonly isInvalidCharacterInPropertyKey: boolean; + readonly isEmptyPropertyKey: boolean; + readonly isCollectionIsExternal: boolean; + readonly isCollectionIsInternal: boolean; + readonly isConfirmSponsorshipFail: boolean; + readonly isUserIsNotCollectionAdmin: boolean; + readonly isFungibleItemsHaveNoId: boolean; + readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsNotEthMirror' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal' | 'ConfirmSponsorshipFail' | 'UserIsNotCollectionAdmin' | 'FungibleItemsHaveNoId'; +} + +/** @name PalletCommonEvent */ +export interface PalletCommonEvent extends Enum { + readonly isCollectionCreated: boolean; + readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>; + readonly isCollectionDestroyed: boolean; + readonly asCollectionDestroyed: u32; + readonly isItemCreated: boolean; + readonly asItemCreated: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; + readonly isItemDestroyed: boolean; + readonly asItemDestroyed: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; + readonly isTransfer: boolean; + readonly asTransfer: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; + readonly isApproved: boolean; + readonly asApproved: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; + readonly isApprovedForAll: boolean; + readonly asApprovedForAll: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>; + readonly isCollectionPropertySet: boolean; + readonly asCollectionPropertySet: ITuple<[u32, Bytes]>; + readonly isCollectionPropertyDeleted: boolean; + readonly asCollectionPropertyDeleted: ITuple<[u32, Bytes]>; + readonly isTokenPropertySet: boolean; + readonly asTokenPropertySet: ITuple<[u32, u32, Bytes]>; + readonly isTokenPropertyDeleted: boolean; + readonly asTokenPropertyDeleted: ITuple<[u32, u32, Bytes]>; + readonly isPropertyPermissionSet: boolean; + readonly asPropertyPermissionSet: ITuple<[u32, Bytes]>; + readonly isAllowListAddressAdded: boolean; + readonly asAllowListAddressAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + readonly isAllowListAddressRemoved: boolean; + readonly asAllowListAddressRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + readonly isCollectionAdminAdded: boolean; + readonly asCollectionAdminAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + readonly isCollectionAdminRemoved: boolean; + readonly asCollectionAdminRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + readonly isCollectionLimitSet: boolean; + readonly asCollectionLimitSet: u32; + readonly isCollectionOwnerChanged: boolean; + readonly asCollectionOwnerChanged: ITuple<[u32, AccountId32]>; + readonly isCollectionPermissionSet: boolean; + readonly asCollectionPermissionSet: u32; + readonly isCollectionSponsorSet: boolean; + readonly asCollectionSponsorSet: ITuple<[u32, AccountId32]>; + readonly isSponsorshipConfirmed: boolean; + readonly asSponsorshipConfirmed: ITuple<[u32, AccountId32]>; + readonly isCollectionSponsorRemoved: boolean; + readonly asCollectionSponsorRemoved: u32; + readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet' | 'AllowListAddressAdded' | 'AllowListAddressRemoved' | 'CollectionAdminAdded' | 'CollectionAdminRemoved' | 'CollectionLimitSet' | 'CollectionOwnerChanged' | 'CollectionPermissionSet' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionSponsorRemoved'; +} + +/** @name PalletConfigurationAppPromotionConfiguration */ +export interface PalletConfigurationAppPromotionConfiguration extends Struct { + readonly recalculationInterval: Option; + readonly pendingInterval: Option; + readonly intervalIncome: Option; + readonly maxStakersPerCalculation: Option; +} + +/** @name PalletConfigurationCall */ +export interface PalletConfigurationCall extends Enum { + readonly isSetWeightToFeeCoefficientOverride: boolean; + readonly asSetWeightToFeeCoefficientOverride: { + readonly coeff: Option; + } & Struct; + readonly isSetMinGasPriceOverride: boolean; + readonly asSetMinGasPriceOverride: { + readonly coeff: Option; + } & Struct; + readonly isSetAppPromotionConfigurationOverride: boolean; + readonly asSetAppPromotionConfigurationOverride: { + readonly configuration: PalletConfigurationAppPromotionConfiguration; + } & Struct; + readonly isSetCollatorSelectionDesiredCollators: boolean; + readonly asSetCollatorSelectionDesiredCollators: { + readonly max: Option; + } & Struct; + readonly isSetCollatorSelectionLicenseBond: boolean; + readonly asSetCollatorSelectionLicenseBond: { + readonly amount: Option; + } & Struct; + readonly isSetCollatorSelectionKickThreshold: boolean; + readonly asSetCollatorSelectionKickThreshold: { + readonly threshold: Option; + } & Struct; + readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetAppPromotionConfigurationOverride' | 'SetCollatorSelectionDesiredCollators' | 'SetCollatorSelectionLicenseBond' | 'SetCollatorSelectionKickThreshold'; +} + +/** @name PalletConfigurationError */ +export interface PalletConfigurationError extends Enum { + readonly isInconsistentConfiguration: boolean; + readonly type: 'InconsistentConfiguration'; +} + +/** @name PalletConfigurationEvent */ +export interface PalletConfigurationEvent extends Enum { + readonly isNewDesiredCollators: boolean; + readonly asNewDesiredCollators: { + readonly desiredCollators: Option; + } & Struct; + readonly isNewCollatorLicenseBond: boolean; + readonly asNewCollatorLicenseBond: { + readonly bondCost: Option; + } & Struct; + readonly isNewCollatorKickThreshold: boolean; + readonly asNewCollatorKickThreshold: { + readonly lengthInBlocks: Option; + } & Struct; + readonly type: 'NewDesiredCollators' | 'NewCollatorLicenseBond' | 'NewCollatorKickThreshold'; +} + +/** @name PalletDemocracyCall */ +export interface PalletDemocracyCall extends Enum { + readonly isPropose: boolean; + readonly asPropose: { + readonly proposal: FrameSupportPreimagesBounded; + readonly value: Compact; + } & Struct; + readonly isSecond: boolean; + readonly asSecond: { + readonly proposal: Compact; + } & Struct; + readonly isVote: boolean; + readonly asVote: { + readonly refIndex: Compact; + readonly vote: PalletDemocracyVoteAccountVote; + } & Struct; + readonly isEmergencyCancel: boolean; + readonly asEmergencyCancel: { + readonly refIndex: u32; + } & Struct; + readonly isExternalPropose: boolean; + readonly asExternalPropose: { + readonly proposal: FrameSupportPreimagesBounded; + } & Struct; + readonly isExternalProposeMajority: boolean; + readonly asExternalProposeMajority: { + readonly proposal: FrameSupportPreimagesBounded; + } & Struct; + readonly isExternalProposeDefault: boolean; + readonly asExternalProposeDefault: { + readonly proposal: FrameSupportPreimagesBounded; + } & Struct; + readonly isFastTrack: boolean; + readonly asFastTrack: { + readonly proposalHash: H256; + readonly votingPeriod: u32; + readonly delay: u32; + } & Struct; + readonly isVetoExternal: boolean; + readonly asVetoExternal: { + readonly proposalHash: H256; + } & Struct; + readonly isCancelReferendum: boolean; + readonly asCancelReferendum: { + readonly refIndex: Compact; + } & Struct; + readonly isDelegate: boolean; + readonly asDelegate: { + readonly to: MultiAddress; + readonly conviction: PalletDemocracyConviction; + readonly balance: u128; + } & Struct; + readonly isUndelegate: boolean; + readonly isClearPublicProposals: boolean; + readonly isUnlock: boolean; + readonly asUnlock: { + readonly target: MultiAddress; + } & Struct; + readonly isRemoveVote: boolean; + readonly asRemoveVote: { + readonly index: u32; + } & Struct; + readonly isRemoveOtherVote: boolean; + readonly asRemoveOtherVote: { + readonly target: MultiAddress; + readonly index: u32; + } & Struct; + readonly isBlacklist: boolean; + readonly asBlacklist: { + readonly proposalHash: H256; + readonly maybeRefIndex: Option; + } & Struct; + readonly isCancelProposal: boolean; + readonly asCancelProposal: { + readonly propIndex: Compact; + } & Struct; + readonly isSetMetadata: boolean; + readonly asSetMetadata: { + readonly owner: PalletDemocracyMetadataOwner; + readonly maybeHash: Option; + } & Struct; + readonly type: 'Propose' | 'Second' | 'Vote' | 'EmergencyCancel' | 'ExternalPropose' | 'ExternalProposeMajority' | 'ExternalProposeDefault' | 'FastTrack' | 'VetoExternal' | 'CancelReferendum' | 'Delegate' | 'Undelegate' | 'ClearPublicProposals' | 'Unlock' | 'RemoveVote' | 'RemoveOtherVote' | 'Blacklist' | 'CancelProposal' | 'SetMetadata'; +} + +/** @name PalletDemocracyConviction */ +export interface PalletDemocracyConviction extends Enum { + readonly isNone: boolean; + readonly isLocked1x: boolean; + readonly isLocked2x: boolean; + readonly isLocked3x: boolean; + readonly isLocked4x: boolean; + readonly isLocked5x: boolean; + readonly isLocked6x: boolean; + readonly type: 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x'; +} + +/** @name PalletDemocracyDelegations */ +export interface PalletDemocracyDelegations extends Struct { + readonly votes: u128; + readonly capital: u128; +} + +/** @name PalletDemocracyError */ +export interface PalletDemocracyError extends Enum { + readonly isValueLow: boolean; + readonly isProposalMissing: boolean; + readonly isAlreadyCanceled: boolean; + readonly isDuplicateProposal: boolean; + readonly isProposalBlacklisted: boolean; + readonly isNotSimpleMajority: boolean; + readonly isInvalidHash: boolean; + readonly isNoProposal: boolean; + readonly isAlreadyVetoed: boolean; + readonly isReferendumInvalid: boolean; + readonly isNoneWaiting: boolean; + readonly isNotVoter: boolean; + readonly isNoPermission: boolean; + readonly isAlreadyDelegating: boolean; + readonly isInsufficientFunds: boolean; + readonly isNotDelegating: boolean; + readonly isVotesExist: boolean; + readonly isInstantNotAllowed: boolean; + readonly isNonsense: boolean; + readonly isWrongUpperBound: boolean; + readonly isMaxVotesReached: boolean; + readonly isTooMany: boolean; + readonly isVotingPeriodLow: boolean; + readonly isPreimageNotExist: boolean; + readonly type: 'ValueLow' | 'ProposalMissing' | 'AlreadyCanceled' | 'DuplicateProposal' | 'ProposalBlacklisted' | 'NotSimpleMajority' | 'InvalidHash' | 'NoProposal' | 'AlreadyVetoed' | 'ReferendumInvalid' | 'NoneWaiting' | 'NotVoter' | 'NoPermission' | 'AlreadyDelegating' | 'InsufficientFunds' | 'NotDelegating' | 'VotesExist' | 'InstantNotAllowed' | 'Nonsense' | 'WrongUpperBound' | 'MaxVotesReached' | 'TooMany' | 'VotingPeriodLow' | 'PreimageNotExist'; +} + +/** @name PalletDemocracyEvent */ +export interface PalletDemocracyEvent extends Enum { + readonly isProposed: boolean; + readonly asProposed: { + readonly proposalIndex: u32; + readonly deposit: u128; + } & Struct; + readonly isTabled: boolean; + readonly asTabled: { + readonly proposalIndex: u32; + readonly deposit: u128; + } & Struct; + readonly isExternalTabled: boolean; + readonly isStarted: boolean; + readonly asStarted: { + readonly refIndex: u32; + readonly threshold: PalletDemocracyVoteThreshold; + } & Struct; + readonly isPassed: boolean; + readonly asPassed: { + readonly refIndex: u32; + } & Struct; + readonly isNotPassed: boolean; + readonly asNotPassed: { + readonly refIndex: u32; + } & Struct; + readonly isCancelled: boolean; + readonly asCancelled: { + readonly refIndex: u32; + } & Struct; + readonly isDelegated: boolean; + readonly asDelegated: { + readonly who: AccountId32; + readonly target: AccountId32; + } & Struct; + readonly isUndelegated: boolean; + readonly asUndelegated: { + readonly account: AccountId32; + } & Struct; + readonly isVetoed: boolean; + readonly asVetoed: { + readonly who: AccountId32; + readonly proposalHash: H256; + readonly until: u32; + } & Struct; + readonly isBlacklisted: boolean; + readonly asBlacklisted: { + readonly proposalHash: H256; + } & Struct; + readonly isVoted: boolean; + readonly asVoted: { + readonly voter: AccountId32; + readonly refIndex: u32; + readonly vote: PalletDemocracyVoteAccountVote; + } & Struct; + readonly isSeconded: boolean; + readonly asSeconded: { + readonly seconder: AccountId32; + readonly propIndex: u32; + } & Struct; + readonly isProposalCanceled: boolean; + readonly asProposalCanceled: { + readonly propIndex: u32; + } & Struct; + readonly isMetadataSet: boolean; + readonly asMetadataSet: { + readonly owner: PalletDemocracyMetadataOwner; + readonly hash_: H256; + } & Struct; + readonly isMetadataCleared: boolean; + readonly asMetadataCleared: { + readonly owner: PalletDemocracyMetadataOwner; + readonly hash_: H256; + } & Struct; + readonly isMetadataTransferred: boolean; + readonly asMetadataTransferred: { + readonly prevOwner: PalletDemocracyMetadataOwner; + readonly owner: PalletDemocracyMetadataOwner; + readonly hash_: H256; + } & Struct; + readonly type: 'Proposed' | 'Tabled' | 'ExternalTabled' | 'Started' | 'Passed' | 'NotPassed' | 'Cancelled' | 'Delegated' | 'Undelegated' | 'Vetoed' | 'Blacklisted' | 'Voted' | 'Seconded' | 'ProposalCanceled' | 'MetadataSet' | 'MetadataCleared' | 'MetadataTransferred'; +} + +/** @name PalletDemocracyMetadataOwner */ +export interface PalletDemocracyMetadataOwner extends Enum { + readonly isExternal: boolean; + readonly isProposal: boolean; + readonly asProposal: u32; + readonly isReferendum: boolean; + readonly asReferendum: u32; + readonly type: 'External' | 'Proposal' | 'Referendum'; +} + +/** @name PalletDemocracyReferendumInfo */ +export interface PalletDemocracyReferendumInfo extends Enum { + readonly isOngoing: boolean; + readonly asOngoing: PalletDemocracyReferendumStatus; + readonly isFinished: boolean; + readonly asFinished: { + readonly approved: bool; + readonly end: u32; + } & Struct; + readonly type: 'Ongoing' | 'Finished'; +} + +/** @name PalletDemocracyReferendumStatus */ +export interface PalletDemocracyReferendumStatus extends Struct { + readonly end: u32; + readonly proposal: FrameSupportPreimagesBounded; + readonly threshold: PalletDemocracyVoteThreshold; + readonly delay: u32; + readonly tally: PalletDemocracyTally; +} + +/** @name PalletDemocracyTally */ +export interface PalletDemocracyTally extends Struct { + readonly ayes: u128; + readonly nays: u128; + readonly turnout: u128; +} + +/** @name PalletDemocracyVoteAccountVote */ +export interface PalletDemocracyVoteAccountVote extends Enum { + readonly isStandard: boolean; + readonly asStandard: { + readonly vote: Vote; + readonly balance: u128; + } & Struct; + readonly isSplit: boolean; + readonly asSplit: { + readonly aye: u128; + readonly nay: u128; + } & Struct; + readonly type: 'Standard' | 'Split'; +} + +/** @name PalletDemocracyVotePriorLock */ +export interface PalletDemocracyVotePriorLock extends ITuple<[u32, u128]> {} + +/** @name PalletDemocracyVoteThreshold */ +export interface PalletDemocracyVoteThreshold extends Enum { + readonly isSuperMajorityApprove: boolean; + readonly isSuperMajorityAgainst: boolean; + readonly isSimpleMajority: boolean; + readonly type: 'SuperMajorityApprove' | 'SuperMajorityAgainst' | 'SimpleMajority'; +} + +/** @name PalletDemocracyVoteVoting */ +export interface PalletDemocracyVoteVoting extends Enum { + readonly isDirect: boolean; + readonly asDirect: { + readonly votes: Vec>; + readonly delegations: PalletDemocracyDelegations; + readonly prior: PalletDemocracyVotePriorLock; + } & Struct; + readonly isDelegating: boolean; + readonly asDelegating: { + readonly balance: u128; + readonly target: AccountId32; + readonly conviction: PalletDemocracyConviction; + readonly delegations: PalletDemocracyDelegations; + readonly prior: PalletDemocracyVotePriorLock; + } & Struct; + readonly type: 'Direct' | 'Delegating'; +} + +/** @name PalletEthereumCall */ +export interface PalletEthereumCall extends Enum { + readonly isTransact: boolean; + readonly asTransact: { + readonly transaction: EthereumTransactionTransactionV2; + } & Struct; + readonly type: 'Transact'; +} + +/** @name PalletEthereumError */ +export interface PalletEthereumError extends Enum { + readonly isInvalidSignature: boolean; + readonly isPreLogExists: boolean; + readonly type: 'InvalidSignature' | 'PreLogExists'; +} + +/** @name PalletEthereumEvent */ +export interface PalletEthereumEvent extends Enum { + readonly isExecuted: boolean; + readonly asExecuted: { + readonly from: H160; + readonly to: H160; + readonly transactionHash: H256; + readonly exitReason: EvmCoreErrorExitReason; + readonly extraData: Bytes; + } & Struct; + readonly type: 'Executed'; +} + +/** @name PalletEthereumFakeTransactionFinalizer */ +export interface PalletEthereumFakeTransactionFinalizer extends Null {} + +/** @name PalletEthereumRawOrigin */ +export interface PalletEthereumRawOrigin extends Enum { + readonly isEthereumTransaction: boolean; + readonly asEthereumTransaction: H160; + readonly type: 'EthereumTransaction'; +} + +/** @name PalletEvmAccountBasicCrossAccountIdRepr */ +export interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum { + readonly isSubstrate: boolean; + readonly asSubstrate: AccountId32; + readonly isEthereum: boolean; + readonly asEthereum: H160; + readonly type: 'Substrate' | 'Ethereum'; +} + +/** @name PalletEvmCall */ +export interface PalletEvmCall extends Enum { + readonly isWithdraw: boolean; + readonly asWithdraw: { + readonly address: H160; + readonly value: u128; + } & Struct; + readonly isCall: boolean; + readonly asCall: { + readonly source: H160; + readonly target: H160; + readonly input: Bytes; + readonly value: U256; + readonly gasLimit: u64; + readonly maxFeePerGas: U256; + readonly maxPriorityFeePerGas: Option; + readonly nonce: Option; + readonly accessList: Vec]>>; + } & Struct; + readonly isCreate: boolean; + readonly asCreate: { + readonly source: H160; + readonly init: Bytes; + readonly value: U256; + readonly gasLimit: u64; + readonly maxFeePerGas: U256; + readonly maxPriorityFeePerGas: Option; + readonly nonce: Option; + readonly accessList: Vec]>>; + } & Struct; + readonly isCreate2: boolean; + readonly asCreate2: { + readonly source: H160; + readonly init: Bytes; + readonly salt: H256; + readonly value: U256; + readonly gasLimit: u64; + readonly maxFeePerGas: U256; + readonly maxPriorityFeePerGas: Option; + readonly nonce: Option; + readonly accessList: Vec]>>; + } & Struct; + readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2'; +} + +/** @name PalletEvmCodeMetadata */ +export interface PalletEvmCodeMetadata extends Struct { + readonly size_: u64; + readonly hash_: H256; +} + +/** @name PalletEvmCoderSubstrateError */ +export interface PalletEvmCoderSubstrateError extends Enum { + readonly isOutOfGas: boolean; + readonly isOutOfFund: boolean; + readonly type: 'OutOfGas' | 'OutOfFund'; +} + +/** @name PalletEvmContractHelpersCall */ +export interface PalletEvmContractHelpersCall extends Enum { + readonly isMigrateFromSelfSponsoring: boolean; + readonly asMigrateFromSelfSponsoring: { + readonly addresses: Vec; + } & Struct; + readonly type: 'MigrateFromSelfSponsoring'; +} + +/** @name PalletEvmContractHelpersError */ +export interface PalletEvmContractHelpersError extends Enum { + readonly isNoPermission: boolean; + readonly isNoPendingSponsor: boolean; + readonly isTooManyMethodsHaveSponsoredLimit: boolean; + readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit'; +} + +/** @name PalletEvmContractHelpersEvent */ +export interface PalletEvmContractHelpersEvent extends Enum { + readonly isContractSponsorSet: boolean; + readonly asContractSponsorSet: ITuple<[H160, AccountId32]>; + readonly isContractSponsorshipConfirmed: boolean; + readonly asContractSponsorshipConfirmed: ITuple<[H160, AccountId32]>; + readonly isContractSponsorRemoved: boolean; + readonly asContractSponsorRemoved: H160; + readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved'; +} + +/** @name PalletEvmContractHelpersSponsoringModeT */ +export interface PalletEvmContractHelpersSponsoringModeT extends Enum { + readonly isDisabled: boolean; + readonly isAllowlisted: boolean; + readonly isGenerous: boolean; + readonly type: 'Disabled' | 'Allowlisted' | 'Generous'; +} + +/** @name PalletEvmError */ +export interface PalletEvmError extends Enum { + readonly isBalanceLow: boolean; + readonly isFeeOverflow: boolean; + readonly isPaymentOverflow: boolean; + readonly isWithdrawFailed: boolean; + readonly isGasPriceTooLow: boolean; + readonly isInvalidNonce: boolean; + readonly isGasLimitTooLow: boolean; + readonly isGasLimitTooHigh: boolean; + readonly isUndefined: boolean; + readonly isReentrancy: boolean; + readonly isTransactionMustComeFromEOA: boolean; + readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce' | 'GasLimitTooLow' | 'GasLimitTooHigh' | 'Undefined' | 'Reentrancy' | 'TransactionMustComeFromEOA'; +} + +/** @name PalletEvmEvent */ +export interface PalletEvmEvent extends Enum { + readonly isLog: boolean; + readonly asLog: { + readonly log: EthereumLog; + } & Struct; + readonly isCreated: boolean; + readonly asCreated: { + readonly address: H160; + } & Struct; + readonly isCreatedFailed: boolean; + readonly asCreatedFailed: { + readonly address: H160; + } & Struct; + readonly isExecuted: boolean; + readonly asExecuted: { + readonly address: H160; + } & Struct; + readonly isExecutedFailed: boolean; + readonly asExecutedFailed: { + readonly address: H160; + } & Struct; + readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed'; +} + +/** @name PalletEvmMigrationCall */ +export interface PalletEvmMigrationCall extends Enum { + readonly isBegin: boolean; + readonly asBegin: { + readonly address: H160; + } & Struct; + readonly isSetData: boolean; + readonly asSetData: { + readonly address: H160; + readonly data: Vec>; + } & Struct; + readonly isFinish: boolean; + readonly asFinish: { + readonly address: H160; + readonly code: Bytes; + } & Struct; + readonly isInsertEthLogs: boolean; + readonly asInsertEthLogs: { + readonly logs: Vec; + } & Struct; + readonly isInsertEvents: boolean; + readonly asInsertEvents: { + readonly events: Vec; + } & Struct; + readonly isRemoveRmrkData: boolean; + readonly type: 'Begin' | 'SetData' | 'Finish' | 'InsertEthLogs' | 'InsertEvents' | 'RemoveRmrkData'; +} + +/** @name PalletEvmMigrationError */ +export interface PalletEvmMigrationError extends Enum { + readonly isAccountNotEmpty: boolean; + readonly isAccountIsNotMigrating: boolean; + readonly isBadEvent: boolean; + readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating' | 'BadEvent'; +} + +/** @name PalletEvmMigrationEvent */ +export interface PalletEvmMigrationEvent extends Enum { + readonly isTestEvent: boolean; + readonly type: 'TestEvent'; +} + +/** @name PalletForeignAssetsAssetId */ +export interface PalletForeignAssetsAssetId extends Enum { + readonly isForeignAssetId: boolean; + readonly asForeignAssetId: u32; + readonly isNativeAssetId: boolean; + readonly asNativeAssetId: PalletForeignAssetsNativeCurrency; + readonly type: 'ForeignAssetId' | 'NativeAssetId'; +} + +/** @name PalletForeignAssetsModuleAssetMetadata */ +export interface PalletForeignAssetsModuleAssetMetadata extends Struct { + readonly name: Bytes; + readonly symbol: Bytes; + readonly decimals: u8; + readonly minimalBalance: u128; +} + +/** @name PalletForeignAssetsModuleCall */ +export interface PalletForeignAssetsModuleCall extends Enum { + readonly isRegisterForeignAsset: boolean; + readonly asRegisterForeignAsset: { + readonly owner: AccountId32; + readonly location: StagingXcmVersionedMultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isUpdateForeignAsset: boolean; + readonly asUpdateForeignAsset: { + readonly foreignAssetId: u32; + readonly location: StagingXcmVersionedMultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset'; +} + +/** @name PalletForeignAssetsModuleError */ +export interface PalletForeignAssetsModuleError extends Enum { + readonly isBadLocation: boolean; + readonly isMultiLocationExisted: boolean; + readonly isAssetIdNotExists: boolean; + readonly isAssetIdExisted: boolean; + readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted'; +} + +/** @name PalletForeignAssetsModuleEvent */ +export interface PalletForeignAssetsModuleEvent extends Enum { + readonly isForeignAssetRegistered: boolean; + readonly asForeignAssetRegistered: { + readonly assetId: u32; + readonly assetAddress: StagingXcmV3MultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isForeignAssetUpdated: boolean; + readonly asForeignAssetUpdated: { + readonly assetId: u32; + readonly assetAddress: StagingXcmV3MultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isAssetRegistered: boolean; + readonly asAssetRegistered: { + readonly assetId: PalletForeignAssetsAssetId; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isAssetUpdated: boolean; + readonly asAssetUpdated: { + readonly assetId: PalletForeignAssetsAssetId; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated'; +} + +/** @name PalletForeignAssetsNativeCurrency */ +export interface PalletForeignAssetsNativeCurrency extends Enum { + readonly isHere: boolean; + readonly isParent: boolean; + readonly type: 'Here' | 'Parent'; +} + +/** @name PalletFungibleError */ +export interface PalletFungibleError extends Enum { + readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean; + readonly isFungibleItemsDontHaveData: boolean; + readonly isFungibleDisallowsNesting: boolean; + readonly isSettingPropertiesNotAllowed: boolean; + readonly isSettingAllowanceForAllNotAllowed: boolean; + readonly isFungibleTokensAreAlwaysValid: boolean; + readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingAllowanceForAllNotAllowed' | 'FungibleTokensAreAlwaysValid'; +} + +/** @name PalletGovOriginsOrigin */ +export interface PalletGovOriginsOrigin extends Enum { + readonly isFellowshipProposition: boolean; + readonly type: 'FellowshipProposition'; +} + +/** @name PalletIdentityBitFlags */ +export interface PalletIdentityBitFlags extends Struct { + readonly _bitLength: 64; + readonly Display: 1; + readonly Legal: 2; + readonly Web: 4; + readonly Riot: 8; + readonly Email: 16; + readonly PgpFingerprint: 32; + readonly Image: 64; + readonly Twitter: 128; +} + +/** @name PalletIdentityCall */ +export interface PalletIdentityCall extends Enum { + readonly isAddRegistrar: boolean; + readonly asAddRegistrar: { + readonly account: MultiAddress; + } & Struct; + readonly isSetIdentity: boolean; + readonly asSetIdentity: { + readonly info: PalletIdentityIdentityInfo; + } & Struct; + readonly isSetSubs: boolean; + readonly asSetSubs: { + readonly subs: Vec>; + } & Struct; + readonly isClearIdentity: boolean; + readonly isRequestJudgement: boolean; + readonly asRequestJudgement: { + readonly regIndex: Compact; + readonly maxFee: Compact; + } & Struct; + readonly isCancelRequest: boolean; + readonly asCancelRequest: { + readonly regIndex: u32; + } & Struct; + readonly isSetFee: boolean; + readonly asSetFee: { + readonly index: Compact; + readonly fee: Compact; + } & Struct; + readonly isSetAccountId: boolean; + readonly asSetAccountId: { + readonly index: Compact; + readonly new_: MultiAddress; + } & Struct; + readonly isSetFields: boolean; + readonly asSetFields: { + readonly index: Compact; + readonly fields: PalletIdentityBitFlags; + } & Struct; + readonly isProvideJudgement: boolean; + readonly asProvideJudgement: { + readonly regIndex: Compact; + readonly target: MultiAddress; + readonly judgement: PalletIdentityJudgement; + readonly identity: H256; + } & Struct; + readonly isKillIdentity: boolean; + readonly asKillIdentity: { + readonly target: MultiAddress; + } & Struct; + readonly isAddSub: boolean; + readonly asAddSub: { + readonly sub: MultiAddress; + readonly data: Data; + } & Struct; + readonly isRenameSub: boolean; + readonly asRenameSub: { + readonly sub: MultiAddress; + readonly data: Data; + } & Struct; + readonly isRemoveSub: boolean; + readonly asRemoveSub: { + readonly sub: MultiAddress; + } & Struct; + readonly isQuitSub: boolean; + readonly isForceInsertIdentities: boolean; + readonly asForceInsertIdentities: { + readonly identities: Vec>; + } & Struct; + readonly isForceRemoveIdentities: boolean; + readonly asForceRemoveIdentities: { + readonly identities: Vec; + } & Struct; + readonly isForceSetSubs: boolean; + readonly asForceSetSubs: { + readonly subs: Vec>]>]>>; + } & Struct; + readonly type: 'AddRegistrar' | 'SetIdentity' | 'SetSubs' | 'ClearIdentity' | 'RequestJudgement' | 'CancelRequest' | 'SetFee' | 'SetAccountId' | 'SetFields' | 'ProvideJudgement' | 'KillIdentity' | 'AddSub' | 'RenameSub' | 'RemoveSub' | 'QuitSub' | 'ForceInsertIdentities' | 'ForceRemoveIdentities' | 'ForceSetSubs'; +} + +/** @name PalletIdentityError */ +export interface PalletIdentityError extends Enum { + readonly isTooManySubAccounts: boolean; + readonly isNotFound: boolean; + readonly isNotNamed: boolean; + readonly isEmptyIndex: boolean; + readonly isFeeChanged: boolean; + readonly isNoIdentity: boolean; + readonly isStickyJudgement: boolean; + readonly isJudgementGiven: boolean; + readonly isInvalidJudgement: boolean; + readonly isInvalidIndex: boolean; + readonly isInvalidTarget: boolean; + readonly isTooManyFields: boolean; + readonly isTooManyRegistrars: boolean; + readonly isAlreadyClaimed: boolean; + readonly isNotSub: boolean; + readonly isNotOwned: boolean; + readonly isJudgementForDifferentIdentity: boolean; + readonly isJudgementPaymentFailed: boolean; + readonly type: 'TooManySubAccounts' | 'NotFound' | 'NotNamed' | 'EmptyIndex' | 'FeeChanged' | 'NoIdentity' | 'StickyJudgement' | 'JudgementGiven' | 'InvalidJudgement' | 'InvalidIndex' | 'InvalidTarget' | 'TooManyFields' | 'TooManyRegistrars' | 'AlreadyClaimed' | 'NotSub' | 'NotOwned' | 'JudgementForDifferentIdentity' | 'JudgementPaymentFailed'; +} + +/** @name PalletIdentityEvent */ +export interface PalletIdentityEvent extends Enum { + readonly isIdentitySet: boolean; + readonly asIdentitySet: { + readonly who: AccountId32; + } & Struct; + readonly isIdentityCleared: boolean; + readonly asIdentityCleared: { + readonly who: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isIdentityKilled: boolean; + readonly asIdentityKilled: { + readonly who: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isIdentitiesInserted: boolean; + readonly asIdentitiesInserted: { + readonly amount: u32; + } & Struct; + readonly isIdentitiesRemoved: boolean; + readonly asIdentitiesRemoved: { + readonly amount: u32; + } & Struct; + readonly isJudgementRequested: boolean; + readonly asJudgementRequested: { + readonly who: AccountId32; + readonly registrarIndex: u32; + } & Struct; + readonly isJudgementUnrequested: boolean; + readonly asJudgementUnrequested: { + readonly who: AccountId32; + readonly registrarIndex: u32; + } & Struct; + readonly isJudgementGiven: boolean; + readonly asJudgementGiven: { + readonly target: AccountId32; + readonly registrarIndex: u32; + } & Struct; + readonly isRegistrarAdded: boolean; + readonly asRegistrarAdded: { + readonly registrarIndex: u32; + } & Struct; + readonly isSubIdentityAdded: boolean; + readonly asSubIdentityAdded: { + readonly sub: AccountId32; + readonly main: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isSubIdentityRemoved: boolean; + readonly asSubIdentityRemoved: { + readonly sub: AccountId32; + readonly main: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isSubIdentityRevoked: boolean; + readonly asSubIdentityRevoked: { + readonly sub: AccountId32; + readonly main: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isSubIdentitiesInserted: boolean; + readonly asSubIdentitiesInserted: { + readonly amount: u32; + } & Struct; + readonly type: 'IdentitySet' | 'IdentityCleared' | 'IdentityKilled' | 'IdentitiesInserted' | 'IdentitiesRemoved' | 'JudgementRequested' | 'JudgementUnrequested' | 'JudgementGiven' | 'RegistrarAdded' | 'SubIdentityAdded' | 'SubIdentityRemoved' | 'SubIdentityRevoked' | 'SubIdentitiesInserted'; +} + +/** @name PalletIdentityIdentityField */ +export interface PalletIdentityIdentityField extends Enum { + readonly isDisplay: boolean; + readonly isLegal: boolean; + readonly isWeb: boolean; + readonly isRiot: boolean; + readonly isEmail: boolean; + readonly isPgpFingerprint: boolean; + readonly isImage: boolean; + readonly isTwitter: boolean; + readonly type: 'Display' | 'Legal' | 'Web' | 'Riot' | 'Email' | 'PgpFingerprint' | 'Image' | 'Twitter'; +} + +/** @name PalletIdentityIdentityInfo */ +export interface PalletIdentityIdentityInfo extends Struct { + readonly additional: Vec>; + readonly display: Data; + readonly legal: Data; + readonly web: Data; + readonly riot: Data; + readonly email: Data; + readonly pgpFingerprint: Option; + readonly image: Data; + readonly twitter: Data; +} + +/** @name PalletIdentityJudgement */ +export interface PalletIdentityJudgement extends Enum { + readonly isUnknown: boolean; + readonly isFeePaid: boolean; + readonly asFeePaid: u128; + readonly isReasonable: boolean; + readonly isKnownGood: boolean; + readonly isOutOfDate: boolean; + readonly isLowQuality: boolean; + readonly isErroneous: boolean; + readonly type: 'Unknown' | 'FeePaid' | 'Reasonable' | 'KnownGood' | 'OutOfDate' | 'LowQuality' | 'Erroneous'; +} + +/** @name PalletIdentityRegistrarInfo */ +export interface PalletIdentityRegistrarInfo extends Struct { + readonly account: AccountId32; + readonly fee: u128; + readonly fields: PalletIdentityBitFlags; +} + +/** @name PalletIdentityRegistration */ +export interface PalletIdentityRegistration extends Struct { + readonly judgements: Vec>; + readonly deposit: u128; + readonly info: PalletIdentityIdentityInfo; +} + +/** @name PalletInflationCall */ +export interface PalletInflationCall extends Enum { + readonly isStartInflation: boolean; + readonly asStartInflation: { + readonly inflationStartRelayBlock: u32; + } & Struct; + readonly type: 'StartInflation'; +} + +/** @name PalletMaintenanceCall */ +export interface PalletMaintenanceCall extends Enum { + readonly isEnable: boolean; + readonly isDisable: boolean; + readonly type: 'Enable' | 'Disable'; +} + +/** @name PalletMaintenanceError */ +export interface PalletMaintenanceError extends Null {} + +/** @name PalletMaintenanceEvent */ +export interface PalletMaintenanceEvent extends Enum { + readonly isMaintenanceEnabled: boolean; + readonly isMaintenanceDisabled: boolean; + readonly type: 'MaintenanceEnabled' | 'MaintenanceDisabled'; +} + +/** @name PalletMembershipCall */ +export interface PalletMembershipCall extends Enum { + readonly isAddMember: boolean; + readonly asAddMember: { + readonly who: MultiAddress; + } & Struct; + readonly isRemoveMember: boolean; + readonly asRemoveMember: { + readonly who: MultiAddress; + } & Struct; + readonly isSwapMember: boolean; + readonly asSwapMember: { + readonly remove: MultiAddress; + readonly add: MultiAddress; + } & Struct; + readonly isResetMembers: boolean; + readonly asResetMembers: { + readonly members: Vec; + } & Struct; + readonly isChangeKey: boolean; + readonly asChangeKey: { + readonly new_: MultiAddress; + } & Struct; + readonly isSetPrime: boolean; + readonly asSetPrime: { + readonly who: MultiAddress; + } & Struct; + readonly isClearPrime: boolean; + readonly type: 'AddMember' | 'RemoveMember' | 'SwapMember' | 'ResetMembers' | 'ChangeKey' | 'SetPrime' | 'ClearPrime'; +} + +/** @name PalletMembershipError */ +export interface PalletMembershipError extends Enum { + readonly isAlreadyMember: boolean; + readonly isNotMember: boolean; + readonly isTooManyMembers: boolean; + readonly type: 'AlreadyMember' | 'NotMember' | 'TooManyMembers'; +} + +/** @name PalletMembershipEvent */ +export interface PalletMembershipEvent extends Enum { + readonly isMemberAdded: boolean; + readonly isMemberRemoved: boolean; + readonly isMembersSwapped: boolean; + readonly isMembersReset: boolean; + readonly isKeyChanged: boolean; + readonly isDummy: boolean; + readonly type: 'MemberAdded' | 'MemberRemoved' | 'MembersSwapped' | 'MembersReset' | 'KeyChanged' | 'Dummy'; +} + +/** @name PalletNonfungibleError */ +export interface PalletNonfungibleError extends Enum { + readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean; + readonly isNonfungibleItemsHaveNoAmount: boolean; + readonly isCantBurnNftWithChildren: boolean; + readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren'; +} + +/** @name PalletNonfungibleItemData */ +export interface PalletNonfungibleItemData extends Struct { + readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; +} + +/** @name PalletPreimageCall */ +export interface PalletPreimageCall extends Enum { + readonly isNotePreimage: boolean; + readonly asNotePreimage: { + readonly bytes: Bytes; + } & Struct; + readonly isUnnotePreimage: boolean; + readonly asUnnotePreimage: { + readonly hash_: H256; + } & Struct; + readonly isRequestPreimage: boolean; + readonly asRequestPreimage: { + readonly hash_: H256; + } & Struct; + readonly isUnrequestPreimage: boolean; + readonly asUnrequestPreimage: { + readonly hash_: H256; + } & Struct; + readonly type: 'NotePreimage' | 'UnnotePreimage' | 'RequestPreimage' | 'UnrequestPreimage'; +} + +/** @name PalletPreimageError */ +export interface PalletPreimageError extends Enum { + readonly isTooBig: boolean; + readonly isAlreadyNoted: boolean; + readonly isNotAuthorized: boolean; + readonly isNotNoted: boolean; + readonly isRequested: boolean; + readonly isNotRequested: boolean; + readonly type: 'TooBig' | 'AlreadyNoted' | 'NotAuthorized' | 'NotNoted' | 'Requested' | 'NotRequested'; +} + +/** @name PalletPreimageEvent */ +export interface PalletPreimageEvent extends Enum { + readonly isNoted: boolean; + readonly asNoted: { + readonly hash_: H256; + } & Struct; + readonly isRequested: boolean; + readonly asRequested: { + readonly hash_: H256; + } & Struct; + readonly isCleared: boolean; + readonly asCleared: { + readonly hash_: H256; + } & Struct; + readonly type: 'Noted' | 'Requested' | 'Cleared'; +} + +/** @name PalletPreimageRequestStatus */ +export interface PalletPreimageRequestStatus extends Enum { + readonly isUnrequested: boolean; + readonly asUnrequested: { + readonly deposit: ITuple<[AccountId32, u128]>; + readonly len: u32; + } & Struct; + readonly isRequested: boolean; + readonly asRequested: { + readonly deposit: Option>; + readonly count: u32; + readonly len: Option; + } & Struct; + readonly type: 'Unrequested' | 'Requested'; +} + +/** @name PalletRankedCollectiveCall */ +export interface PalletRankedCollectiveCall extends Enum { + readonly isAddMember: boolean; + readonly asAddMember: { + readonly who: MultiAddress; + } & Struct; + readonly isPromoteMember: boolean; + readonly asPromoteMember: { + readonly who: MultiAddress; + } & Struct; + readonly isDemoteMember: boolean; + readonly asDemoteMember: { + readonly who: MultiAddress; + } & Struct; + readonly isRemoveMember: boolean; + readonly asRemoveMember: { + readonly who: MultiAddress; + readonly minRank: u16; + } & Struct; + readonly isVote: boolean; + readonly asVote: { + readonly poll: u32; + readonly aye: bool; + } & Struct; + readonly isCleanupPoll: boolean; + readonly asCleanupPoll: { + readonly pollIndex: u32; + readonly max: u32; + } & Struct; + readonly type: 'AddMember' | 'PromoteMember' | 'DemoteMember' | 'RemoveMember' | 'Vote' | 'CleanupPoll'; +} + +/** @name PalletRankedCollectiveError */ +export interface PalletRankedCollectiveError extends Enum { + readonly isAlreadyMember: boolean; + readonly isNotMember: boolean; + readonly isNotPolling: boolean; + readonly isOngoing: boolean; + readonly isNoneRemaining: boolean; + readonly isCorruption: boolean; + readonly isRankTooLow: boolean; + readonly isInvalidWitness: boolean; + readonly isNoPermission: boolean; + readonly type: 'AlreadyMember' | 'NotMember' | 'NotPolling' | 'Ongoing' | 'NoneRemaining' | 'Corruption' | 'RankTooLow' | 'InvalidWitness' | 'NoPermission'; +} + +/** @name PalletRankedCollectiveEvent */ +export interface PalletRankedCollectiveEvent extends Enum { + readonly isMemberAdded: boolean; + readonly asMemberAdded: { + readonly who: AccountId32; + } & Struct; + readonly isRankChanged: boolean; + readonly asRankChanged: { + readonly who: AccountId32; + readonly rank: u16; + } & Struct; + readonly isMemberRemoved: boolean; + readonly asMemberRemoved: { + readonly who: AccountId32; + readonly rank: u16; + } & Struct; + readonly isVoted: boolean; + readonly asVoted: { + readonly who: AccountId32; + readonly poll: u32; + readonly vote: PalletRankedCollectiveVoteRecord; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly type: 'MemberAdded' | 'RankChanged' | 'MemberRemoved' | 'Voted'; +} + +/** @name PalletRankedCollectiveMemberRecord */ +export interface PalletRankedCollectiveMemberRecord extends Struct { + readonly rank: u16; +} + +/** @name PalletRankedCollectiveTally */ +export interface PalletRankedCollectiveTally extends Struct { + readonly bareAyes: u32; + readonly ayes: u32; + readonly nays: u32; +} + +/** @name PalletRankedCollectiveVoteRecord */ +export interface PalletRankedCollectiveVoteRecord extends Enum { + readonly isAye: boolean; + readonly asAye: u32; + readonly isNay: boolean; + readonly asNay: u32; + readonly type: 'Aye' | 'Nay'; +} + +/** @name PalletReferendaCall */ +export interface PalletReferendaCall extends Enum { + readonly isSubmit: boolean; + readonly asSubmit: { + readonly proposalOrigin: OpalRuntimeOriginCaller; + readonly proposal: FrameSupportPreimagesBounded; + readonly enactmentMoment: FrameSupportScheduleDispatchTime; + } & Struct; + readonly isPlaceDecisionDeposit: boolean; + readonly asPlaceDecisionDeposit: { + readonly index: u32; + } & Struct; + readonly isRefundDecisionDeposit: boolean; + readonly asRefundDecisionDeposit: { + readonly index: u32; + } & Struct; + readonly isCancel: boolean; + readonly asCancel: { + readonly index: u32; + } & Struct; + readonly isKill: boolean; + readonly asKill: { + readonly index: u32; + } & Struct; + readonly isNudgeReferendum: boolean; + readonly asNudgeReferendum: { + readonly index: u32; + } & Struct; + readonly isOneFewerDeciding: boolean; + readonly asOneFewerDeciding: { + readonly track: u16; + } & Struct; + readonly isRefundSubmissionDeposit: boolean; + readonly asRefundSubmissionDeposit: { + readonly index: u32; + } & Struct; + readonly isSetMetadata: boolean; + readonly asSetMetadata: { + readonly index: u32; + readonly maybeHash: Option; + } & Struct; + readonly type: 'Submit' | 'PlaceDecisionDeposit' | 'RefundDecisionDeposit' | 'Cancel' | 'Kill' | 'NudgeReferendum' | 'OneFewerDeciding' | 'RefundSubmissionDeposit' | 'SetMetadata'; +} + +/** @name PalletReferendaCurve */ +export interface PalletReferendaCurve extends Enum { + readonly isLinearDecreasing: boolean; + readonly asLinearDecreasing: { + readonly length: Perbill; + readonly floor: Perbill; + readonly ceil: Perbill; + } & Struct; + readonly isSteppedDecreasing: boolean; + readonly asSteppedDecreasing: { + readonly begin: Perbill; + readonly end: Perbill; + readonly step: Perbill; + readonly period: Perbill; + } & Struct; + readonly isReciprocal: boolean; + readonly asReciprocal: { + readonly factor: i64; + readonly xOffset: i64; + readonly yOffset: i64; + } & Struct; + readonly type: 'LinearDecreasing' | 'SteppedDecreasing' | 'Reciprocal'; +} + +/** @name PalletReferendaDecidingStatus */ +export interface PalletReferendaDecidingStatus extends Struct { + readonly since: u32; + readonly confirming: Option; +} + +/** @name PalletReferendaDeposit */ +export interface PalletReferendaDeposit extends Struct { + readonly who: AccountId32; + readonly amount: u128; +} + +/** @name PalletReferendaError */ +export interface PalletReferendaError extends Enum { + readonly isNotOngoing: boolean; + readonly isHasDeposit: boolean; + readonly isBadTrack: boolean; + readonly isFull: boolean; + readonly isQueueEmpty: boolean; + readonly isBadReferendum: boolean; + readonly isNothingToDo: boolean; + readonly isNoTrack: boolean; + readonly isUnfinished: boolean; + readonly isNoPermission: boolean; + readonly isNoDeposit: boolean; + readonly isBadStatus: boolean; + readonly isPreimageNotExist: boolean; + readonly type: 'NotOngoing' | 'HasDeposit' | 'BadTrack' | 'Full' | 'QueueEmpty' | 'BadReferendum' | 'NothingToDo' | 'NoTrack' | 'Unfinished' | 'NoPermission' | 'NoDeposit' | 'BadStatus' | 'PreimageNotExist'; +} + +/** @name PalletReferendaEvent */ +export interface PalletReferendaEvent extends Enum { + readonly isSubmitted: boolean; + readonly asSubmitted: { + readonly index: u32; + readonly track: u16; + readonly proposal: FrameSupportPreimagesBounded; + } & Struct; + readonly isDecisionDepositPlaced: boolean; + readonly asDecisionDepositPlaced: { + readonly index: u32; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isDecisionDepositRefunded: boolean; + readonly asDecisionDepositRefunded: { + readonly index: u32; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isDepositSlashed: boolean; + readonly asDepositSlashed: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isDecisionStarted: boolean; + readonly asDecisionStarted: { + readonly index: u32; + readonly track: u16; + readonly proposal: FrameSupportPreimagesBounded; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isConfirmStarted: boolean; + readonly asConfirmStarted: { + readonly index: u32; + } & Struct; + readonly isConfirmAborted: boolean; + readonly asConfirmAborted: { + readonly index: u32; + } & Struct; + readonly isConfirmed: boolean; + readonly asConfirmed: { + readonly index: u32; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isApproved: boolean; + readonly asApproved: { + readonly index: u32; + } & Struct; + readonly isRejected: boolean; + readonly asRejected: { + readonly index: u32; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isTimedOut: boolean; + readonly asTimedOut: { + readonly index: u32; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isCancelled: boolean; + readonly asCancelled: { + readonly index: u32; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isKilled: boolean; + readonly asKilled: { + readonly index: u32; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isSubmissionDepositRefunded: boolean; + readonly asSubmissionDepositRefunded: { + readonly index: u32; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isMetadataSet: boolean; + readonly asMetadataSet: { + readonly index: u32; + readonly hash_: H256; + } & Struct; + readonly isMetadataCleared: boolean; + readonly asMetadataCleared: { + readonly index: u32; + readonly hash_: H256; + } & Struct; + readonly type: 'Submitted' | 'DecisionDepositPlaced' | 'DecisionDepositRefunded' | 'DepositSlashed' | 'DecisionStarted' | 'ConfirmStarted' | 'ConfirmAborted' | 'Confirmed' | 'Approved' | 'Rejected' | 'TimedOut' | 'Cancelled' | 'Killed' | 'SubmissionDepositRefunded' | 'MetadataSet' | 'MetadataCleared'; +} + +/** @name PalletReferendaReferendumInfo */ +export interface PalletReferendaReferendumInfo extends Enum { + readonly isOngoing: boolean; + readonly asOngoing: PalletReferendaReferendumStatus; + readonly isApproved: boolean; + readonly asApproved: ITuple<[u32, Option, Option]>; + readonly isRejected: boolean; + readonly asRejected: ITuple<[u32, Option, Option]>; + readonly isCancelled: boolean; + readonly asCancelled: ITuple<[u32, Option, Option]>; + readonly isTimedOut: boolean; + readonly asTimedOut: ITuple<[u32, Option, Option]>; + readonly isKilled: boolean; + readonly asKilled: u32; + readonly type: 'Ongoing' | 'Approved' | 'Rejected' | 'Cancelled' | 'TimedOut' | 'Killed'; +} + +/** @name PalletReferendaReferendumStatus */ +export interface PalletReferendaReferendumStatus extends Struct { + readonly track: u16; + readonly origin: OpalRuntimeOriginCaller; + readonly proposal: FrameSupportPreimagesBounded; + readonly enactment: FrameSupportScheduleDispatchTime; + readonly submitted: u32; + readonly submissionDeposit: PalletReferendaDeposit; + readonly decisionDeposit: Option; + readonly deciding: Option; + readonly tally: PalletRankedCollectiveTally; + readonly inQueue: bool; + readonly alarm: Option]>>; +} + +/** @name PalletReferendaTrackInfo */ +export interface PalletReferendaTrackInfo extends Struct { + readonly name: Text; + readonly maxDeciding: u32; + readonly decisionDeposit: u128; + readonly preparePeriod: u32; + readonly decisionPeriod: u32; + readonly confirmPeriod: u32; + readonly minEnactmentPeriod: u32; + readonly minApproval: PalletReferendaCurve; + readonly minSupport: PalletReferendaCurve; +} + +/** @name PalletRefungibleError */ +export interface PalletRefungibleError extends Enum { + readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean; + readonly isWrongRefungiblePieces: boolean; + readonly isRepartitionWhileNotOwningAllPieces: boolean; + readonly isRefungibleDisallowsNesting: boolean; + readonly isSettingPropertiesNotAllowed: boolean; + readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; +} + +/** @name PalletSchedulerCall */ +export interface PalletSchedulerCall extends Enum { + readonly isSchedule: boolean; + readonly asSchedule: { + readonly when: u32; + readonly maybePeriodic: Option>; + readonly priority: u8; + readonly call: Call; + } & Struct; + readonly isCancel: boolean; + readonly asCancel: { + readonly when: u32; + readonly index: u32; + } & Struct; + readonly isScheduleNamed: boolean; + readonly asScheduleNamed: { + readonly id: U8aFixed; + readonly when: u32; + readonly maybePeriodic: Option>; + readonly priority: u8; + readonly call: Call; + } & Struct; + readonly isCancelNamed: boolean; + readonly asCancelNamed: { + readonly id: U8aFixed; + } & Struct; + readonly isScheduleAfter: boolean; + readonly asScheduleAfter: { + readonly after: u32; + readonly maybePeriodic: Option>; + readonly priority: u8; + readonly call: Call; + } & Struct; + readonly isScheduleNamedAfter: boolean; + readonly asScheduleNamedAfter: { + readonly id: U8aFixed; + readonly after: u32; + readonly maybePeriodic: Option>; + readonly priority: u8; + readonly call: Call; + } & Struct; + readonly type: 'Schedule' | 'Cancel' | 'ScheduleNamed' | 'CancelNamed' | 'ScheduleAfter' | 'ScheduleNamedAfter'; +} + +/** @name PalletSchedulerError */ +export interface PalletSchedulerError extends Enum { + readonly isFailedToSchedule: boolean; + readonly isNotFound: boolean; + readonly isTargetBlockNumberInPast: boolean; + readonly isRescheduleNoChange: boolean; + readonly isNamed: boolean; + readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange' | 'Named'; +} + +/** @name PalletSchedulerEvent */ +export interface PalletSchedulerEvent extends Enum { + readonly isScheduled: boolean; + readonly asScheduled: { + readonly when: u32; + readonly index: u32; + } & Struct; + readonly isCanceled: boolean; + readonly asCanceled: { + readonly when: u32; + readonly index: u32; + } & Struct; + readonly isDispatched: boolean; + readonly asDispatched: { + readonly task: ITuple<[u32, u32]>; + readonly id: Option; + readonly result: Result; + } & Struct; + readonly isCallUnavailable: boolean; + readonly asCallUnavailable: { + readonly task: ITuple<[u32, u32]>; + readonly id: Option; + } & Struct; + readonly isPeriodicFailed: boolean; + readonly asPeriodicFailed: { + readonly task: ITuple<[u32, u32]>; + readonly id: Option; + } & Struct; + readonly isPermanentlyOverweight: boolean; + readonly asPermanentlyOverweight: { + readonly task: ITuple<[u32, u32]>; + readonly id: Option; + } & Struct; + readonly type: 'Scheduled' | 'Canceled' | 'Dispatched' | 'CallUnavailable' | 'PeriodicFailed' | 'PermanentlyOverweight'; +} + +/** @name PalletSchedulerScheduled */ +export interface PalletSchedulerScheduled extends Struct { + readonly maybeId: Option; + readonly priority: u8; + readonly call: FrameSupportPreimagesBounded; + readonly maybePeriodic: Option>; + readonly origin: OpalRuntimeOriginCaller; +} + +/** @name PalletSessionCall */ +export interface PalletSessionCall extends Enum { + readonly isSetKeys: boolean; + readonly asSetKeys: { + readonly keys_: OpalRuntimeRuntimeCommonSessionKeys; + readonly proof: Bytes; + } & Struct; + readonly isPurgeKeys: boolean; + readonly type: 'SetKeys' | 'PurgeKeys'; +} + +/** @name PalletSessionError */ +export interface PalletSessionError extends Enum { + readonly isInvalidProof: boolean; + readonly isNoAssociatedValidatorId: boolean; + readonly isDuplicatedKey: boolean; + readonly isNoKeys: boolean; + readonly isNoAccount: boolean; + readonly type: 'InvalidProof' | 'NoAssociatedValidatorId' | 'DuplicatedKey' | 'NoKeys' | 'NoAccount'; +} + +/** @name PalletSessionEvent */ +export interface PalletSessionEvent extends Enum { + readonly isNewSession: boolean; + readonly asNewSession: { + readonly sessionIndex: u32; + } & Struct; + readonly type: 'NewSession'; +} + +/** @name PalletStateTrieMigrationCall */ +export interface PalletStateTrieMigrationCall extends Enum { + readonly isControlAutoMigration: boolean; + readonly asControlAutoMigration: { + readonly maybeConfig: Option; + } & Struct; + readonly isContinueMigrate: boolean; + readonly asContinueMigrate: { + readonly limits: PalletStateTrieMigrationMigrationLimits; + readonly realSizeUpper: u32; + readonly witnessTask: PalletStateTrieMigrationMigrationTask; + } & Struct; + readonly isMigrateCustomTop: boolean; + readonly asMigrateCustomTop: { + readonly keys_: Vec; + readonly witnessSize: u32; + } & Struct; + readonly isMigrateCustomChild: boolean; + readonly asMigrateCustomChild: { + readonly root: Bytes; + readonly childKeys: Vec; + readonly totalSize: u32; + } & Struct; + readonly isSetSignedMaxLimits: boolean; + readonly asSetSignedMaxLimits: { + readonly limits: PalletStateTrieMigrationMigrationLimits; + } & Struct; + readonly isForceSetProgress: boolean; + readonly asForceSetProgress: { + readonly progressTop: PalletStateTrieMigrationProgress; + readonly progressChild: PalletStateTrieMigrationProgress; + } & Struct; + readonly type: 'ControlAutoMigration' | 'ContinueMigrate' | 'MigrateCustomTop' | 'MigrateCustomChild' | 'SetSignedMaxLimits' | 'ForceSetProgress'; +} + +/** @name PalletStateTrieMigrationError */ +export interface PalletStateTrieMigrationError extends Enum { + readonly isMaxSignedLimits: boolean; + readonly isKeyTooLong: boolean; + readonly isNotEnoughFunds: boolean; + readonly isBadWitness: boolean; + readonly isSignedMigrationNotAllowed: boolean; + readonly isBadChildRoot: boolean; + readonly type: 'MaxSignedLimits' | 'KeyTooLong' | 'NotEnoughFunds' | 'BadWitness' | 'SignedMigrationNotAllowed' | 'BadChildRoot'; +} + +/** @name PalletStateTrieMigrationEvent */ +export interface PalletStateTrieMigrationEvent extends Enum { + readonly isMigrated: boolean; + readonly asMigrated: { + readonly top: u32; + readonly child: u32; + readonly compute: PalletStateTrieMigrationMigrationCompute; + } & Struct; + readonly isSlashed: boolean; + readonly asSlashed: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isAutoMigrationFinished: boolean; + readonly isHalted: boolean; + readonly asHalted: { + readonly error: PalletStateTrieMigrationError; + } & Struct; + readonly type: 'Migrated' | 'Slashed' | 'AutoMigrationFinished' | 'Halted'; +} + +/** @name PalletStateTrieMigrationMigrationCompute */ +export interface PalletStateTrieMigrationMigrationCompute extends Enum { + readonly isSigned: boolean; + readonly isAuto: boolean; + readonly type: 'Signed' | 'Auto'; +} + +/** @name PalletStateTrieMigrationMigrationLimits */ +export interface PalletStateTrieMigrationMigrationLimits extends Struct { + readonly size_: u32; + readonly item: u32; +} + +/** @name PalletStateTrieMigrationMigrationTask */ +export interface PalletStateTrieMigrationMigrationTask extends Struct { + readonly progressTop: PalletStateTrieMigrationProgress; + readonly progressChild: PalletStateTrieMigrationProgress; + readonly size_: u32; + readonly topItems: u32; + readonly childItems: u32; +} + +/** @name PalletStateTrieMigrationProgress */ +export interface PalletStateTrieMigrationProgress extends Enum { + readonly isToStart: boolean; + readonly isLastKey: boolean; + readonly asLastKey: Bytes; + readonly isComplete: boolean; + readonly type: 'ToStart' | 'LastKey' | 'Complete'; +} + +/** @name PalletStructureCall */ +export interface PalletStructureCall extends Null {} + +/** @name PalletStructureError */ +export interface PalletStructureError extends Enum { + readonly isOuroborosDetected: boolean; + readonly isDepthLimit: boolean; + readonly isBreadthLimit: boolean; + readonly isTokenNotFound: boolean; + readonly isCantNestTokenUnderCollection: boolean; + readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound' | 'CantNestTokenUnderCollection'; +} + +/** @name PalletStructureEvent */ +export interface PalletStructureEvent extends Enum { + readonly isExecuted: boolean; + readonly asExecuted: Result; + readonly type: 'Executed'; +} + +/** @name PalletSudoCall */ +export interface PalletSudoCall extends Enum { + readonly isSudo: boolean; + readonly asSudo: { + readonly call: Call; + } & Struct; + readonly isSudoUncheckedWeight: boolean; + readonly asSudoUncheckedWeight: { + readonly call: Call; + readonly weight: SpWeightsWeightV2Weight; + } & Struct; + readonly isSetKey: boolean; + readonly asSetKey: { + readonly new_: MultiAddress; + } & Struct; + readonly isSudoAs: boolean; + readonly asSudoAs: { + readonly who: MultiAddress; + readonly call: Call; + } & Struct; + readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs'; +} + +/** @name PalletSudoError */ +export interface PalletSudoError extends Enum { + readonly isRequireSudo: boolean; + readonly type: 'RequireSudo'; +} + +/** @name PalletSudoEvent */ +export interface PalletSudoEvent extends Enum { + readonly isSudid: boolean; + readonly asSudid: { + readonly sudoResult: Result; + } & Struct; + readonly isKeyChanged: boolean; + readonly asKeyChanged: { + readonly oldSudoer: Option; + } & Struct; + readonly isSudoAsDone: boolean; + readonly asSudoAsDone: { + readonly sudoResult: Result; + } & Struct; + readonly type: 'Sudid' | 'KeyChanged' | 'SudoAsDone'; +} + +/** @name PalletTemplateTransactionPaymentChargeTransactionPayment */ +export interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact {} + +/** @name PalletTestUtilsCall */ +export interface PalletTestUtilsCall extends Enum { + readonly isEnable: boolean; + readonly isSetTestValue: boolean; + readonly asSetTestValue: { + readonly value: u32; + } & Struct; + readonly isSetTestValueAndRollback: boolean; + readonly asSetTestValueAndRollback: { + readonly value: u32; + } & Struct; + readonly isIncTestValue: boolean; + readonly isJustTakeFee: boolean; + readonly isBatchAll: boolean; + readonly asBatchAll: { + readonly calls: Vec; + } & Struct; + readonly type: 'Enable' | 'SetTestValue' | 'SetTestValueAndRollback' | 'IncTestValue' | 'JustTakeFee' | 'BatchAll'; +} + +/** @name PalletTestUtilsError */ +export interface PalletTestUtilsError extends Enum { + readonly isTestPalletDisabled: boolean; + readonly isTriggerRollback: boolean; + readonly type: 'TestPalletDisabled' | 'TriggerRollback'; +} + +/** @name PalletTestUtilsEvent */ +export interface PalletTestUtilsEvent extends Enum { + readonly isValueIsSet: boolean; + readonly isShouldRollback: boolean; + readonly isBatchCompleted: boolean; + readonly type: 'ValueIsSet' | 'ShouldRollback' | 'BatchCompleted'; +} + +/** @name PalletTimestampCall */ +export interface PalletTimestampCall extends Enum { + readonly isSet: boolean; + readonly asSet: { + readonly now: Compact; + } & Struct; + readonly type: 'Set'; +} + +/** @name PalletTransactionPaymentEvent */ +export interface PalletTransactionPaymentEvent extends Enum { + readonly isTransactionFeePaid: boolean; + readonly asTransactionFeePaid: { + readonly who: AccountId32; + readonly actualFee: u128; + readonly tip: u128; + } & Struct; + readonly type: 'TransactionFeePaid'; +} + +/** @name PalletTransactionPaymentReleases */ +export interface PalletTransactionPaymentReleases extends Enum { + readonly isV1Ancient: boolean; + readonly isV2: boolean; + readonly type: 'V1Ancient' | 'V2'; +} + +/** @name PalletTreasuryCall */ +export interface PalletTreasuryCall extends Enum { + readonly isProposeSpend: boolean; + readonly asProposeSpend: { + readonly value: Compact; + readonly beneficiary: MultiAddress; + } & Struct; + readonly isRejectProposal: boolean; + readonly asRejectProposal: { + readonly proposalId: Compact; + } & Struct; + readonly isApproveProposal: boolean; + readonly asApproveProposal: { + readonly proposalId: Compact; + } & Struct; + readonly isSpend: boolean; + readonly asSpend: { + readonly amount: Compact; + readonly beneficiary: MultiAddress; + } & Struct; + readonly isRemoveApproval: boolean; + readonly asRemoveApproval: { + readonly proposalId: Compact; + } & Struct; + readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval'; +} + +/** @name PalletTreasuryError */ +export interface PalletTreasuryError extends Enum { + readonly isInsufficientProposersBalance: boolean; + readonly isInvalidIndex: boolean; + readonly isTooManyApprovals: boolean; + readonly isInsufficientPermission: boolean; + readonly isProposalNotApproved: boolean; + readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved'; +} + +/** @name PalletTreasuryEvent */ +export interface PalletTreasuryEvent extends Enum { + readonly isProposed: boolean; + readonly asProposed: { + readonly proposalIndex: u32; + } & Struct; + readonly isSpending: boolean; + readonly asSpending: { + readonly budgetRemaining: u128; + } & Struct; + readonly isAwarded: boolean; + readonly asAwarded: { + readonly proposalIndex: u32; + readonly award: u128; + readonly account: AccountId32; + } & Struct; + readonly isRejected: boolean; + readonly asRejected: { + readonly proposalIndex: u32; + readonly slashed: u128; + } & Struct; + readonly isBurnt: boolean; + readonly asBurnt: { + readonly burntFunds: u128; + } & Struct; + readonly isRollover: boolean; + readonly asRollover: { + readonly rolloverBalance: u128; + } & Struct; + readonly isDeposit: boolean; + readonly asDeposit: { + readonly value: u128; + } & Struct; + readonly isSpendApproved: boolean; + readonly asSpendApproved: { + readonly proposalIndex: u32; + readonly amount: u128; + readonly beneficiary: AccountId32; + } & Struct; + readonly isUpdatedInactive: boolean; + readonly asUpdatedInactive: { + readonly reactivated: u128; + readonly deactivated: u128; + } & Struct; + readonly type: 'Proposed' | 'Spending' | 'Awarded' | 'Rejected' | 'Burnt' | 'Rollover' | 'Deposit' | 'SpendApproved' | 'UpdatedInactive'; +} + +/** @name PalletTreasuryProposal */ +export interface PalletTreasuryProposal extends Struct { + readonly proposer: AccountId32; + readonly value: u128; + readonly beneficiary: AccountId32; + readonly bond: u128; +} + +/** @name PalletUniqueCall */ +export interface PalletUniqueCall extends Enum { + readonly isCreateCollection: boolean; + readonly asCreateCollection: { + readonly collectionName: Vec; + readonly collectionDescription: Vec; + readonly tokenPrefix: Bytes; + readonly mode: UpDataStructsCollectionMode; + } & Struct; + readonly isCreateCollectionEx: boolean; + readonly asCreateCollectionEx: { + readonly data: UpDataStructsCreateCollectionData; + } & Struct; + readonly isDestroyCollection: boolean; + readonly asDestroyCollection: { + readonly collectionId: u32; + } & Struct; + readonly isAddToAllowList: boolean; + readonly asAddToAllowList: { + readonly collectionId: u32; + readonly address: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isRemoveFromAllowList: boolean; + readonly asRemoveFromAllowList: { + readonly collectionId: u32; + readonly address: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isChangeCollectionOwner: boolean; + readonly asChangeCollectionOwner: { + readonly collectionId: u32; + readonly newOwner: AccountId32; + } & Struct; + readonly isAddCollectionAdmin: boolean; + readonly asAddCollectionAdmin: { + readonly collectionId: u32; + readonly newAdminId: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isRemoveCollectionAdmin: boolean; + readonly asRemoveCollectionAdmin: { + readonly collectionId: u32; + readonly accountId: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isSetCollectionSponsor: boolean; + readonly asSetCollectionSponsor: { + readonly collectionId: u32; + readonly newSponsor: AccountId32; + } & Struct; + readonly isConfirmSponsorship: boolean; + readonly asConfirmSponsorship: { + readonly collectionId: u32; + } & Struct; + readonly isRemoveCollectionSponsor: boolean; + readonly asRemoveCollectionSponsor: { + readonly collectionId: u32; + } & Struct; + readonly isCreateItem: boolean; + readonly asCreateItem: { + readonly collectionId: u32; + readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; + readonly data: UpDataStructsCreateItemData; + } & Struct; + readonly isCreateMultipleItems: boolean; + readonly asCreateMultipleItems: { + readonly collectionId: u32; + readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; + readonly itemsData: Vec; + } & Struct; + readonly isSetCollectionProperties: boolean; + readonly asSetCollectionProperties: { + readonly collectionId: u32; + readonly properties: Vec; + } & Struct; + readonly isDeleteCollectionProperties: boolean; + readonly asDeleteCollectionProperties: { + readonly collectionId: u32; + readonly propertyKeys: Vec; + } & Struct; + readonly isSetTokenProperties: boolean; + readonly asSetTokenProperties: { + readonly collectionId: u32; + readonly tokenId: u32; + readonly properties: Vec; + } & Struct; + readonly isDeleteTokenProperties: boolean; + readonly asDeleteTokenProperties: { + readonly collectionId: u32; + readonly tokenId: u32; + readonly propertyKeys: Vec; + } & Struct; + readonly isSetTokenPropertyPermissions: boolean; + readonly asSetTokenPropertyPermissions: { + readonly collectionId: u32; + readonly propertyPermissions: Vec; + } & Struct; + readonly isCreateMultipleItemsEx: boolean; + readonly asCreateMultipleItemsEx: { + readonly collectionId: u32; + readonly data: UpDataStructsCreateItemExData; + } & Struct; + readonly isSetTransfersEnabledFlag: boolean; + readonly asSetTransfersEnabledFlag: { + readonly collectionId: u32; + readonly value: bool; + } & Struct; + readonly isBurnItem: boolean; + readonly asBurnItem: { + readonly collectionId: u32; + readonly itemId: u32; + readonly value: u128; + } & Struct; + readonly isBurnFrom: boolean; + readonly asBurnFrom: { + readonly collectionId: u32; + readonly from: PalletEvmAccountBasicCrossAccountIdRepr; + readonly itemId: u32; + readonly value: u128; + } & Struct; + readonly isTransfer: boolean; + readonly asTransfer: { + readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr; + readonly collectionId: u32; + readonly itemId: u32; + readonly value: u128; + } & Struct; + readonly isApprove: boolean; + readonly asApprove: { + readonly spender: PalletEvmAccountBasicCrossAccountIdRepr; + readonly collectionId: u32; + readonly itemId: u32; + readonly amount: u128; + } & Struct; + readonly isApproveFrom: boolean; + readonly asApproveFrom: { + readonly from: PalletEvmAccountBasicCrossAccountIdRepr; + readonly to: PalletEvmAccountBasicCrossAccountIdRepr; + readonly collectionId: u32; + readonly itemId: u32; + readonly amount: u128; + } & Struct; + readonly isTransferFrom: boolean; + readonly asTransferFrom: { + readonly from: PalletEvmAccountBasicCrossAccountIdRepr; + readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr; + readonly collectionId: u32; + readonly itemId: u32; + readonly value: u128; + } & Struct; + readonly isSetCollectionLimits: boolean; + readonly asSetCollectionLimits: { + readonly collectionId: u32; + readonly newLimit: UpDataStructsCollectionLimits; + } & Struct; + readonly isSetCollectionPermissions: boolean; + readonly asSetCollectionPermissions: { + readonly collectionId: u32; + readonly newPermission: UpDataStructsCollectionPermissions; + } & Struct; + readonly isRepartition: boolean; + readonly asRepartition: { + readonly collectionId: u32; + readonly tokenId: u32; + readonly amount: u128; + } & Struct; + readonly isSetAllowanceForAll: boolean; + readonly asSetAllowanceForAll: { + readonly collectionId: u32; + readonly operator: PalletEvmAccountBasicCrossAccountIdRepr; + readonly approve: bool; + } & Struct; + readonly isForceRepairCollection: boolean; + readonly asForceRepairCollection: { + readonly collectionId: u32; + } & Struct; + readonly isForceRepairItem: boolean; + readonly asForceRepairItem: { + readonly collectionId: u32; + readonly itemId: u32; + } & Struct; + readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'ApproveFrom' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem'; +} + +/** @name PalletUniqueError */ +export interface PalletUniqueError extends Enum { + readonly isCollectionDecimalPointLimitExceeded: boolean; + readonly isEmptyArgument: boolean; + readonly isRepartitionCalledOnNonRefungibleCollection: boolean; + readonly type: 'CollectionDecimalPointLimitExceeded' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection'; +} + +/** @name PalletUtilityCall */ +export interface PalletUtilityCall extends Enum { + readonly isBatch: boolean; + readonly asBatch: { + readonly calls: Vec; + } & Struct; + readonly isAsDerivative: boolean; + readonly asAsDerivative: { + readonly index: u16; + readonly call: Call; + } & Struct; + readonly isBatchAll: boolean; + readonly asBatchAll: { + readonly calls: Vec; + } & Struct; + readonly isDispatchAs: boolean; + readonly asDispatchAs: { + readonly asOrigin: OpalRuntimeOriginCaller; + readonly call: Call; + } & Struct; + readonly isForceBatch: boolean; + readonly asForceBatch: { + readonly calls: Vec; + } & Struct; + readonly isWithWeight: boolean; + readonly asWithWeight: { + readonly call: Call; + readonly weight: SpWeightsWeightV2Weight; + } & Struct; + readonly type: 'Batch' | 'AsDerivative' | 'BatchAll' | 'DispatchAs' | 'ForceBatch' | 'WithWeight'; +} + +/** @name PalletUtilityError */ +export interface PalletUtilityError extends Enum { + readonly isTooManyCalls: boolean; + readonly type: 'TooManyCalls'; +} + +/** @name PalletUtilityEvent */ +export interface PalletUtilityEvent extends Enum { + readonly isBatchInterrupted: boolean; + readonly asBatchInterrupted: { + readonly index: u32; + readonly error: SpRuntimeDispatchError; + } & Struct; + readonly isBatchCompleted: boolean; + readonly isBatchCompletedWithErrors: boolean; + readonly isItemCompleted: boolean; + readonly isItemFailed: boolean; + readonly asItemFailed: { + readonly error: SpRuntimeDispatchError; + } & Struct; + readonly isDispatchedAs: boolean; + readonly asDispatchedAs: { + readonly result: Result; + } & Struct; + readonly type: 'BatchInterrupted' | 'BatchCompleted' | 'BatchCompletedWithErrors' | 'ItemCompleted' | 'ItemFailed' | 'DispatchedAs'; +} + +/** @name PalletXcmCall */ +export interface PalletXcmCall extends Enum { + readonly isSend: boolean; + readonly asSend: { + readonly dest: StagingXcmVersionedMultiLocation; + readonly message: StagingXcmVersionedXcm; + } & Struct; + readonly isTeleportAssets: boolean; + readonly asTeleportAssets: { + readonly dest: StagingXcmVersionedMultiLocation; + readonly beneficiary: StagingXcmVersionedMultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + readonly feeAssetItem: u32; + } & Struct; + readonly isReserveTransferAssets: boolean; + readonly asReserveTransferAssets: { + readonly dest: StagingXcmVersionedMultiLocation; + readonly beneficiary: StagingXcmVersionedMultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + readonly feeAssetItem: u32; + } & Struct; + readonly isExecute: boolean; + readonly asExecute: { + readonly message: StagingXcmVersionedXcm; + readonly maxWeight: SpWeightsWeightV2Weight; + } & Struct; + readonly isForceXcmVersion: boolean; + readonly asForceXcmVersion: { + readonly location: StagingXcmV3MultiLocation; + readonly version: u32; + } & Struct; + readonly isForceDefaultXcmVersion: boolean; + readonly asForceDefaultXcmVersion: { + readonly maybeXcmVersion: Option; + } & Struct; + readonly isForceSubscribeVersionNotify: boolean; + readonly asForceSubscribeVersionNotify: { + readonly location: StagingXcmVersionedMultiLocation; + } & Struct; + readonly isForceUnsubscribeVersionNotify: boolean; + readonly asForceUnsubscribeVersionNotify: { + readonly location: StagingXcmVersionedMultiLocation; + } & Struct; + readonly isLimitedReserveTransferAssets: boolean; + readonly asLimitedReserveTransferAssets: { + readonly dest: StagingXcmVersionedMultiLocation; + readonly beneficiary: StagingXcmVersionedMultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + readonly feeAssetItem: u32; + readonly weightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isLimitedTeleportAssets: boolean; + readonly asLimitedTeleportAssets: { + readonly dest: StagingXcmVersionedMultiLocation; + readonly beneficiary: StagingXcmVersionedMultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + readonly feeAssetItem: u32; + readonly weightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isForceSuspension: boolean; + readonly asForceSuspension: { + readonly suspended: bool; + } & Struct; + readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets' | 'ForceSuspension'; +} + +/** @name PalletXcmError */ +export interface PalletXcmError extends Enum { + readonly isUnreachable: boolean; + readonly isSendFailure: boolean; + readonly isFiltered: boolean; + readonly isUnweighableMessage: boolean; + readonly isDestinationNotInvertible: boolean; + readonly isEmpty: boolean; + readonly isCannotReanchor: boolean; + readonly isTooManyAssets: boolean; + readonly isInvalidOrigin: boolean; + readonly isBadVersion: boolean; + readonly isBadLocation: boolean; + readonly isNoSubscription: boolean; + readonly isAlreadySubscribed: boolean; + readonly isInvalidAsset: boolean; + readonly isLowBalance: boolean; + readonly isTooManyLocks: boolean; + readonly isAccountNotSovereign: boolean; + readonly isFeesNotMet: boolean; + readonly isLockNotFound: boolean; + readonly isInUse: boolean; + readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed' | 'InvalidAsset' | 'LowBalance' | 'TooManyLocks' | 'AccountNotSovereign' | 'FeesNotMet' | 'LockNotFound' | 'InUse'; +} + +/** @name PalletXcmEvent */ +export interface PalletXcmEvent extends Enum { + readonly isAttempted: boolean; + readonly asAttempted: { + readonly outcome: StagingXcmV3TraitsOutcome; + } & Struct; + readonly isSent: boolean; + readonly asSent: { + readonly origin: StagingXcmV3MultiLocation; + readonly destination: StagingXcmV3MultiLocation; + readonly message: StagingXcmV3Xcm; + readonly messageId: U8aFixed; + } & Struct; + readonly isUnexpectedResponse: boolean; + readonly asUnexpectedResponse: { + readonly origin: StagingXcmV3MultiLocation; + readonly queryId: u64; + } & Struct; + readonly isResponseReady: boolean; + readonly asResponseReady: { + readonly queryId: u64; + readonly response: StagingXcmV3Response; + } & Struct; + readonly isNotified: boolean; + readonly asNotified: { + readonly queryId: u64; + readonly palletIndex: u8; + readonly callIndex: u8; + } & Struct; + readonly isNotifyOverweight: boolean; + readonly asNotifyOverweight: { + readonly queryId: u64; + readonly palletIndex: u8; + readonly callIndex: u8; + readonly actualWeight: SpWeightsWeightV2Weight; + readonly maxBudgetedWeight: SpWeightsWeightV2Weight; + } & Struct; + readonly isNotifyDispatchError: boolean; + readonly asNotifyDispatchError: { + readonly queryId: u64; + readonly palletIndex: u8; + readonly callIndex: u8; + } & Struct; + readonly isNotifyDecodeFailed: boolean; + readonly asNotifyDecodeFailed: { + readonly queryId: u64; + readonly palletIndex: u8; + readonly callIndex: u8; + } & Struct; + readonly isInvalidResponder: boolean; + readonly asInvalidResponder: { + readonly origin: StagingXcmV3MultiLocation; + readonly queryId: u64; + readonly expectedLocation: Option; + } & Struct; + readonly isInvalidResponderVersion: boolean; + readonly asInvalidResponderVersion: { + readonly origin: StagingXcmV3MultiLocation; + readonly queryId: u64; + } & Struct; + readonly isResponseTaken: boolean; + readonly asResponseTaken: { + readonly queryId: u64; + } & Struct; + readonly isAssetsTrapped: boolean; + readonly asAssetsTrapped: { + readonly hash_: H256; + readonly origin: StagingXcmV3MultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + } & Struct; + readonly isVersionChangeNotified: boolean; + readonly asVersionChangeNotified: { + readonly destination: StagingXcmV3MultiLocation; + readonly result: u32; + readonly cost: StagingXcmV3MultiassetMultiAssets; + readonly messageId: U8aFixed; + } & Struct; + readonly isSupportedVersionChanged: boolean; + readonly asSupportedVersionChanged: { + readonly location: StagingXcmV3MultiLocation; + readonly version: u32; + } & Struct; + readonly isNotifyTargetSendFail: boolean; + readonly asNotifyTargetSendFail: { + readonly location: StagingXcmV3MultiLocation; + readonly queryId: u64; + readonly error: StagingXcmV3TraitsError; + } & Struct; + readonly isNotifyTargetMigrationFail: boolean; + readonly asNotifyTargetMigrationFail: { + readonly location: StagingXcmVersionedMultiLocation; + readonly queryId: u64; + } & Struct; + readonly isInvalidQuerierVersion: boolean; + readonly asInvalidQuerierVersion: { + readonly origin: StagingXcmV3MultiLocation; + readonly queryId: u64; + } & Struct; + readonly isInvalidQuerier: boolean; + readonly asInvalidQuerier: { + readonly origin: StagingXcmV3MultiLocation; + readonly queryId: u64; + readonly expectedQuerier: StagingXcmV3MultiLocation; + readonly maybeActualQuerier: Option; + } & Struct; + readonly isVersionNotifyStarted: boolean; + readonly asVersionNotifyStarted: { + readonly destination: StagingXcmV3MultiLocation; + readonly cost: StagingXcmV3MultiassetMultiAssets; + readonly messageId: U8aFixed; + } & Struct; + readonly isVersionNotifyRequested: boolean; + readonly asVersionNotifyRequested: { + readonly destination: StagingXcmV3MultiLocation; + readonly cost: StagingXcmV3MultiassetMultiAssets; + readonly messageId: U8aFixed; + } & Struct; + readonly isVersionNotifyUnrequested: boolean; + readonly asVersionNotifyUnrequested: { + readonly destination: StagingXcmV3MultiLocation; + readonly cost: StagingXcmV3MultiassetMultiAssets; + readonly messageId: U8aFixed; + } & Struct; + readonly isFeesPaid: boolean; + readonly asFeesPaid: { + readonly paying: StagingXcmV3MultiLocation; + readonly fees: StagingXcmV3MultiassetMultiAssets; + } & Struct; + readonly isAssetsClaimed: boolean; + readonly asAssetsClaimed: { + readonly hash_: H256; + readonly origin: StagingXcmV3MultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + } & Struct; + readonly type: 'Attempted' | 'Sent' | 'UnexpectedResponse' | 'ResponseReady' | 'Notified' | 'NotifyOverweight' | 'NotifyDispatchError' | 'NotifyDecodeFailed' | 'InvalidResponder' | 'InvalidResponderVersion' | 'ResponseTaken' | 'AssetsTrapped' | 'VersionChangeNotified' | 'SupportedVersionChanged' | 'NotifyTargetSendFail' | 'NotifyTargetMigrationFail' | 'InvalidQuerierVersion' | 'InvalidQuerier' | 'VersionNotifyStarted' | 'VersionNotifyRequested' | 'VersionNotifyUnrequested' | 'FeesPaid' | 'AssetsClaimed'; +} + +/** @name PalletXcmOrigin */ +export interface PalletXcmOrigin extends Enum { + readonly isXcm: boolean; + readonly asXcm: StagingXcmV3MultiLocation; + readonly isResponse: boolean; + readonly asResponse: StagingXcmV3MultiLocation; + readonly type: 'Xcm' | 'Response'; +} + +/** @name PalletXcmQueryStatus */ +export interface PalletXcmQueryStatus extends Enum { + readonly isPending: boolean; + readonly asPending: { + readonly responder: StagingXcmVersionedMultiLocation; + readonly maybeMatchQuerier: Option; + readonly maybeNotify: Option>; + readonly timeout: u32; + } & Struct; + readonly isVersionNotifier: boolean; + readonly asVersionNotifier: { + readonly origin: StagingXcmVersionedMultiLocation; + readonly isActive: bool; + } & Struct; + readonly isReady: boolean; + readonly asReady: { + readonly response: StagingXcmVersionedResponse; + readonly at: u32; + } & Struct; + readonly type: 'Pending' | 'VersionNotifier' | 'Ready'; +} + +/** @name PalletXcmRemoteLockedFungibleRecord */ +export interface PalletXcmRemoteLockedFungibleRecord extends Struct { + readonly amount: u128; + readonly owner: StagingXcmVersionedMultiLocation; + readonly locker: StagingXcmVersionedMultiLocation; + readonly consumers: Vec>; +} + +/** @name PalletXcmVersionMigrationStage */ +export interface PalletXcmVersionMigrationStage extends Enum { + readonly isMigrateSupportedVersion: boolean; + readonly isMigrateVersionNotifiers: boolean; + readonly isNotifyCurrentTargets: boolean; + readonly asNotifyCurrentTargets: Option; + readonly isMigrateAndNotifyOldTargets: boolean; + readonly type: 'MigrateSupportedVersion' | 'MigrateVersionNotifiers' | 'NotifyCurrentTargets' | 'MigrateAndNotifyOldTargets'; +} + +/** @name ParachainInfoCall */ +export interface ParachainInfoCall extends Null {} + +/** @name PhantomTypeUpDataStructs */ +export interface PhantomTypeUpDataStructs extends Vec> {} + +/** @name PolkadotCorePrimitivesInboundDownwardMessage */ +export interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct { + readonly sentAt: u32; + readonly msg: Bytes; +} + +/** @name PolkadotCorePrimitivesInboundHrmpMessage */ +export interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct { + readonly sentAt: u32; + readonly data: Bytes; +} + +/** @name PolkadotCorePrimitivesOutboundHrmpMessage */ +export interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct { + readonly recipient: u32; + readonly data: Bytes; +} + +/** @name PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat */ +export interface PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat extends Enum { + readonly isConcatenatedVersionedXcm: boolean; + readonly isConcatenatedEncodedBlob: boolean; + readonly isSignals: boolean; + readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals'; +} + +/** @name PolkadotPrimitivesV5AbridgedHostConfiguration */ +export interface PolkadotPrimitivesV5AbridgedHostConfiguration extends Struct { + readonly maxCodeSize: u32; + readonly maxHeadDataSize: u32; + readonly maxUpwardQueueCount: u32; + readonly maxUpwardQueueSize: u32; + readonly maxUpwardMessageSize: u32; + readonly maxUpwardMessageNumPerCandidate: u32; + readonly hrmpMaxMessageNumPerCandidate: u32; + readonly validationUpgradeCooldown: u32; + readonly validationUpgradeDelay: u32; + readonly asyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams; +} + +/** @name PolkadotPrimitivesV5AbridgedHrmpChannel */ +export interface PolkadotPrimitivesV5AbridgedHrmpChannel extends Struct { + readonly maxCapacity: u32; + readonly maxTotalSize: u32; + readonly maxMessageSize: u32; + readonly msgCount: u32; + readonly totalSize: u32; + readonly mqcHead: Option; +} + +/** @name PolkadotPrimitivesV5PersistedValidationData */ +export interface PolkadotPrimitivesV5PersistedValidationData extends Struct { + readonly parentHead: Bytes; + readonly relayParentNumber: u32; + readonly relayParentStorageRoot: H256; + readonly maxPovSize: u32; +} + +/** @name PolkadotPrimitivesV5UpgradeGoAhead */ +export interface PolkadotPrimitivesV5UpgradeGoAhead extends Enum { + readonly isAbort: boolean; + readonly isGoAhead: boolean; + readonly type: 'Abort' | 'GoAhead'; +} + +/** @name PolkadotPrimitivesV5UpgradeRestriction */ +export interface PolkadotPrimitivesV5UpgradeRestriction extends Enum { + readonly isPresent: boolean; + readonly type: 'Present'; +} + +/** @name PolkadotPrimitivesVstagingAsyncBackingParams */ +export interface PolkadotPrimitivesVstagingAsyncBackingParams extends Struct { + readonly maxCandidateDepth: u32; + readonly allowedAncestryLen: u32; +} + +/** @name SpArithmeticArithmeticError */ +export interface SpArithmeticArithmeticError extends Enum { + readonly isUnderflow: boolean; + readonly isOverflow: boolean; + readonly isDivisionByZero: boolean; + readonly type: 'Underflow' | 'Overflow' | 'DivisionByZero'; +} + +/** @name SpConsensusAuraSr25519AppSr25519Public */ +export interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public {} + +/** @name SpCoreCryptoKeyTypeId */ +export interface SpCoreCryptoKeyTypeId extends U8aFixed {} + +/** @name SpCoreEcdsaSignature */ +export interface SpCoreEcdsaSignature extends U8aFixed {} + +/** @name SpCoreEd25519Signature */ +export interface SpCoreEd25519Signature extends U8aFixed {} + +/** @name SpCoreSr25519Public */ +export interface SpCoreSr25519Public extends U8aFixed {} + +/** @name SpCoreSr25519Signature */ +export interface SpCoreSr25519Signature extends U8aFixed {} + +/** @name SpCoreVoid */ +export interface SpCoreVoid extends Null {} + +/** @name SpRuntimeDigest */ +export interface SpRuntimeDigest extends Struct { + readonly logs: Vec; +} + +/** @name SpRuntimeDigestDigestItem */ +export interface SpRuntimeDigestDigestItem extends Enum { + readonly isOther: boolean; + readonly asOther: Bytes; + readonly isConsensus: boolean; + readonly asConsensus: ITuple<[U8aFixed, Bytes]>; + readonly isSeal: boolean; + readonly asSeal: ITuple<[U8aFixed, Bytes]>; + readonly isPreRuntime: boolean; + readonly asPreRuntime: ITuple<[U8aFixed, Bytes]>; + readonly isRuntimeEnvironmentUpdated: boolean; + readonly type: 'Other' | 'Consensus' | 'Seal' | 'PreRuntime' | 'RuntimeEnvironmentUpdated'; +} + +/** @name SpRuntimeDispatchError */ +export interface SpRuntimeDispatchError extends Enum { + readonly isOther: boolean; + readonly isCannotLookup: boolean; + readonly isBadOrigin: boolean; + readonly isModule: boolean; + readonly asModule: SpRuntimeModuleError; + readonly isConsumerRemaining: boolean; + readonly isNoProviders: boolean; + readonly isTooManyConsumers: boolean; + readonly isToken: boolean; + readonly asToken: SpRuntimeTokenError; + readonly isArithmetic: boolean; + readonly asArithmetic: SpArithmeticArithmeticError; + readonly isTransactional: boolean; + readonly asTransactional: SpRuntimeTransactionalError; + readonly isExhausted: boolean; + readonly isCorruption: boolean; + readonly isUnavailable: boolean; + readonly isRootNotAllowed: boolean; + readonly type: 'Other' | 'CannotLookup' | 'BadOrigin' | 'Module' | 'ConsumerRemaining' | 'NoProviders' | 'TooManyConsumers' | 'Token' | 'Arithmetic' | 'Transactional' | 'Exhausted' | 'Corruption' | 'Unavailable' | 'RootNotAllowed'; +} + +/** @name SpRuntimeModuleError */ +export interface SpRuntimeModuleError extends Struct { + readonly index: u8; + readonly error: U8aFixed; +} + +/** @name SpRuntimeMultiSignature */ +export interface SpRuntimeMultiSignature extends Enum { + readonly isEd25519: boolean; + readonly asEd25519: SpCoreEd25519Signature; + readonly isSr25519: boolean; + readonly asSr25519: SpCoreSr25519Signature; + readonly isEcdsa: boolean; + readonly asEcdsa: SpCoreEcdsaSignature; + readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa'; +} + +/** @name SpRuntimeTokenError */ +export interface SpRuntimeTokenError extends Enum { + readonly isFundsUnavailable: boolean; + readonly isOnlyProvider: boolean; + readonly isBelowMinimum: boolean; + readonly isCannotCreate: boolean; + readonly isUnknownAsset: boolean; + readonly isFrozen: boolean; + readonly isUnsupported: boolean; + readonly isCannotCreateHold: boolean; + readonly isNotExpendable: boolean; + readonly isBlocked: boolean; + readonly type: 'FundsUnavailable' | 'OnlyProvider' | 'BelowMinimum' | 'CannotCreate' | 'UnknownAsset' | 'Frozen' | 'Unsupported' | 'CannotCreateHold' | 'NotExpendable' | 'Blocked'; +} + +/** @name SpRuntimeTransactionalError */ +export interface SpRuntimeTransactionalError extends Enum { + readonly isLimitReached: boolean; + readonly isNoLayer: boolean; + readonly type: 'LimitReached' | 'NoLayer'; +} + +/** @name SpRuntimeTransactionValidityInvalidTransaction */ +export interface SpRuntimeTransactionValidityInvalidTransaction extends Enum { + readonly isCall: boolean; + readonly isPayment: boolean; + readonly isFuture: boolean; + readonly isStale: boolean; + readonly isBadProof: boolean; + readonly isAncientBirthBlock: boolean; + readonly isExhaustsResources: boolean; + readonly isCustom: boolean; + readonly asCustom: u8; + readonly isBadMandatory: boolean; + readonly isMandatoryValidation: boolean; + readonly isBadSigner: boolean; + readonly type: 'Call' | 'Payment' | 'Future' | 'Stale' | 'BadProof' | 'AncientBirthBlock' | 'ExhaustsResources' | 'Custom' | 'BadMandatory' | 'MandatoryValidation' | 'BadSigner'; +} + +/** @name SpRuntimeTransactionValidityTransactionValidityError */ +export interface SpRuntimeTransactionValidityTransactionValidityError extends Enum { + readonly isInvalid: boolean; + readonly asInvalid: SpRuntimeTransactionValidityInvalidTransaction; + readonly isUnknown: boolean; + readonly asUnknown: SpRuntimeTransactionValidityUnknownTransaction; + readonly type: 'Invalid' | 'Unknown'; +} + +/** @name SpRuntimeTransactionValidityUnknownTransaction */ +export interface SpRuntimeTransactionValidityUnknownTransaction extends Enum { + readonly isCannotLookup: boolean; + readonly isNoUnsignedValidator: boolean; + readonly isCustom: boolean; + readonly asCustom: u8; + readonly type: 'CannotLookup' | 'NoUnsignedValidator' | 'Custom'; +} + +/** @name SpTrieStorageProof */ +export interface SpTrieStorageProof extends Struct { + readonly trieNodes: BTreeSet; +} + +/** @name SpVersionRuntimeVersion */ +export interface SpVersionRuntimeVersion extends Struct { + readonly specName: Text; + readonly implName: Text; + readonly authoringVersion: u32; + readonly specVersion: u32; + readonly implVersion: u32; + readonly apis: Vec>; + readonly transactionVersion: u32; + readonly stateVersion: u8; +} + +/** @name SpWeightsRuntimeDbWeight */ +export interface SpWeightsRuntimeDbWeight extends Struct { + readonly read: u64; + readonly write: u64; +} + +/** @name SpWeightsWeightV2Weight */ +export interface SpWeightsWeightV2Weight extends Struct { + readonly refTime: Compact; + readonly proofSize: Compact; +} + +/** @name StagingXcmDoubleEncoded */ +export interface StagingXcmDoubleEncoded extends Struct { + readonly encoded: Bytes; +} + +/** @name StagingXcmV2BodyId */ +export interface StagingXcmV2BodyId extends Enum { + readonly isUnit: boolean; + readonly isNamed: boolean; + readonly asNamed: Bytes; + readonly isIndex: boolean; + readonly asIndex: Compact; + readonly isExecutive: boolean; + readonly isTechnical: boolean; + readonly isLegislative: boolean; + readonly isJudicial: boolean; + readonly isDefense: boolean; + readonly isAdministration: boolean; + readonly isTreasury: boolean; + readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury'; +} + +/** @name StagingXcmV2BodyPart */ +export interface StagingXcmV2BodyPart extends Enum { + readonly isVoice: boolean; + readonly isMembers: boolean; + readonly asMembers: { + readonly count: Compact; + } & Struct; + readonly isFraction: boolean; + readonly asFraction: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly isAtLeastProportion: boolean; + readonly asAtLeastProportion: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly isMoreThanProportion: boolean; + readonly asMoreThanProportion: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion'; +} + +/** @name StagingXcmV2Instruction */ +export interface StagingXcmV2Instruction extends Enum { + readonly isWithdrawAsset: boolean; + readonly asWithdrawAsset: StagingXcmV2MultiassetMultiAssets; + readonly isReserveAssetDeposited: boolean; + readonly asReserveAssetDeposited: StagingXcmV2MultiassetMultiAssets; + readonly isReceiveTeleportedAsset: boolean; + readonly asReceiveTeleportedAsset: StagingXcmV2MultiassetMultiAssets; + readonly isQueryResponse: boolean; + readonly asQueryResponse: { + readonly queryId: Compact; + readonly response: StagingXcmV2Response; + readonly maxWeight: Compact; + } & Struct; + readonly isTransferAsset: boolean; + readonly asTransferAsset: { + readonly assets: StagingXcmV2MultiassetMultiAssets; + readonly beneficiary: StagingXcmV2MultiLocation; + } & Struct; + readonly isTransferReserveAsset: boolean; + readonly asTransferReserveAsset: { + readonly assets: StagingXcmV2MultiassetMultiAssets; + readonly dest: StagingXcmV2MultiLocation; + readonly xcm: StagingXcmV2Xcm; + } & Struct; + readonly isTransact: boolean; + readonly asTransact: { + readonly originType: StagingXcmV2OriginKind; + readonly requireWeightAtMost: Compact; + readonly call: StagingXcmDoubleEncoded; + } & Struct; + readonly isHrmpNewChannelOpenRequest: boolean; + readonly asHrmpNewChannelOpenRequest: { + readonly sender: Compact; + readonly maxMessageSize: Compact; + readonly maxCapacity: Compact; + } & Struct; + readonly isHrmpChannelAccepted: boolean; + readonly asHrmpChannelAccepted: { + readonly recipient: Compact; + } & Struct; + readonly isHrmpChannelClosing: boolean; + readonly asHrmpChannelClosing: { + readonly initiator: Compact; + readonly sender: Compact; + readonly recipient: Compact; + } & Struct; + readonly isClearOrigin: boolean; + readonly isDescendOrigin: boolean; + readonly asDescendOrigin: StagingXcmV2MultilocationJunctions; + readonly isReportError: boolean; + readonly asReportError: { + readonly queryId: Compact; + readonly dest: StagingXcmV2MultiLocation; + readonly maxResponseWeight: Compact; + } & Struct; + readonly isDepositAsset: boolean; + readonly asDepositAsset: { + readonly assets: StagingXcmV2MultiassetMultiAssetFilter; + readonly maxAssets: Compact; + readonly beneficiary: StagingXcmV2MultiLocation; + } & Struct; + readonly isDepositReserveAsset: boolean; + readonly asDepositReserveAsset: { + readonly assets: StagingXcmV2MultiassetMultiAssetFilter; + readonly maxAssets: Compact; + readonly dest: StagingXcmV2MultiLocation; + readonly xcm: StagingXcmV2Xcm; + } & Struct; + readonly isExchangeAsset: boolean; + readonly asExchangeAsset: { + readonly give: StagingXcmV2MultiassetMultiAssetFilter; + readonly receive: StagingXcmV2MultiassetMultiAssets; + } & Struct; + readonly isInitiateReserveWithdraw: boolean; + readonly asInitiateReserveWithdraw: { + readonly assets: StagingXcmV2MultiassetMultiAssetFilter; + readonly reserve: StagingXcmV2MultiLocation; + readonly xcm: StagingXcmV2Xcm; + } & Struct; + readonly isInitiateTeleport: boolean; + readonly asInitiateTeleport: { + readonly assets: StagingXcmV2MultiassetMultiAssetFilter; + readonly dest: StagingXcmV2MultiLocation; + readonly xcm: StagingXcmV2Xcm; + } & Struct; + readonly isQueryHolding: boolean; + readonly asQueryHolding: { + readonly queryId: Compact; + readonly dest: StagingXcmV2MultiLocation; + readonly assets: StagingXcmV2MultiassetMultiAssetFilter; + readonly maxResponseWeight: Compact; + } & Struct; + readonly isBuyExecution: boolean; + readonly asBuyExecution: { + readonly fees: StagingXcmV2MultiAsset; + readonly weightLimit: StagingXcmV2WeightLimit; + } & Struct; + readonly isRefundSurplus: boolean; + readonly isSetErrorHandler: boolean; + readonly asSetErrorHandler: StagingXcmV2Xcm; + readonly isSetAppendix: boolean; + readonly asSetAppendix: StagingXcmV2Xcm; + readonly isClearError: boolean; + readonly isClaimAsset: boolean; + readonly asClaimAsset: { + readonly assets: StagingXcmV2MultiassetMultiAssets; + readonly ticket: StagingXcmV2MultiLocation; + } & Struct; + readonly isTrap: boolean; + readonly asTrap: Compact; + readonly isSubscribeVersion: boolean; + readonly asSubscribeVersion: { + readonly queryId: Compact; + readonly maxResponseWeight: Compact; + } & Struct; + readonly isUnsubscribeVersion: boolean; + readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion'; +} + +/** @name StagingXcmV2Junction */ +export interface StagingXcmV2Junction extends Enum { + readonly isParachain: boolean; + readonly asParachain: Compact; + readonly isAccountId32: boolean; + readonly asAccountId32: { + readonly network: StagingXcmV2NetworkId; + readonly id: U8aFixed; + } & Struct; + readonly isAccountIndex64: boolean; + readonly asAccountIndex64: { + readonly network: StagingXcmV2NetworkId; + readonly index: Compact; + } & Struct; + readonly isAccountKey20: boolean; + readonly asAccountKey20: { + readonly network: StagingXcmV2NetworkId; + readonly key: U8aFixed; + } & Struct; + readonly isPalletInstance: boolean; + readonly asPalletInstance: u8; + readonly isGeneralIndex: boolean; + readonly asGeneralIndex: Compact; + readonly isGeneralKey: boolean; + readonly asGeneralKey: Bytes; + readonly isOnlyChild: boolean; + readonly isPlurality: boolean; + readonly asPlurality: { + readonly id: StagingXcmV2BodyId; + readonly part: StagingXcmV2BodyPart; + } & Struct; + readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality'; +} + +/** @name StagingXcmV2MultiAsset */ +export interface StagingXcmV2MultiAsset extends Struct { + readonly id: StagingXcmV2MultiassetAssetId; + readonly fun: StagingXcmV2MultiassetFungibility; +} + +/** @name StagingXcmV2MultiassetAssetId */ +export interface StagingXcmV2MultiassetAssetId extends Enum { + readonly isConcrete: boolean; + readonly asConcrete: StagingXcmV2MultiLocation; + readonly isAbstract: boolean; + readonly asAbstract: Bytes; + readonly type: 'Concrete' | 'Abstract'; +} + +/** @name StagingXcmV2MultiassetAssetInstance */ +export interface StagingXcmV2MultiassetAssetInstance extends Enum { + readonly isUndefined: boolean; + readonly isIndex: boolean; + readonly asIndex: Compact; + readonly isArray4: boolean; + readonly asArray4: U8aFixed; + readonly isArray8: boolean; + readonly asArray8: U8aFixed; + readonly isArray16: boolean; + readonly asArray16: U8aFixed; + readonly isArray32: boolean; + readonly asArray32: U8aFixed; + readonly isBlob: boolean; + readonly asBlob: Bytes; + readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob'; +} + +/** @name StagingXcmV2MultiassetFungibility */ +export interface StagingXcmV2MultiassetFungibility extends Enum { + readonly isFungible: boolean; + readonly asFungible: Compact; + readonly isNonFungible: boolean; + readonly asNonFungible: StagingXcmV2MultiassetAssetInstance; + readonly type: 'Fungible' | 'NonFungible'; +} + +/** @name StagingXcmV2MultiassetMultiAssetFilter */ +export interface StagingXcmV2MultiassetMultiAssetFilter extends Enum { + readonly isDefinite: boolean; + readonly asDefinite: StagingXcmV2MultiassetMultiAssets; + readonly isWild: boolean; + readonly asWild: StagingXcmV2MultiassetWildMultiAsset; + readonly type: 'Definite' | 'Wild'; +} + +/** @name StagingXcmV2MultiassetMultiAssets */ +export interface StagingXcmV2MultiassetMultiAssets extends Vec {} + +/** @name StagingXcmV2MultiassetWildFungibility */ +export interface StagingXcmV2MultiassetWildFungibility extends Enum { + readonly isFungible: boolean; + readonly isNonFungible: boolean; + readonly type: 'Fungible' | 'NonFungible'; +} + +/** @name StagingXcmV2MultiassetWildMultiAsset */ +export interface StagingXcmV2MultiassetWildMultiAsset extends Enum { + readonly isAll: boolean; + readonly isAllOf: boolean; + readonly asAllOf: { + readonly id: StagingXcmV2MultiassetAssetId; + readonly fun: StagingXcmV2MultiassetWildFungibility; + } & Struct; + readonly type: 'All' | 'AllOf'; +} + +/** @name StagingXcmV2MultiLocation */ +export interface StagingXcmV2MultiLocation extends Struct { + readonly parents: u8; + readonly interior: StagingXcmV2MultilocationJunctions; +} + +/** @name StagingXcmV2MultilocationJunctions */ +export interface StagingXcmV2MultilocationJunctions extends Enum { + readonly isHere: boolean; + readonly isX1: boolean; + readonly asX1: StagingXcmV2Junction; + readonly isX2: boolean; + readonly asX2: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX3: boolean; + readonly asX3: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX4: boolean; + readonly asX4: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX5: boolean; + readonly asX5: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX6: boolean; + readonly asX6: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX7: boolean; + readonly asX7: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX8: boolean; + readonly asX8: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; +} + +/** @name StagingXcmV2NetworkId */ +export interface StagingXcmV2NetworkId extends Enum { + readonly isAny: boolean; + readonly isNamed: boolean; + readonly asNamed: Bytes; + readonly isPolkadot: boolean; + readonly isKusama: boolean; + readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama'; +} + +/** @name StagingXcmV2OriginKind */ +export interface StagingXcmV2OriginKind extends Enum { + readonly isNative: boolean; + readonly isSovereignAccount: boolean; + readonly isSuperuser: boolean; + readonly isXcm: boolean; + readonly type: 'Native' | 'SovereignAccount' | 'Superuser' | 'Xcm'; +} + +/** @name StagingXcmV2Response */ +export interface StagingXcmV2Response extends Enum { + readonly isNull: boolean; + readonly isAssets: boolean; + readonly asAssets: StagingXcmV2MultiassetMultiAssets; + readonly isExecutionResult: boolean; + readonly asExecutionResult: Option>; + readonly isVersion: boolean; + readonly asVersion: u32; + readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version'; +} + +/** @name StagingXcmV2TraitsError */ +export interface StagingXcmV2TraitsError extends Enum { + readonly isOverflow: boolean; + readonly isUnimplemented: boolean; + readonly isUntrustedReserveLocation: boolean; + readonly isUntrustedTeleportLocation: boolean; + readonly isMultiLocationFull: boolean; + readonly isMultiLocationNotInvertible: boolean; + readonly isBadOrigin: boolean; + readonly isInvalidLocation: boolean; + readonly isAssetNotFound: boolean; + readonly isFailedToTransactAsset: boolean; + readonly isNotWithdrawable: boolean; + readonly isLocationCannotHold: boolean; + readonly isExceedsMaxMessageSize: boolean; + readonly isDestinationUnsupported: boolean; + readonly isTransport: boolean; + readonly isUnroutable: boolean; + readonly isUnknownClaim: boolean; + readonly isFailedToDecode: boolean; + readonly isMaxWeightInvalid: boolean; + readonly isNotHoldingFees: boolean; + readonly isTooExpensive: boolean; + readonly isTrap: boolean; + readonly asTrap: u64; + readonly isUnhandledXcmVersion: boolean; + readonly isWeightLimitReached: boolean; + readonly asWeightLimitReached: u64; + readonly isBarrier: boolean; + readonly isWeightNotComputable: boolean; + readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'MultiLocationFull' | 'MultiLocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable'; +} + +/** @name StagingXcmV2WeightLimit */ +export interface StagingXcmV2WeightLimit extends Enum { + readonly isUnlimited: boolean; + readonly isLimited: boolean; + readonly asLimited: Compact; + readonly type: 'Unlimited' | 'Limited'; +} + +/** @name StagingXcmV2Xcm */ +export interface StagingXcmV2Xcm extends Vec {} + +/** @name StagingXcmV3Instruction */ +export interface StagingXcmV3Instruction extends Enum { + readonly isWithdrawAsset: boolean; + readonly asWithdrawAsset: StagingXcmV3MultiassetMultiAssets; + readonly isReserveAssetDeposited: boolean; + readonly asReserveAssetDeposited: StagingXcmV3MultiassetMultiAssets; + readonly isReceiveTeleportedAsset: boolean; + readonly asReceiveTeleportedAsset: StagingXcmV3MultiassetMultiAssets; + readonly isQueryResponse: boolean; + readonly asQueryResponse: { + readonly queryId: Compact; + readonly response: StagingXcmV3Response; + readonly maxWeight: SpWeightsWeightV2Weight; + readonly querier: Option; + } & Struct; + readonly isTransferAsset: boolean; + readonly asTransferAsset: { + readonly assets: StagingXcmV3MultiassetMultiAssets; + readonly beneficiary: StagingXcmV3MultiLocation; + } & Struct; + readonly isTransferReserveAsset: boolean; + readonly asTransferReserveAsset: { + readonly assets: StagingXcmV3MultiassetMultiAssets; + readonly dest: StagingXcmV3MultiLocation; + readonly xcm: StagingXcmV3Xcm; + } & Struct; + readonly isTransact: boolean; + readonly asTransact: { + readonly originKind: StagingXcmV2OriginKind; + readonly requireWeightAtMost: SpWeightsWeightV2Weight; + readonly call: StagingXcmDoubleEncoded; + } & Struct; + readonly isHrmpNewChannelOpenRequest: boolean; + readonly asHrmpNewChannelOpenRequest: { + readonly sender: Compact; + readonly maxMessageSize: Compact; + readonly maxCapacity: Compact; + } & Struct; + readonly isHrmpChannelAccepted: boolean; + readonly asHrmpChannelAccepted: { + readonly recipient: Compact; + } & Struct; + readonly isHrmpChannelClosing: boolean; + readonly asHrmpChannelClosing: { + readonly initiator: Compact; + readonly sender: Compact; + readonly recipient: Compact; + } & Struct; + readonly isClearOrigin: boolean; + readonly isDescendOrigin: boolean; + readonly asDescendOrigin: StagingXcmV3Junctions; + readonly isReportError: boolean; + readonly asReportError: StagingXcmV3QueryResponseInfo; + readonly isDepositAsset: boolean; + readonly asDepositAsset: { + readonly assets: StagingXcmV3MultiassetMultiAssetFilter; + readonly beneficiary: StagingXcmV3MultiLocation; + } & Struct; + readonly isDepositReserveAsset: boolean; + readonly asDepositReserveAsset: { + readonly assets: StagingXcmV3MultiassetMultiAssetFilter; + readonly dest: StagingXcmV3MultiLocation; + readonly xcm: StagingXcmV3Xcm; + } & Struct; + readonly isExchangeAsset: boolean; + readonly asExchangeAsset: { + readonly give: StagingXcmV3MultiassetMultiAssetFilter; + readonly want: StagingXcmV3MultiassetMultiAssets; + readonly maximal: bool; + } & Struct; + readonly isInitiateReserveWithdraw: boolean; + readonly asInitiateReserveWithdraw: { + readonly assets: StagingXcmV3MultiassetMultiAssetFilter; + readonly reserve: StagingXcmV3MultiLocation; + readonly xcm: StagingXcmV3Xcm; + } & Struct; + readonly isInitiateTeleport: boolean; + readonly asInitiateTeleport: { + readonly assets: StagingXcmV3MultiassetMultiAssetFilter; + readonly dest: StagingXcmV3MultiLocation; + readonly xcm: StagingXcmV3Xcm; + } & Struct; + readonly isReportHolding: boolean; + readonly asReportHolding: { + readonly responseInfo: StagingXcmV3QueryResponseInfo; + readonly assets: StagingXcmV3MultiassetMultiAssetFilter; + } & Struct; + readonly isBuyExecution: boolean; + readonly asBuyExecution: { + readonly fees: StagingXcmV3MultiAsset; + readonly weightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isRefundSurplus: boolean; + readonly isSetErrorHandler: boolean; + readonly asSetErrorHandler: StagingXcmV3Xcm; + readonly isSetAppendix: boolean; + readonly asSetAppendix: StagingXcmV3Xcm; + readonly isClearError: boolean; + readonly isClaimAsset: boolean; + readonly asClaimAsset: { + readonly assets: StagingXcmV3MultiassetMultiAssets; + readonly ticket: StagingXcmV3MultiLocation; + } & Struct; + readonly isTrap: boolean; + readonly asTrap: Compact; + readonly isSubscribeVersion: boolean; + readonly asSubscribeVersion: { + readonly queryId: Compact; + readonly maxResponseWeight: SpWeightsWeightV2Weight; + } & Struct; + readonly isUnsubscribeVersion: boolean; + readonly isBurnAsset: boolean; + readonly asBurnAsset: StagingXcmV3MultiassetMultiAssets; + readonly isExpectAsset: boolean; + readonly asExpectAsset: StagingXcmV3MultiassetMultiAssets; + readonly isExpectOrigin: boolean; + readonly asExpectOrigin: Option; + readonly isExpectError: boolean; + readonly asExpectError: Option>; + readonly isExpectTransactStatus: boolean; + readonly asExpectTransactStatus: StagingXcmV3MaybeErrorCode; + readonly isQueryPallet: boolean; + readonly asQueryPallet: { + readonly moduleName: Bytes; + readonly responseInfo: StagingXcmV3QueryResponseInfo; + } & Struct; + readonly isExpectPallet: boolean; + readonly asExpectPallet: { + readonly index: Compact; + readonly name: Bytes; + readonly moduleName: Bytes; + readonly crateMajor: Compact; + readonly minCrateMinor: Compact; + } & Struct; + readonly isReportTransactStatus: boolean; + readonly asReportTransactStatus: StagingXcmV3QueryResponseInfo; + readonly isClearTransactStatus: boolean; + readonly isUniversalOrigin: boolean; + readonly asUniversalOrigin: StagingXcmV3Junction; + readonly isExportMessage: boolean; + readonly asExportMessage: { + readonly network: StagingXcmV3JunctionNetworkId; + readonly destination: StagingXcmV3Junctions; + readonly xcm: StagingXcmV3Xcm; + } & Struct; + readonly isLockAsset: boolean; + readonly asLockAsset: { + readonly asset: StagingXcmV3MultiAsset; + readonly unlocker: StagingXcmV3MultiLocation; + } & Struct; + readonly isUnlockAsset: boolean; + readonly asUnlockAsset: { + readonly asset: StagingXcmV3MultiAsset; + readonly target: StagingXcmV3MultiLocation; + } & Struct; + readonly isNoteUnlockable: boolean; + readonly asNoteUnlockable: { + readonly asset: StagingXcmV3MultiAsset; + readonly owner: StagingXcmV3MultiLocation; + } & Struct; + readonly isRequestUnlock: boolean; + readonly asRequestUnlock: { + readonly asset: StagingXcmV3MultiAsset; + readonly locker: StagingXcmV3MultiLocation; + } & Struct; + readonly isSetFeesMode: boolean; + readonly asSetFeesMode: { + readonly jitWithdraw: bool; + } & Struct; + readonly isSetTopic: boolean; + readonly asSetTopic: U8aFixed; + readonly isClearTopic: boolean; + readonly isAliasOrigin: boolean; + readonly asAliasOrigin: StagingXcmV3MultiLocation; + readonly isUnpaidExecution: boolean; + readonly asUnpaidExecution: { + readonly weightLimit: StagingXcmV3WeightLimit; + readonly checkOrigin: Option; + } & Struct; + readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'ReportHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion' | 'BurnAsset' | 'ExpectAsset' | 'ExpectOrigin' | 'ExpectError' | 'ExpectTransactStatus' | 'QueryPallet' | 'ExpectPallet' | 'ReportTransactStatus' | 'ClearTransactStatus' | 'UniversalOrigin' | 'ExportMessage' | 'LockAsset' | 'UnlockAsset' | 'NoteUnlockable' | 'RequestUnlock' | 'SetFeesMode' | 'SetTopic' | 'ClearTopic' | 'AliasOrigin' | 'UnpaidExecution'; +} + +/** @name StagingXcmV3Junction */ +export interface StagingXcmV3Junction extends Enum { + readonly isParachain: boolean; + readonly asParachain: Compact; + readonly isAccountId32: boolean; + readonly asAccountId32: { + readonly network: Option; + readonly id: U8aFixed; + } & Struct; + readonly isAccountIndex64: boolean; + readonly asAccountIndex64: { + readonly network: Option; + readonly index: Compact; + } & Struct; + readonly isAccountKey20: boolean; + readonly asAccountKey20: { + readonly network: Option; + readonly key: U8aFixed; + } & Struct; + readonly isPalletInstance: boolean; + readonly asPalletInstance: u8; + readonly isGeneralIndex: boolean; + readonly asGeneralIndex: Compact; + readonly isGeneralKey: boolean; + readonly asGeneralKey: { + readonly length: u8; + readonly data: U8aFixed; + } & Struct; + readonly isOnlyChild: boolean; + readonly isPlurality: boolean; + readonly asPlurality: { + readonly id: StagingXcmV3JunctionBodyId; + readonly part: StagingXcmV3JunctionBodyPart; + } & Struct; + readonly isGlobalConsensus: boolean; + readonly asGlobalConsensus: StagingXcmV3JunctionNetworkId; + readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality' | 'GlobalConsensus'; +} + +/** @name StagingXcmV3JunctionBodyId */ +export interface StagingXcmV3JunctionBodyId extends Enum { + readonly isUnit: boolean; + readonly isMoniker: boolean; + readonly asMoniker: U8aFixed; + readonly isIndex: boolean; + readonly asIndex: Compact; + readonly isExecutive: boolean; + readonly isTechnical: boolean; + readonly isLegislative: boolean; + readonly isJudicial: boolean; + readonly isDefense: boolean; + readonly isAdministration: boolean; + readonly isTreasury: boolean; + readonly type: 'Unit' | 'Moniker' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury'; +} + +/** @name StagingXcmV3JunctionBodyPart */ +export interface StagingXcmV3JunctionBodyPart extends Enum { + readonly isVoice: boolean; + readonly isMembers: boolean; + readonly asMembers: { + readonly count: Compact; + } & Struct; + readonly isFraction: boolean; + readonly asFraction: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly isAtLeastProportion: boolean; + readonly asAtLeastProportion: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly isMoreThanProportion: boolean; + readonly asMoreThanProportion: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion'; +} + +/** @name StagingXcmV3JunctionNetworkId */ +export interface StagingXcmV3JunctionNetworkId extends Enum { + readonly isByGenesis: boolean; + readonly asByGenesis: U8aFixed; + readonly isByFork: boolean; + readonly asByFork: { + readonly blockNumber: u64; + readonly blockHash: U8aFixed; + } & Struct; + readonly isPolkadot: boolean; + readonly isKusama: boolean; + readonly isWestend: boolean; + readonly isRococo: boolean; + readonly isWococo: boolean; + readonly isEthereum: boolean; + readonly asEthereum: { + readonly chainId: Compact; + } & Struct; + readonly isBitcoinCore: boolean; + readonly isBitcoinCash: boolean; + readonly type: 'ByGenesis' | 'ByFork' | 'Polkadot' | 'Kusama' | 'Westend' | 'Rococo' | 'Wococo' | 'Ethereum' | 'BitcoinCore' | 'BitcoinCash'; +} + +/** @name StagingXcmV3Junctions */ +export interface StagingXcmV3Junctions extends Enum { + readonly isHere: boolean; + readonly isX1: boolean; + readonly asX1: StagingXcmV3Junction; + readonly isX2: boolean; + readonly asX2: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX3: boolean; + readonly asX3: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX4: boolean; + readonly asX4: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX5: boolean; + readonly asX5: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX6: boolean; + readonly asX6: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX7: boolean; + readonly asX7: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX8: boolean; + readonly asX8: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; +} + +/** @name StagingXcmV3MaybeErrorCode */ +export interface StagingXcmV3MaybeErrorCode extends Enum { + readonly isSuccess: boolean; + readonly isError: boolean; + readonly asError: Bytes; + readonly isTruncatedError: boolean; + readonly asTruncatedError: Bytes; + readonly type: 'Success' | 'Error' | 'TruncatedError'; +} + +/** @name StagingXcmV3MultiAsset */ +export interface StagingXcmV3MultiAsset extends Struct { + readonly id: StagingXcmV3MultiassetAssetId; + readonly fun: StagingXcmV3MultiassetFungibility; +} + +/** @name StagingXcmV3MultiassetAssetId */ +export interface StagingXcmV3MultiassetAssetId extends Enum { + readonly isConcrete: boolean; + readonly asConcrete: StagingXcmV3MultiLocation; + readonly isAbstract: boolean; + readonly asAbstract: U8aFixed; + readonly type: 'Concrete' | 'Abstract'; +} + +/** @name StagingXcmV3MultiassetAssetInstance */ +export interface StagingXcmV3MultiassetAssetInstance extends Enum { + readonly isUndefined: boolean; + readonly isIndex: boolean; + readonly asIndex: Compact; + readonly isArray4: boolean; + readonly asArray4: U8aFixed; + readonly isArray8: boolean; + readonly asArray8: U8aFixed; + readonly isArray16: boolean; + readonly asArray16: U8aFixed; + readonly isArray32: boolean; + readonly asArray32: U8aFixed; + readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32'; +} + +/** @name StagingXcmV3MultiassetFungibility */ +export interface StagingXcmV3MultiassetFungibility extends Enum { + readonly isFungible: boolean; + readonly asFungible: Compact; + readonly isNonFungible: boolean; + readonly asNonFungible: StagingXcmV3MultiassetAssetInstance; + readonly type: 'Fungible' | 'NonFungible'; +} + +/** @name StagingXcmV3MultiassetMultiAssetFilter */ +export interface StagingXcmV3MultiassetMultiAssetFilter extends Enum { + readonly isDefinite: boolean; + readonly asDefinite: StagingXcmV3MultiassetMultiAssets; + readonly isWild: boolean; + readonly asWild: StagingXcmV3MultiassetWildMultiAsset; + readonly type: 'Definite' | 'Wild'; +} + +/** @name StagingXcmV3MultiassetMultiAssets */ +export interface StagingXcmV3MultiassetMultiAssets extends Vec {} + +/** @name StagingXcmV3MultiassetWildFungibility */ +export interface StagingXcmV3MultiassetWildFungibility extends Enum { + readonly isFungible: boolean; + readonly isNonFungible: boolean; + readonly type: 'Fungible' | 'NonFungible'; +} + +/** @name StagingXcmV3MultiassetWildMultiAsset */ +export interface StagingXcmV3MultiassetWildMultiAsset extends Enum { + readonly isAll: boolean; + readonly isAllOf: boolean; + readonly asAllOf: { + readonly id: StagingXcmV3MultiassetAssetId; + readonly fun: StagingXcmV3MultiassetWildFungibility; + } & Struct; + readonly isAllCounted: boolean; + readonly asAllCounted: Compact; + readonly isAllOfCounted: boolean; + readonly asAllOfCounted: { + readonly id: StagingXcmV3MultiassetAssetId; + readonly fun: StagingXcmV3MultiassetWildFungibility; + readonly count: Compact; + } & Struct; + readonly type: 'All' | 'AllOf' | 'AllCounted' | 'AllOfCounted'; +} + +/** @name StagingXcmV3MultiLocation */ +export interface StagingXcmV3MultiLocation extends Struct { + readonly parents: u8; + readonly interior: StagingXcmV3Junctions; +} + +/** @name StagingXcmV3PalletInfo */ +export interface StagingXcmV3PalletInfo extends Struct { + readonly index: Compact; + readonly name: Bytes; + readonly moduleName: Bytes; + readonly major: Compact; + readonly minor: Compact; + readonly patch: Compact; +} + +/** @name StagingXcmV3QueryResponseInfo */ +export interface StagingXcmV3QueryResponseInfo extends Struct { + readonly destination: StagingXcmV3MultiLocation; + readonly queryId: Compact; + readonly maxWeight: SpWeightsWeightV2Weight; +} + +/** @name StagingXcmV3Response */ +export interface StagingXcmV3Response extends Enum { + readonly isNull: boolean; + readonly isAssets: boolean; + readonly asAssets: StagingXcmV3MultiassetMultiAssets; + readonly isExecutionResult: boolean; + readonly asExecutionResult: Option>; + readonly isVersion: boolean; + readonly asVersion: u32; + readonly isPalletsInfo: boolean; + readonly asPalletsInfo: Vec; + readonly isDispatchResult: boolean; + readonly asDispatchResult: StagingXcmV3MaybeErrorCode; + readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version' | 'PalletsInfo' | 'DispatchResult'; +} + +/** @name StagingXcmV3TraitsError */ +export interface StagingXcmV3TraitsError extends Enum { + readonly isOverflow: boolean; + readonly isUnimplemented: boolean; + readonly isUntrustedReserveLocation: boolean; + readonly isUntrustedTeleportLocation: boolean; + readonly isLocationFull: boolean; + readonly isLocationNotInvertible: boolean; + readonly isBadOrigin: boolean; + readonly isInvalidLocation: boolean; + readonly isAssetNotFound: boolean; + readonly isFailedToTransactAsset: boolean; + readonly isNotWithdrawable: boolean; + readonly isLocationCannotHold: boolean; + readonly isExceedsMaxMessageSize: boolean; + readonly isDestinationUnsupported: boolean; + readonly isTransport: boolean; + readonly isUnroutable: boolean; + readonly isUnknownClaim: boolean; + readonly isFailedToDecode: boolean; + readonly isMaxWeightInvalid: boolean; + readonly isNotHoldingFees: boolean; + readonly isTooExpensive: boolean; + readonly isTrap: boolean; + readonly asTrap: u64; + readonly isExpectationFalse: boolean; + readonly isPalletNotFound: boolean; + readonly isNameMismatch: boolean; + readonly isVersionIncompatible: boolean; + readonly isHoldingWouldOverflow: boolean; + readonly isExportError: boolean; + readonly isReanchorFailed: boolean; + readonly isNoDeal: boolean; + readonly isFeesNotMet: boolean; + readonly isLockError: boolean; + readonly isNoPermission: boolean; + readonly isUnanchored: boolean; + readonly isNotDepositable: boolean; + readonly isUnhandledXcmVersion: boolean; + readonly isWeightLimitReached: boolean; + readonly asWeightLimitReached: SpWeightsWeightV2Weight; + readonly isBarrier: boolean; + readonly isWeightNotComputable: boolean; + readonly isExceedsStackLimit: boolean; + readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'LocationFull' | 'LocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'ExpectationFalse' | 'PalletNotFound' | 'NameMismatch' | 'VersionIncompatible' | 'HoldingWouldOverflow' | 'ExportError' | 'ReanchorFailed' | 'NoDeal' | 'FeesNotMet' | 'LockError' | 'NoPermission' | 'Unanchored' | 'NotDepositable' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable' | 'ExceedsStackLimit'; +} + +/** @name StagingXcmV3TraitsOutcome */ +export interface StagingXcmV3TraitsOutcome extends Enum { + readonly isComplete: boolean; + readonly asComplete: SpWeightsWeightV2Weight; + readonly isIncomplete: boolean; + readonly asIncomplete: ITuple<[SpWeightsWeightV2Weight, StagingXcmV3TraitsError]>; + readonly isError: boolean; + readonly asError: StagingXcmV3TraitsError; + readonly type: 'Complete' | 'Incomplete' | 'Error'; +} + +/** @name StagingXcmV3WeightLimit */ +export interface StagingXcmV3WeightLimit extends Enum { + readonly isUnlimited: boolean; + readonly isLimited: boolean; + readonly asLimited: SpWeightsWeightV2Weight; + readonly type: 'Unlimited' | 'Limited'; +} + +/** @name StagingXcmV3Xcm */ +export interface StagingXcmV3Xcm extends Vec {} + +/** @name StagingXcmVersionedAssetId */ +export interface StagingXcmVersionedAssetId extends Enum { + readonly isV3: boolean; + readonly asV3: StagingXcmV3MultiassetAssetId; + readonly type: 'V3'; +} + +/** @name StagingXcmVersionedMultiAsset */ +export interface StagingXcmVersionedMultiAsset extends Enum { + readonly isV2: boolean; + readonly asV2: StagingXcmV2MultiAsset; + readonly isV3: boolean; + readonly asV3: StagingXcmV3MultiAsset; + readonly type: 'V2' | 'V3'; +} + +/** @name StagingXcmVersionedMultiAssets */ +export interface StagingXcmVersionedMultiAssets extends Enum { + readonly isV2: boolean; + readonly asV2: StagingXcmV2MultiassetMultiAssets; + readonly isV3: boolean; + readonly asV3: StagingXcmV3MultiassetMultiAssets; + readonly type: 'V2' | 'V3'; +} + +/** @name StagingXcmVersionedMultiLocation */ +export interface StagingXcmVersionedMultiLocation extends Enum { + readonly isV2: boolean; + readonly asV2: StagingXcmV2MultiLocation; + readonly isV3: boolean; + readonly asV3: StagingXcmV3MultiLocation; + readonly type: 'V2' | 'V3'; +} + +/** @name StagingXcmVersionedResponse */ +export interface StagingXcmVersionedResponse extends Enum { + readonly isV2: boolean; + readonly asV2: StagingXcmV2Response; + readonly isV3: boolean; + readonly asV3: StagingXcmV3Response; + readonly type: 'V2' | 'V3'; +} + +/** @name StagingXcmVersionedXcm */ +export interface StagingXcmVersionedXcm extends Enum { + readonly isV2: boolean; + readonly asV2: StagingXcmV2Xcm; + readonly isV3: boolean; + readonly asV3: StagingXcmV3Xcm; + readonly type: 'V2' | 'V3'; +} + +/** @name UpDataStructsAccessMode */ +export interface UpDataStructsAccessMode extends Enum { + readonly isNormal: boolean; + readonly isAllowList: boolean; + readonly type: 'Normal' | 'AllowList'; +} + +/** @name UpDataStructsCollection */ +export interface UpDataStructsCollection extends Struct { + readonly owner: AccountId32; + readonly mode: UpDataStructsCollectionMode; + readonly name: Vec; + readonly description: Vec; + readonly tokenPrefix: Bytes; + readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; + readonly limits: UpDataStructsCollectionLimits; + readonly permissions: UpDataStructsCollectionPermissions; + readonly flags: U8aFixed; +} + +/** @name UpDataStructsCollectionLimits */ +export interface UpDataStructsCollectionLimits extends Struct { + readonly accountTokenOwnershipLimit: Option; + readonly sponsoredDataSize: Option; + readonly sponsoredDataRateLimit: Option; + readonly tokenLimit: Option; + readonly sponsorTransferTimeout: Option; + readonly sponsorApproveTimeout: Option; + readonly ownerCanTransfer: Option; + readonly ownerCanDestroy: Option; + readonly transfersEnabled: Option; +} + +/** @name UpDataStructsCollectionMode */ +export interface UpDataStructsCollectionMode extends Enum { + readonly isNft: boolean; + readonly isFungible: boolean; + readonly asFungible: u8; + readonly isReFungible: boolean; + readonly type: 'Nft' | 'Fungible' | 'ReFungible'; +} + +/** @name UpDataStructsCollectionPermissions */ +export interface UpDataStructsCollectionPermissions extends Struct { + readonly access: Option; + readonly mintMode: Option; + readonly nesting: Option; +} + +/** @name UpDataStructsCollectionStats */ +export interface UpDataStructsCollectionStats extends Struct { + readonly created: u32; + readonly destroyed: u32; + readonly alive: u32; +} + +/** @name UpDataStructsCreateCollectionData */ +export interface UpDataStructsCreateCollectionData extends Struct { + readonly mode: UpDataStructsCollectionMode; + readonly access: Option; + readonly name: Vec; + readonly description: Vec; + readonly tokenPrefix: Bytes; + readonly limits: Option; + readonly permissions: Option; + readonly tokenPropertyPermissions: Vec; + readonly properties: Vec; + readonly adminList: Vec; + readonly pendingSponsor: Option; + readonly flags: U8aFixed; +} + +/** @name UpDataStructsCreateFungibleData */ +export interface UpDataStructsCreateFungibleData extends Struct { + readonly value: u128; +} + +/** @name UpDataStructsCreateItemData */ +export interface UpDataStructsCreateItemData extends Enum { + readonly isNft: boolean; + readonly asNft: UpDataStructsCreateNftData; + readonly isFungible: boolean; + readonly asFungible: UpDataStructsCreateFungibleData; + readonly isReFungible: boolean; + readonly asReFungible: UpDataStructsCreateReFungibleData; + readonly type: 'Nft' | 'Fungible' | 'ReFungible'; +} + +/** @name UpDataStructsCreateItemExData */ +export interface UpDataStructsCreateItemExData extends Enum { + readonly isNft: boolean; + readonly asNft: Vec; + readonly isFungible: boolean; + readonly asFungible: BTreeMap; + readonly isRefungibleMultipleItems: boolean; + readonly asRefungibleMultipleItems: Vec; + readonly isRefungibleMultipleOwners: boolean; + readonly asRefungibleMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners; + readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners'; +} + +/** @name UpDataStructsCreateNftData */ +export interface UpDataStructsCreateNftData extends Struct { + readonly properties: Vec; +} + +/** @name UpDataStructsCreateNftExData */ +export interface UpDataStructsCreateNftExData extends Struct { + readonly properties: Vec; + readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; +} + +/** @name UpDataStructsCreateReFungibleData */ +export interface UpDataStructsCreateReFungibleData extends Struct { + readonly pieces: u128; + readonly properties: Vec; +} + +/** @name UpDataStructsCreateRefungibleExMultipleOwners */ +export interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct { + readonly users: BTreeMap; + readonly properties: Vec; +} + +/** @name UpDataStructsCreateRefungibleExSingleOwner */ +export interface UpDataStructsCreateRefungibleExSingleOwner extends Struct { + readonly user: PalletEvmAccountBasicCrossAccountIdRepr; + readonly pieces: u128; + readonly properties: Vec; +} + +/** @name UpDataStructsNestingPermissions */ +export interface UpDataStructsNestingPermissions extends Struct { + readonly tokenOwner: bool; + readonly collectionAdmin: bool; + readonly restricted: Option; +} + +/** @name UpDataStructsOwnerRestrictedSet */ +export interface UpDataStructsOwnerRestrictedSet extends BTreeSet {} + +/** @name UpDataStructsProperties */ +export interface UpDataStructsProperties extends Struct { + readonly map: UpDataStructsPropertiesMapBoundedVec; + readonly consumedSpace: u32; + readonly reserved: u32; +} + +/** @name UpDataStructsPropertiesMapBoundedVec */ +export interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap {} + +/** @name UpDataStructsPropertiesMapPropertyPermission */ +export interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap {} + +/** @name UpDataStructsProperty */ +export interface UpDataStructsProperty extends Struct { + readonly key: Bytes; + readonly value: Bytes; +} + +/** @name UpDataStructsPropertyKeyPermission */ +export interface UpDataStructsPropertyKeyPermission extends Struct { + readonly key: Bytes; + readonly permission: UpDataStructsPropertyPermission; +} + +/** @name UpDataStructsPropertyPermission */ +export interface UpDataStructsPropertyPermission extends Struct { + readonly mutable: bool; + readonly collectionAdmin: bool; + readonly tokenOwner: bool; +} + +/** @name UpDataStructsPropertyScope */ +export interface UpDataStructsPropertyScope extends Enum { + readonly isNone: boolean; + readonly isRmrk: boolean; + readonly type: 'None' | 'Rmrk'; +} + +/** @name UpDataStructsRpcCollection */ +export interface UpDataStructsRpcCollection extends Struct { + readonly owner: AccountId32; + readonly mode: UpDataStructsCollectionMode; + readonly name: Vec; + readonly description: Vec; + readonly tokenPrefix: Bytes; + readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; + readonly limits: UpDataStructsCollectionLimits; + readonly permissions: UpDataStructsCollectionPermissions; + readonly tokenPropertyPermissions: Vec; + readonly properties: Vec; + readonly readOnly: bool; + readonly flags: UpDataStructsRpcCollectionFlags; +} + +/** @name UpDataStructsRpcCollectionFlags */ +export interface UpDataStructsRpcCollectionFlags extends Struct { + readonly foreign: bool; + readonly erc721metadata: bool; +} + +/** @name UpDataStructsSponsoringRateLimit */ +export interface UpDataStructsSponsoringRateLimit extends Enum { + readonly isSponsoringDisabled: boolean; + readonly isBlocks: boolean; + readonly asBlocks: u32; + readonly type: 'SponsoringDisabled' | 'Blocks'; +} + +/** @name UpDataStructsSponsorshipStateAccountId32 */ +export interface UpDataStructsSponsorshipStateAccountId32 extends Enum { + readonly isDisabled: boolean; + readonly isUnconfirmed: boolean; + readonly asUnconfirmed: AccountId32; + readonly isConfirmed: boolean; + readonly asConfirmed: AccountId32; + readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; +} + +/** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr */ +export interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum { + readonly isDisabled: boolean; + readonly isUnconfirmed: boolean; + readonly asUnconfirmed: PalletEvmAccountBasicCrossAccountIdRepr; + readonly isConfirmed: boolean; + readonly asConfirmed: PalletEvmAccountBasicCrossAccountIdRepr; + readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; +} + +/** @name UpDataStructsTokenChild */ +export interface UpDataStructsTokenChild extends Struct { + readonly token: u32; + readonly collection: u32; +} + +/** @name UpDataStructsTokenData */ +export interface UpDataStructsTokenData extends Struct { + readonly properties: Vec; + readonly owner: Option; + readonly pieces: u128; +} + +/** @name UpPovEstimateRpcPovInfo */ +export interface UpPovEstimateRpcPovInfo extends Struct { + readonly proofSize: u64; + readonly compactProofSize: u64; + readonly compressedProofSize: u64; + readonly results: Vec, SpRuntimeTransactionValidityTransactionValidityError>>; + readonly keyValues: Vec; +} + +/** @name UpPovEstimateRpcTrieKeyValue */ +export interface UpPovEstimateRpcTrieKeyValue extends Struct { + readonly key: Bytes; + readonly value: Bytes; +} + +export type PHANTOM_DEFAULT = 'default'; --- /dev/null +++ b/js-packages/types/definitions.ts @@ -0,0 +1,20 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +export {default as unique} from './unique/definitions.js'; +export {default as appPromotion} from './appPromotion/definitions.js'; +export {default as povinfo} from './povinfo/definitions.js'; +export {default as default} from './default/definitions.js'; --- /dev/null +++ b/js-packages/types/index.ts @@ -0,0 +1,4 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +export * from './types.js'; --- /dev/null +++ b/js-packages/types/lookup.ts @@ -0,0 +1,5165 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +/* eslint-disable sort-keys */ + +export default { + /** + * Lookup3: frame_system::AccountInfo> + **/ + FrameSystemAccountInfo: { + nonce: 'u32', + consumers: 'u32', + providers: 'u32', + sufficients: 'u32', + data: 'PalletBalancesAccountData' + }, + /** + * Lookup5: pallet_balances::types::AccountData + **/ + PalletBalancesAccountData: { + free: 'u128', + reserved: 'u128', + frozen: 'u128', + flags: 'u128' + }, + /** + * Lookup8: frame_support::dispatch::PerDispatchClass + **/ + FrameSupportDispatchPerDispatchClassWeight: { + normal: 'SpWeightsWeightV2Weight', + operational: 'SpWeightsWeightV2Weight', + mandatory: 'SpWeightsWeightV2Weight' + }, + /** + * Lookup9: sp_weights::weight_v2::Weight + **/ + SpWeightsWeightV2Weight: { + refTime: 'Compact', + proofSize: 'Compact' + }, + /** + * Lookup14: sp_runtime::generic::digest::Digest + **/ + SpRuntimeDigest: { + logs: 'Vec' + }, + /** + * Lookup16: sp_runtime::generic::digest::DigestItem + **/ + SpRuntimeDigestDigestItem: { + _enum: { + Other: 'Bytes', + __Unused1: 'Null', + __Unused2: 'Null', + __Unused3: 'Null', + Consensus: '([u8;4],Bytes)', + Seal: '([u8;4],Bytes)', + PreRuntime: '([u8;4],Bytes)', + __Unused7: 'Null', + RuntimeEnvironmentUpdated: 'Null' + } + }, + /** + * Lookup19: frame_system::EventRecord + **/ + FrameSystemEventRecord: { + phase: 'FrameSystemPhase', + event: 'Event', + topics: 'Vec' + }, + /** + * Lookup21: frame_system::pallet::Event + **/ + FrameSystemEvent: { + _enum: { + ExtrinsicSuccess: { + dispatchInfo: 'FrameSupportDispatchDispatchInfo', + }, + ExtrinsicFailed: { + dispatchError: 'SpRuntimeDispatchError', + dispatchInfo: 'FrameSupportDispatchDispatchInfo', + }, + CodeUpdated: 'Null', + NewAccount: { + account: 'AccountId32', + }, + KilledAccount: { + account: 'AccountId32', + }, + Remarked: { + _alias: { + hash_: 'hash', + }, + sender: 'AccountId32', + hash_: 'H256' + } + } + }, + /** + * Lookup22: frame_support::dispatch::DispatchInfo + **/ + FrameSupportDispatchDispatchInfo: { + weight: 'SpWeightsWeightV2Weight', + class: 'FrameSupportDispatchDispatchClass', + paysFee: 'FrameSupportDispatchPays' + }, + /** + * Lookup23: frame_support::dispatch::DispatchClass + **/ + FrameSupportDispatchDispatchClass: { + _enum: ['Normal', 'Operational', 'Mandatory'] + }, + /** + * Lookup24: frame_support::dispatch::Pays + **/ + FrameSupportDispatchPays: { + _enum: ['Yes', 'No'] + }, + /** + * Lookup25: sp_runtime::DispatchError + **/ + SpRuntimeDispatchError: { + _enum: { + Other: 'Null', + CannotLookup: 'Null', + BadOrigin: 'Null', + Module: 'SpRuntimeModuleError', + ConsumerRemaining: 'Null', + NoProviders: 'Null', + TooManyConsumers: 'Null', + Token: 'SpRuntimeTokenError', + Arithmetic: 'SpArithmeticArithmeticError', + Transactional: 'SpRuntimeTransactionalError', + Exhausted: 'Null', + Corruption: 'Null', + Unavailable: 'Null', + RootNotAllowed: 'Null' + } + }, + /** + * Lookup26: sp_runtime::ModuleError + **/ + SpRuntimeModuleError: { + index: 'u8', + error: '[u8;4]' + }, + /** + * Lookup27: sp_runtime::TokenError + **/ + SpRuntimeTokenError: { + _enum: ['FundsUnavailable', 'OnlyProvider', 'BelowMinimum', 'CannotCreate', 'UnknownAsset', 'Frozen', 'Unsupported', 'CannotCreateHold', 'NotExpendable', 'Blocked'] + }, + /** + * Lookup28: sp_arithmetic::ArithmeticError + **/ + SpArithmeticArithmeticError: { + _enum: ['Underflow', 'Overflow', 'DivisionByZero'] + }, + /** + * Lookup29: sp_runtime::TransactionalError + **/ + SpRuntimeTransactionalError: { + _enum: ['LimitReached', 'NoLayer'] + }, + /** + * Lookup30: pallet_state_trie_migration::pallet::Event + **/ + PalletStateTrieMigrationEvent: { + _enum: { + Migrated: { + top: 'u32', + child: 'u32', + compute: 'PalletStateTrieMigrationMigrationCompute', + }, + Slashed: { + who: 'AccountId32', + amount: 'u128', + }, + AutoMigrationFinished: 'Null', + Halted: { + error: 'PalletStateTrieMigrationError' + } + } + }, + /** + * Lookup31: pallet_state_trie_migration::pallet::MigrationCompute + **/ + PalletStateTrieMigrationMigrationCompute: { + _enum: ['Signed', 'Auto'] + }, + /** + * Lookup32: pallet_state_trie_migration::pallet::Error + **/ + PalletStateTrieMigrationError: { + _enum: ['MaxSignedLimits', 'KeyTooLong', 'NotEnoughFunds', 'BadWitness', 'SignedMigrationNotAllowed', 'BadChildRoot'] + }, + /** + * Lookup33: cumulus_pallet_parachain_system::pallet::Event + **/ + CumulusPalletParachainSystemEvent: { + _enum: { + ValidationFunctionStored: 'Null', + ValidationFunctionApplied: { + relayChainBlockNum: 'u32', + }, + ValidationFunctionDiscarded: 'Null', + UpgradeAuthorized: { + codeHash: 'H256', + }, + DownwardMessagesReceived: { + count: 'u32', + }, + DownwardMessagesProcessed: { + weightUsed: 'SpWeightsWeightV2Weight', + dmqHead: 'H256', + }, + UpwardMessageSent: { + messageHash: 'Option<[u8;32]>' + } + } + }, + /** + * Lookup35: pallet_collator_selection::pallet::Event + **/ + PalletCollatorSelectionEvent: { + _enum: { + InvulnerableAdded: { + invulnerable: 'AccountId32', + }, + InvulnerableRemoved: { + invulnerable: 'AccountId32', + }, + LicenseObtained: { + accountId: 'AccountId32', + deposit: 'u128', + }, + LicenseReleased: { + accountId: 'AccountId32', + depositReturned: 'u128', + }, + CandidateAdded: { + accountId: 'AccountId32', + }, + CandidateRemoved: { + accountId: 'AccountId32' + } + } + }, + /** + * Lookup36: pallet_session::pallet::Event + **/ + PalletSessionEvent: { + _enum: { + NewSession: { + sessionIndex: 'u32' + } + } + }, + /** + * Lookup37: pallet_balances::pallet::Event + **/ + PalletBalancesEvent: { + _enum: { + Endowed: { + account: 'AccountId32', + freeBalance: 'u128', + }, + DustLost: { + account: 'AccountId32', + amount: 'u128', + }, + Transfer: { + from: 'AccountId32', + to: 'AccountId32', + amount: 'u128', + }, + BalanceSet: { + who: 'AccountId32', + free: 'u128', + }, + Reserved: { + who: 'AccountId32', + amount: 'u128', + }, + Unreserved: { + who: 'AccountId32', + amount: 'u128', + }, + ReserveRepatriated: { + from: 'AccountId32', + to: 'AccountId32', + amount: 'u128', + destinationStatus: 'FrameSupportTokensMiscBalanceStatus', + }, + Deposit: { + who: 'AccountId32', + amount: 'u128', + }, + Withdraw: { + who: 'AccountId32', + amount: 'u128', + }, + Slashed: { + who: 'AccountId32', + amount: 'u128', + }, + Minted: { + who: 'AccountId32', + amount: 'u128', + }, + Burned: { + who: 'AccountId32', + amount: 'u128', + }, + Suspended: { + who: 'AccountId32', + amount: 'u128', + }, + Restored: { + who: 'AccountId32', + amount: 'u128', + }, + Upgraded: { + who: 'AccountId32', + }, + Issued: { + amount: 'u128', + }, + Rescinded: { + amount: 'u128', + }, + Locked: { + who: 'AccountId32', + amount: 'u128', + }, + Unlocked: { + who: 'AccountId32', + amount: 'u128', + }, + Frozen: { + who: 'AccountId32', + amount: 'u128', + }, + Thawed: { + who: 'AccountId32', + amount: 'u128' + } + } + }, + /** + * Lookup38: frame_support::traits::tokens::misc::BalanceStatus + **/ + FrameSupportTokensMiscBalanceStatus: { + _enum: ['Free', 'Reserved'] + }, + /** + * Lookup39: pallet_transaction_payment::pallet::Event + **/ + PalletTransactionPaymentEvent: { + _enum: { + TransactionFeePaid: { + who: 'AccountId32', + actualFee: 'u128', + tip: 'u128' + } + } + }, + /** + * Lookup40: pallet_treasury::pallet::Event + **/ + PalletTreasuryEvent: { + _enum: { + Proposed: { + proposalIndex: 'u32', + }, + Spending: { + budgetRemaining: 'u128', + }, + Awarded: { + proposalIndex: 'u32', + award: 'u128', + account: 'AccountId32', + }, + Rejected: { + proposalIndex: 'u32', + slashed: 'u128', + }, + Burnt: { + burntFunds: 'u128', + }, + Rollover: { + rolloverBalance: 'u128', + }, + Deposit: { + value: 'u128', + }, + SpendApproved: { + proposalIndex: 'u32', + amount: 'u128', + beneficiary: 'AccountId32', + }, + UpdatedInactive: { + reactivated: 'u128', + deactivated: 'u128' + } + } + }, + /** + * Lookup41: pallet_sudo::pallet::Event + **/ + PalletSudoEvent: { + _enum: { + Sudid: { + sudoResult: 'Result', + }, + KeyChanged: { + oldSudoer: 'Option', + }, + SudoAsDone: { + sudoResult: 'Result' + } + } + }, + /** + * Lookup45: orml_vesting::module::Event + **/ + OrmlVestingModuleEvent: { + _enum: { + VestingScheduleAdded: { + from: 'AccountId32', + to: 'AccountId32', + vestingSchedule: 'OrmlVestingVestingSchedule', + }, + Claimed: { + who: 'AccountId32', + amount: 'u128', + }, + VestingSchedulesUpdated: { + who: 'AccountId32' + } + } + }, + /** + * Lookup46: orml_vesting::VestingSchedule + **/ + OrmlVestingVestingSchedule: { + start: 'u32', + period: 'u32', + periodCount: 'u32', + perPeriod: 'Compact' + }, + /** + * Lookup48: orml_xtokens::module::Event + **/ + OrmlXtokensModuleEvent: { + _enum: { + TransferredMultiAssets: { + sender: 'AccountId32', + assets: 'StagingXcmV3MultiassetMultiAssets', + fee: 'StagingXcmV3MultiAsset', + dest: 'StagingXcmV3MultiLocation' + } + } + }, + /** + * Lookup49: staging_xcm::v3::multiasset::MultiAssets + **/ + StagingXcmV3MultiassetMultiAssets: 'Vec', + /** + * Lookup51: staging_xcm::v3::multiasset::MultiAsset + **/ + StagingXcmV3MultiAsset: { + id: 'StagingXcmV3MultiassetAssetId', + fun: 'StagingXcmV3MultiassetFungibility' + }, + /** + * Lookup52: staging_xcm::v3::multiasset::AssetId + **/ + StagingXcmV3MultiassetAssetId: { + _enum: { + Concrete: 'StagingXcmV3MultiLocation', + Abstract: '[u8;32]' + } + }, + /** + * Lookup53: staging_xcm::v3::multilocation::MultiLocation + **/ + StagingXcmV3MultiLocation: { + parents: 'u8', + interior: 'StagingXcmV3Junctions' + }, + /** + * Lookup54: staging_xcm::v3::junctions::Junctions + **/ + StagingXcmV3Junctions: { + _enum: { + Here: 'Null', + X1: 'StagingXcmV3Junction', + X2: '(StagingXcmV3Junction,StagingXcmV3Junction)', + X3: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)', + X4: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)', + X5: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)', + X6: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)', + X7: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)', + X8: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)' + } + }, + /** + * Lookup55: staging_xcm::v3::junction::Junction + **/ + StagingXcmV3Junction: { + _enum: { + Parachain: 'Compact', + AccountId32: { + network: 'Option', + id: '[u8;32]', + }, + AccountIndex64: { + network: 'Option', + index: 'Compact', + }, + AccountKey20: { + network: 'Option', + key: '[u8;20]', + }, + PalletInstance: 'u8', + GeneralIndex: 'Compact', + GeneralKey: { + length: 'u8', + data: '[u8;32]', + }, + OnlyChild: 'Null', + Plurality: { + id: 'StagingXcmV3JunctionBodyId', + part: 'StagingXcmV3JunctionBodyPart', + }, + GlobalConsensus: 'StagingXcmV3JunctionNetworkId' + } + }, + /** + * Lookup58: staging_xcm::v3::junction::NetworkId + **/ + StagingXcmV3JunctionNetworkId: { + _enum: { + ByGenesis: '[u8;32]', + ByFork: { + blockNumber: 'u64', + blockHash: '[u8;32]', + }, + Polkadot: 'Null', + Kusama: 'Null', + Westend: 'Null', + Rococo: 'Null', + Wococo: 'Null', + Ethereum: { + chainId: 'Compact', + }, + BitcoinCore: 'Null', + BitcoinCash: 'Null' + } + }, + /** + * Lookup60: staging_xcm::v3::junction::BodyId + **/ + StagingXcmV3JunctionBodyId: { + _enum: { + Unit: 'Null', + Moniker: '[u8;4]', + Index: 'Compact', + Executive: 'Null', + Technical: 'Null', + Legislative: 'Null', + Judicial: 'Null', + Defense: 'Null', + Administration: 'Null', + Treasury: 'Null' + } + }, + /** + * Lookup61: staging_xcm::v3::junction::BodyPart + **/ + StagingXcmV3JunctionBodyPart: { + _enum: { + Voice: 'Null', + Members: { + count: 'Compact', + }, + Fraction: { + nom: 'Compact', + denom: 'Compact', + }, + AtLeastProportion: { + nom: 'Compact', + denom: 'Compact', + }, + MoreThanProportion: { + nom: 'Compact', + denom: 'Compact' + } + } + }, + /** + * Lookup62: staging_xcm::v3::multiasset::Fungibility + **/ + StagingXcmV3MultiassetFungibility: { + _enum: { + Fungible: 'Compact', + NonFungible: 'StagingXcmV3MultiassetAssetInstance' + } + }, + /** + * Lookup63: staging_xcm::v3::multiasset::AssetInstance + **/ + StagingXcmV3MultiassetAssetInstance: { + _enum: { + Undefined: 'Null', + Index: 'Compact', + Array4: '[u8;4]', + Array8: '[u8;8]', + Array16: '[u8;16]', + Array32: '[u8;32]' + } + }, + /** + * Lookup66: orml_tokens::module::Event + **/ + OrmlTokensModuleEvent: { + _enum: { + Endowed: { + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + amount: 'u128', + }, + DustLost: { + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + amount: 'u128', + }, + Transfer: { + currencyId: 'PalletForeignAssetsAssetId', + from: 'AccountId32', + to: 'AccountId32', + amount: 'u128', + }, + Reserved: { + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + amount: 'u128', + }, + Unreserved: { + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + amount: 'u128', + }, + ReserveRepatriated: { + currencyId: 'PalletForeignAssetsAssetId', + from: 'AccountId32', + to: 'AccountId32', + amount: 'u128', + status: 'FrameSupportTokensMiscBalanceStatus', + }, + BalanceSet: { + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + free: 'u128', + reserved: 'u128', + }, + TotalIssuanceSet: { + currencyId: 'PalletForeignAssetsAssetId', + amount: 'u128', + }, + Withdrawn: { + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + amount: 'u128', + }, + Slashed: { + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + freeAmount: 'u128', + reservedAmount: 'u128', + }, + Deposited: { + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + amount: 'u128', + }, + LockSet: { + lockId: '[u8;8]', + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + amount: 'u128', + }, + LockRemoved: { + lockId: '[u8;8]', + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + }, + Locked: { + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + amount: 'u128', + }, + Unlocked: { + currencyId: 'PalletForeignAssetsAssetId', + who: 'AccountId32', + amount: 'u128', + }, + Issued: { + currencyId: 'PalletForeignAssetsAssetId', + amount: 'u128', + }, + Rescinded: { + currencyId: 'PalletForeignAssetsAssetId', + amount: 'u128' + } + } + }, + /** + * Lookup67: pallet_foreign_assets::AssetId + **/ + PalletForeignAssetsAssetId: { + _enum: { + ForeignAssetId: 'u32', + NativeAssetId: 'PalletForeignAssetsNativeCurrency' + } + }, + /** + * Lookup68: pallet_foreign_assets::NativeCurrency + **/ + PalletForeignAssetsNativeCurrency: { + _enum: ['Here', 'Parent'] + }, + /** + * Lookup69: pallet_identity::pallet::Event + **/ + PalletIdentityEvent: { + _enum: { + IdentitySet: { + who: 'AccountId32', + }, + IdentityCleared: { + who: 'AccountId32', + deposit: 'u128', + }, + IdentityKilled: { + who: 'AccountId32', + deposit: 'u128', + }, + IdentitiesInserted: { + amount: 'u32', + }, + IdentitiesRemoved: { + amount: 'u32', + }, + JudgementRequested: { + who: 'AccountId32', + registrarIndex: 'u32', + }, + JudgementUnrequested: { + who: 'AccountId32', + registrarIndex: 'u32', + }, + JudgementGiven: { + target: 'AccountId32', + registrarIndex: 'u32', + }, + RegistrarAdded: { + registrarIndex: 'u32', + }, + SubIdentityAdded: { + sub: 'AccountId32', + main: 'AccountId32', + deposit: 'u128', + }, + SubIdentityRemoved: { + sub: 'AccountId32', + main: 'AccountId32', + deposit: 'u128', + }, + SubIdentityRevoked: { + sub: 'AccountId32', + main: 'AccountId32', + deposit: 'u128', + }, + SubIdentitiesInserted: { + amount: 'u32' + } + } + }, + /** + * Lookup70: pallet_preimage::pallet::Event + **/ + PalletPreimageEvent: { + _enum: { + Noted: { + _alias: { + hash_: 'hash', + }, + hash_: 'H256', + }, + Requested: { + _alias: { + hash_: 'hash', + }, + hash_: 'H256', + }, + Cleared: { + _alias: { + hash_: 'hash', + }, + hash_: 'H256' + } + } + }, + /** + * Lookup71: pallet_democracy::pallet::Event + **/ + PalletDemocracyEvent: { + _enum: { + Proposed: { + proposalIndex: 'u32', + deposit: 'u128', + }, + Tabled: { + proposalIndex: 'u32', + deposit: 'u128', + }, + ExternalTabled: 'Null', + Started: { + refIndex: 'u32', + threshold: 'PalletDemocracyVoteThreshold', + }, + Passed: { + refIndex: 'u32', + }, + NotPassed: { + refIndex: 'u32', + }, + Cancelled: { + refIndex: 'u32', + }, + Delegated: { + who: 'AccountId32', + target: 'AccountId32', + }, + Undelegated: { + account: 'AccountId32', + }, + Vetoed: { + who: 'AccountId32', + proposalHash: 'H256', + until: 'u32', + }, + Blacklisted: { + proposalHash: 'H256', + }, + Voted: { + voter: 'AccountId32', + refIndex: 'u32', + vote: 'PalletDemocracyVoteAccountVote', + }, + Seconded: { + seconder: 'AccountId32', + propIndex: 'u32', + }, + ProposalCanceled: { + propIndex: 'u32', + }, + MetadataSet: { + _alias: { + hash_: 'hash', + }, + owner: 'PalletDemocracyMetadataOwner', + hash_: 'H256', + }, + MetadataCleared: { + _alias: { + hash_: 'hash', + }, + owner: 'PalletDemocracyMetadataOwner', + hash_: 'H256', + }, + MetadataTransferred: { + _alias: { + hash_: 'hash', + }, + prevOwner: 'PalletDemocracyMetadataOwner', + owner: 'PalletDemocracyMetadataOwner', + hash_: 'H256' + } + } + }, + /** + * Lookup72: pallet_democracy::vote_threshold::VoteThreshold + **/ + PalletDemocracyVoteThreshold: { + _enum: ['SuperMajorityApprove', 'SuperMajorityAgainst', 'SimpleMajority'] + }, + /** + * Lookup73: pallet_democracy::vote::AccountVote + **/ + PalletDemocracyVoteAccountVote: { + _enum: { + Standard: { + vote: 'Vote', + balance: 'u128', + }, + Split: { + aye: 'u128', + nay: 'u128' + } + } + }, + /** + * Lookup75: pallet_democracy::types::MetadataOwner + **/ + PalletDemocracyMetadataOwner: { + _enum: { + External: 'Null', + Proposal: 'u32', + Referendum: 'u32' + } + }, + /** + * Lookup76: pallet_collective::pallet::Event + **/ + PalletCollectiveEvent: { + _enum: { + Proposed: { + account: 'AccountId32', + proposalIndex: 'u32', + proposalHash: 'H256', + threshold: 'u32', + }, + Voted: { + account: 'AccountId32', + proposalHash: 'H256', + voted: 'bool', + yes: 'u32', + no: 'u32', + }, + Approved: { + proposalHash: 'H256', + }, + Disapproved: { + proposalHash: 'H256', + }, + Executed: { + proposalHash: 'H256', + result: 'Result', + }, + MemberExecuted: { + proposalHash: 'H256', + result: 'Result', + }, + Closed: { + proposalHash: 'H256', + yes: 'u32', + no: 'u32' + } + } + }, + /** + * Lookup79: pallet_membership::pallet::Event + **/ + PalletMembershipEvent: { + _enum: ['MemberAdded', 'MemberRemoved', 'MembersSwapped', 'MembersReset', 'KeyChanged', 'Dummy'] + }, + /** + * Lookup81: pallet_ranked_collective::pallet::Event + **/ + PalletRankedCollectiveEvent: { + _enum: { + MemberAdded: { + who: 'AccountId32', + }, + RankChanged: { + who: 'AccountId32', + rank: 'u16', + }, + MemberRemoved: { + who: 'AccountId32', + rank: 'u16', + }, + Voted: { + who: 'AccountId32', + poll: 'u32', + vote: 'PalletRankedCollectiveVoteRecord', + tally: 'PalletRankedCollectiveTally' + } + } + }, + /** + * Lookup83: pallet_ranked_collective::VoteRecord + **/ + PalletRankedCollectiveVoteRecord: { + _enum: { + Aye: 'u32', + Nay: 'u32' + } + }, + /** + * Lookup84: pallet_ranked_collective::Tally + **/ + PalletRankedCollectiveTally: { + bareAyes: 'u32', + ayes: 'u32', + nays: 'u32' + }, + /** + * Lookup85: pallet_referenda::pallet::Event + **/ + PalletReferendaEvent: { + _enum: { + Submitted: { + index: 'u32', + track: 'u16', + proposal: 'FrameSupportPreimagesBounded', + }, + DecisionDepositPlaced: { + index: 'u32', + who: 'AccountId32', + amount: 'u128', + }, + DecisionDepositRefunded: { + index: 'u32', + who: 'AccountId32', + amount: 'u128', + }, + DepositSlashed: { + who: 'AccountId32', + amount: 'u128', + }, + DecisionStarted: { + index: 'u32', + track: 'u16', + proposal: 'FrameSupportPreimagesBounded', + tally: 'PalletRankedCollectiveTally', + }, + ConfirmStarted: { + index: 'u32', + }, + ConfirmAborted: { + index: 'u32', + }, + Confirmed: { + index: 'u32', + tally: 'PalletRankedCollectiveTally', + }, + Approved: { + index: 'u32', + }, + Rejected: { + index: 'u32', + tally: 'PalletRankedCollectiveTally', + }, + TimedOut: { + index: 'u32', + tally: 'PalletRankedCollectiveTally', + }, + Cancelled: { + index: 'u32', + tally: 'PalletRankedCollectiveTally', + }, + Killed: { + index: 'u32', + tally: 'PalletRankedCollectiveTally', + }, + SubmissionDepositRefunded: { + index: 'u32', + who: 'AccountId32', + amount: 'u128', + }, + MetadataSet: { + _alias: { + hash_: 'hash', + }, + index: 'u32', + hash_: 'H256', + }, + MetadataCleared: { + _alias: { + hash_: 'hash', + }, + index: 'u32', + hash_: 'H256' + } + } + }, + /** + * Lookup86: frame_support::traits::preimages::Bounded + **/ + FrameSupportPreimagesBounded: { + _enum: { + Legacy: { + _alias: { + hash_: 'hash', + }, + hash_: 'H256', + }, + Inline: 'Bytes', + Lookup: { + _alias: { + hash_: 'hash', + }, + hash_: 'H256', + len: 'u32' + } + } + }, + /** + * Lookup88: frame_system::pallet::Call + **/ + FrameSystemCall: { + _enum: { + remark: { + remark: 'Bytes', + }, + set_heap_pages: { + pages: 'u64', + }, + set_code: { + code: 'Bytes', + }, + set_code_without_checks: { + code: 'Bytes', + }, + set_storage: { + items: 'Vec<(Bytes,Bytes)>', + }, + kill_storage: { + _alias: { + keys_: 'keys', + }, + keys_: 'Vec', + }, + kill_prefix: { + prefix: 'Bytes', + subkeys: 'u32', + }, + remark_with_event: { + remark: 'Bytes' + } + } + }, + /** + * Lookup92: pallet_state_trie_migration::pallet::Call + **/ + PalletStateTrieMigrationCall: { + _enum: { + control_auto_migration: { + maybeConfig: 'Option', + }, + continue_migrate: { + limits: 'PalletStateTrieMigrationMigrationLimits', + realSizeUpper: 'u32', + witnessTask: 'PalletStateTrieMigrationMigrationTask', + }, + migrate_custom_top: { + _alias: { + keys_: 'keys', + }, + keys_: 'Vec', + witnessSize: 'u32', + }, + migrate_custom_child: { + root: 'Bytes', + childKeys: 'Vec', + totalSize: 'u32', + }, + set_signed_max_limits: { + limits: 'PalletStateTrieMigrationMigrationLimits', + }, + force_set_progress: { + progressTop: 'PalletStateTrieMigrationProgress', + progressChild: 'PalletStateTrieMigrationProgress' + } + } + }, + /** + * Lookup94: pallet_state_trie_migration::pallet::MigrationLimits + **/ + PalletStateTrieMigrationMigrationLimits: { + _alias: { + size_: 'size' + }, + size_: 'u32', + item: 'u32' + }, + /** + * Lookup95: pallet_state_trie_migration::pallet::MigrationTask + **/ + PalletStateTrieMigrationMigrationTask: { + _alias: { + size_: 'size' + }, + progressTop: 'PalletStateTrieMigrationProgress', + progressChild: 'PalletStateTrieMigrationProgress', + size_: 'u32', + topItems: 'u32', + childItems: 'u32' + }, + /** + * Lookup96: pallet_state_trie_migration::pallet::Progress + **/ + PalletStateTrieMigrationProgress: { + _enum: { + ToStart: 'Null', + LastKey: 'Bytes', + Complete: 'Null' + } + }, + /** + * Lookup98: cumulus_pallet_parachain_system::pallet::Call + **/ + CumulusPalletParachainSystemCall: { + _enum: { + set_validation_data: { + data: 'CumulusPrimitivesParachainInherentParachainInherentData', + }, + sudo_send_upward_message: { + message: 'Bytes', + }, + authorize_upgrade: { + codeHash: 'H256', + checkVersion: 'bool', + }, + enact_authorized_upgrade: { + code: 'Bytes' + } + } + }, + /** + * Lookup99: cumulus_primitives_parachain_inherent::ParachainInherentData + **/ + CumulusPrimitivesParachainInherentParachainInherentData: { + validationData: 'PolkadotPrimitivesV5PersistedValidationData', + relayChainState: 'SpTrieStorageProof', + downwardMessages: 'Vec', + horizontalMessages: 'BTreeMap>' + }, + /** + * Lookup100: polkadot_primitives::v5::PersistedValidationData + **/ + PolkadotPrimitivesV5PersistedValidationData: { + parentHead: 'Bytes', + relayParentNumber: 'u32', + relayParentStorageRoot: 'H256', + maxPovSize: 'u32' + }, + /** + * Lookup102: sp_trie::storage_proof::StorageProof + **/ + SpTrieStorageProof: { + trieNodes: 'BTreeSet' + }, + /** + * Lookup105: polkadot_core_primitives::InboundDownwardMessage + **/ + PolkadotCorePrimitivesInboundDownwardMessage: { + sentAt: 'u32', + msg: 'Bytes' + }, + /** + * Lookup109: polkadot_core_primitives::InboundHrmpMessage + **/ + PolkadotCorePrimitivesInboundHrmpMessage: { + sentAt: 'u32', + data: 'Bytes' + }, + /** + * Lookup112: parachain_info::pallet::Call + **/ + ParachainInfoCall: 'Null', + /** + * Lookup113: pallet_collator_selection::pallet::Call + **/ + PalletCollatorSelectionCall: { + _enum: { + add_invulnerable: { + _alias: { + new_: 'new', + }, + new_: 'AccountId32', + }, + remove_invulnerable: { + who: 'AccountId32', + }, + get_license: 'Null', + onboard: 'Null', + offboard: 'Null', + release_license: 'Null', + force_release_license: { + who: 'AccountId32' + } + } + }, + /** + * Lookup114: pallet_session::pallet::Call + **/ + PalletSessionCall: { + _enum: { + set_keys: { + _alias: { + keys_: 'keys', + }, + keys_: 'OpalRuntimeRuntimeCommonSessionKeys', + proof: 'Bytes', + }, + purge_keys: 'Null' + } + }, + /** + * Lookup115: opal_runtime::runtime_common::SessionKeys + **/ + OpalRuntimeRuntimeCommonSessionKeys: { + aura: 'SpConsensusAuraSr25519AppSr25519Public' + }, + /** + * Lookup116: sp_consensus_aura::sr25519::app_sr25519::Public + **/ + SpConsensusAuraSr25519AppSr25519Public: 'SpCoreSr25519Public', + /** + * Lookup117: sp_core::sr25519::Public + **/ + SpCoreSr25519Public: '[u8;32]', + /** + * Lookup118: pallet_balances::pallet::Call + **/ + PalletBalancesCall: { + _enum: { + transfer_allow_death: { + dest: 'MultiAddress', + value: 'Compact', + }, + set_balance_deprecated: { + who: 'MultiAddress', + newFree: 'Compact', + oldReserved: 'Compact', + }, + force_transfer: { + source: 'MultiAddress', + dest: 'MultiAddress', + value: 'Compact', + }, + transfer_keep_alive: { + dest: 'MultiAddress', + value: 'Compact', + }, + transfer_all: { + dest: 'MultiAddress', + keepAlive: 'bool', + }, + force_unreserve: { + who: 'MultiAddress', + amount: 'u128', + }, + upgrade_accounts: { + who: 'Vec', + }, + transfer: { + dest: 'MultiAddress', + value: 'Compact', + }, + force_set_balance: { + who: 'MultiAddress', + newFree: 'Compact' + } + } + }, + /** + * Lookup122: pallet_timestamp::pallet::Call + **/ + PalletTimestampCall: { + _enum: { + set: { + now: 'Compact' + } + } + }, + /** + * Lookup123: pallet_treasury::pallet::Call + **/ + PalletTreasuryCall: { + _enum: { + propose_spend: { + value: 'Compact', + beneficiary: 'MultiAddress', + }, + reject_proposal: { + proposalId: 'Compact', + }, + approve_proposal: { + proposalId: 'Compact', + }, + spend: { + amount: 'Compact', + beneficiary: 'MultiAddress', + }, + remove_approval: { + proposalId: 'Compact' + } + } + }, + /** + * Lookup124: pallet_sudo::pallet::Call + **/ + PalletSudoCall: { + _enum: { + sudo: { + call: 'Call', + }, + sudo_unchecked_weight: { + call: 'Call', + weight: 'SpWeightsWeightV2Weight', + }, + set_key: { + _alias: { + new_: 'new', + }, + new_: 'MultiAddress', + }, + sudo_as: { + who: 'MultiAddress', + call: 'Call' + } + } + }, + /** + * Lookup125: orml_vesting::module::Call + **/ + OrmlVestingModuleCall: { + _enum: { + claim: 'Null', + vested_transfer: { + dest: 'MultiAddress', + schedule: 'OrmlVestingVestingSchedule', + }, + update_vesting_schedules: { + who: 'MultiAddress', + vestingSchedules: 'Vec', + }, + claim_for: { + dest: 'MultiAddress' + } + } + }, + /** + * Lookup127: orml_xtokens::module::Call + **/ + OrmlXtokensModuleCall: { + _enum: { + transfer: { + currencyId: 'PalletForeignAssetsAssetId', + amount: 'u128', + dest: 'StagingXcmVersionedMultiLocation', + destWeightLimit: 'StagingXcmV3WeightLimit', + }, + transfer_multiasset: { + asset: 'StagingXcmVersionedMultiAsset', + dest: 'StagingXcmVersionedMultiLocation', + destWeightLimit: 'StagingXcmV3WeightLimit', + }, + transfer_with_fee: { + currencyId: 'PalletForeignAssetsAssetId', + amount: 'u128', + fee: 'u128', + dest: 'StagingXcmVersionedMultiLocation', + destWeightLimit: 'StagingXcmV3WeightLimit', + }, + transfer_multiasset_with_fee: { + asset: 'StagingXcmVersionedMultiAsset', + fee: 'StagingXcmVersionedMultiAsset', + dest: 'StagingXcmVersionedMultiLocation', + destWeightLimit: 'StagingXcmV3WeightLimit', + }, + transfer_multicurrencies: { + currencies: 'Vec<(PalletForeignAssetsAssetId,u128)>', + feeItem: 'u32', + dest: 'StagingXcmVersionedMultiLocation', + destWeightLimit: 'StagingXcmV3WeightLimit', + }, + transfer_multiassets: { + assets: 'StagingXcmVersionedMultiAssets', + feeItem: 'u32', + dest: 'StagingXcmVersionedMultiLocation', + destWeightLimit: 'StagingXcmV3WeightLimit' + } + } + }, + /** + * Lookup128: staging_xcm::VersionedMultiLocation + **/ + StagingXcmVersionedMultiLocation: { + _enum: { + __Unused0: 'Null', + V2: 'StagingXcmV2MultiLocation', + __Unused2: 'Null', + V3: 'StagingXcmV3MultiLocation' + } + }, + /** + * Lookup129: staging_xcm::v2::multilocation::MultiLocation + **/ + StagingXcmV2MultiLocation: { + parents: 'u8', + interior: 'StagingXcmV2MultilocationJunctions' + }, + /** + * Lookup130: staging_xcm::v2::multilocation::Junctions + **/ + StagingXcmV2MultilocationJunctions: { + _enum: { + Here: 'Null', + X1: 'StagingXcmV2Junction', + X2: '(StagingXcmV2Junction,StagingXcmV2Junction)', + X3: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)', + X4: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)', + X5: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)', + X6: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)', + X7: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)', + X8: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)' + } + }, + /** + * Lookup131: staging_xcm::v2::junction::Junction + **/ + StagingXcmV2Junction: { + _enum: { + Parachain: 'Compact', + AccountId32: { + network: 'StagingXcmV2NetworkId', + id: '[u8;32]', + }, + AccountIndex64: { + network: 'StagingXcmV2NetworkId', + index: 'Compact', + }, + AccountKey20: { + network: 'StagingXcmV2NetworkId', + key: '[u8;20]', + }, + PalletInstance: 'u8', + GeneralIndex: 'Compact', + GeneralKey: 'Bytes', + OnlyChild: 'Null', + Plurality: { + id: 'StagingXcmV2BodyId', + part: 'StagingXcmV2BodyPart' + } + } + }, + /** + * Lookup132: staging_xcm::v2::NetworkId + **/ + StagingXcmV2NetworkId: { + _enum: { + Any: 'Null', + Named: 'Bytes', + Polkadot: 'Null', + Kusama: 'Null' + } + }, + /** + * Lookup134: staging_xcm::v2::BodyId + **/ + StagingXcmV2BodyId: { + _enum: { + Unit: 'Null', + Named: 'Bytes', + Index: 'Compact', + Executive: 'Null', + Technical: 'Null', + Legislative: 'Null', + Judicial: 'Null', + Defense: 'Null', + Administration: 'Null', + Treasury: 'Null' + } + }, + /** + * Lookup135: staging_xcm::v2::BodyPart + **/ + StagingXcmV2BodyPart: { + _enum: { + Voice: 'Null', + Members: { + count: 'Compact', + }, + Fraction: { + nom: 'Compact', + denom: 'Compact', + }, + AtLeastProportion: { + nom: 'Compact', + denom: 'Compact', + }, + MoreThanProportion: { + nom: 'Compact', + denom: 'Compact' + } + } + }, + /** + * Lookup136: staging_xcm::v3::WeightLimit + **/ + StagingXcmV3WeightLimit: { + _enum: { + Unlimited: 'Null', + Limited: 'SpWeightsWeightV2Weight' + } + }, + /** + * Lookup137: staging_xcm::VersionedMultiAsset + **/ + StagingXcmVersionedMultiAsset: { + _enum: { + __Unused0: 'Null', + V2: 'StagingXcmV2MultiAsset', + __Unused2: 'Null', + V3: 'StagingXcmV3MultiAsset' + } + }, + /** + * Lookup138: staging_xcm::v2::multiasset::MultiAsset + **/ + StagingXcmV2MultiAsset: { + id: 'StagingXcmV2MultiassetAssetId', + fun: 'StagingXcmV2MultiassetFungibility' + }, + /** + * Lookup139: staging_xcm::v2::multiasset::AssetId + **/ + StagingXcmV2MultiassetAssetId: { + _enum: { + Concrete: 'StagingXcmV2MultiLocation', + Abstract: 'Bytes' + } + }, + /** + * Lookup140: staging_xcm::v2::multiasset::Fungibility + **/ + StagingXcmV2MultiassetFungibility: { + _enum: { + Fungible: 'Compact', + NonFungible: 'StagingXcmV2MultiassetAssetInstance' + } + }, + /** + * Lookup141: staging_xcm::v2::multiasset::AssetInstance + **/ + StagingXcmV2MultiassetAssetInstance: { + _enum: { + Undefined: 'Null', + Index: 'Compact', + Array4: '[u8;4]', + Array8: '[u8;8]', + Array16: '[u8;16]', + Array32: '[u8;32]', + Blob: 'Bytes' + } + }, + /** + * Lookup144: staging_xcm::VersionedMultiAssets + **/ + StagingXcmVersionedMultiAssets: { + _enum: { + __Unused0: 'Null', + V2: 'StagingXcmV2MultiassetMultiAssets', + __Unused2: 'Null', + V3: 'StagingXcmV3MultiassetMultiAssets' + } + }, + /** + * Lookup145: staging_xcm::v2::multiasset::MultiAssets + **/ + StagingXcmV2MultiassetMultiAssets: 'Vec', + /** + * Lookup147: orml_tokens::module::Call + **/ + OrmlTokensModuleCall: { + _enum: { + transfer: { + dest: 'MultiAddress', + currencyId: 'PalletForeignAssetsAssetId', + amount: 'Compact', + }, + transfer_all: { + dest: 'MultiAddress', + currencyId: 'PalletForeignAssetsAssetId', + keepAlive: 'bool', + }, + transfer_keep_alive: { + dest: 'MultiAddress', + currencyId: 'PalletForeignAssetsAssetId', + amount: 'Compact', + }, + force_transfer: { + source: 'MultiAddress', + dest: 'MultiAddress', + currencyId: 'PalletForeignAssetsAssetId', + amount: 'Compact', + }, + set_balance: { + who: 'MultiAddress', + currencyId: 'PalletForeignAssetsAssetId', + newFree: 'Compact', + newReserved: 'Compact' + } + } + }, + /** + * Lookup148: pallet_identity::pallet::Call + **/ + PalletIdentityCall: { + _enum: { + add_registrar: { + account: 'MultiAddress', + }, + set_identity: { + info: 'PalletIdentityIdentityInfo', + }, + set_subs: { + subs: 'Vec<(AccountId32,Data)>', + }, + clear_identity: 'Null', + request_judgement: { + regIndex: 'Compact', + maxFee: 'Compact', + }, + cancel_request: { + regIndex: 'u32', + }, + set_fee: { + index: 'Compact', + fee: 'Compact', + }, + set_account_id: { + _alias: { + new_: 'new', + }, + index: 'Compact', + new_: 'MultiAddress', + }, + set_fields: { + index: 'Compact', + fields: 'PalletIdentityBitFlags', + }, + provide_judgement: { + regIndex: 'Compact', + target: 'MultiAddress', + judgement: 'PalletIdentityJudgement', + identity: 'H256', + }, + kill_identity: { + target: 'MultiAddress', + }, + add_sub: { + sub: 'MultiAddress', + data: 'Data', + }, + rename_sub: { + sub: 'MultiAddress', + data: 'Data', + }, + remove_sub: { + sub: 'MultiAddress', + }, + quit_sub: 'Null', + force_insert_identities: { + identities: 'Vec<(AccountId32,PalletIdentityRegistration)>', + }, + force_remove_identities: { + identities: 'Vec', + }, + force_set_subs: { + subs: 'Vec<(AccountId32,(u128,Vec<(AccountId32,Data)>))>' + } + } + }, + /** + * Lookup149: pallet_identity::types::IdentityInfo + **/ + PalletIdentityIdentityInfo: { + additional: 'Vec<(Data,Data)>', + display: 'Data', + legal: 'Data', + web: 'Data', + riot: 'Data', + email: 'Data', + pgpFingerprint: 'Option<[u8;20]>', + image: 'Data', + twitter: 'Data' + }, + /** + * Lookup185: pallet_identity::types::BitFlags + **/ + PalletIdentityBitFlags: { + _bitLength: 64, + Display: 1, + Legal: 2, + Web: 4, + Riot: 8, + Email: 16, + PgpFingerprint: 32, + Image: 64, + Twitter: 128 + }, + /** + * Lookup186: pallet_identity::types::IdentityField + **/ + PalletIdentityIdentityField: { + _enum: ['__Unused0', 'Display', 'Legal', '__Unused3', 'Web', '__Unused5', '__Unused6', '__Unused7', 'Riot', '__Unused9', '__Unused10', '__Unused11', '__Unused12', '__Unused13', '__Unused14', '__Unused15', 'Email', '__Unused17', '__Unused18', '__Unused19', '__Unused20', '__Unused21', '__Unused22', '__Unused23', '__Unused24', '__Unused25', '__Unused26', '__Unused27', '__Unused28', '__Unused29', '__Unused30', '__Unused31', 'PgpFingerprint', '__Unused33', '__Unused34', '__Unused35', '__Unused36', '__Unused37', '__Unused38', '__Unused39', '__Unused40', '__Unused41', '__Unused42', '__Unused43', '__Unused44', '__Unused45', '__Unused46', '__Unused47', '__Unused48', '__Unused49', '__Unused50', '__Unused51', '__Unused52', '__Unused53', '__Unused54', '__Unused55', '__Unused56', '__Unused57', '__Unused58', '__Unused59', '__Unused60', '__Unused61', '__Unused62', '__Unused63', 'Image', '__Unused65', '__Unused66', '__Unused67', '__Unused68', '__Unused69', '__Unused70', '__Unused71', '__Unused72', '__Unused73', '__Unused74', '__Unused75', '__Unused76', '__Unused77', '__Unused78', '__Unused79', '__Unused80', '__Unused81', '__Unused82', '__Unused83', '__Unused84', '__Unused85', '__Unused86', '__Unused87', '__Unused88', '__Unused89', '__Unused90', '__Unused91', '__Unused92', '__Unused93', '__Unused94', '__Unused95', '__Unused96', '__Unused97', '__Unused98', '__Unused99', '__Unused100', '__Unused101', '__Unused102', '__Unused103', '__Unused104', '__Unused105', '__Unused106', '__Unused107', '__Unused108', '__Unused109', '__Unused110', '__Unused111', '__Unused112', '__Unused113', '__Unused114', '__Unused115', '__Unused116', '__Unused117', '__Unused118', '__Unused119', '__Unused120', '__Unused121', '__Unused122', '__Unused123', '__Unused124', '__Unused125', '__Unused126', '__Unused127', 'Twitter'] + }, + /** + * Lookup187: pallet_identity::types::Judgement + **/ + PalletIdentityJudgement: { + _enum: { + Unknown: 'Null', + FeePaid: 'u128', + Reasonable: 'Null', + KnownGood: 'Null', + OutOfDate: 'Null', + LowQuality: 'Null', + Erroneous: 'Null' + } + }, + /** + * Lookup190: pallet_identity::types::Registration + **/ + PalletIdentityRegistration: { + judgements: 'Vec<(u32,PalletIdentityJudgement)>', + deposit: 'u128', + info: 'PalletIdentityIdentityInfo' + }, + /** + * Lookup198: pallet_preimage::pallet::Call + **/ + PalletPreimageCall: { + _enum: { + note_preimage: { + bytes: 'Bytes', + }, + unnote_preimage: { + _alias: { + hash_: 'hash', + }, + hash_: 'H256', + }, + request_preimage: { + _alias: { + hash_: 'hash', + }, + hash_: 'H256', + }, + unrequest_preimage: { + _alias: { + hash_: 'hash', + }, + hash_: 'H256' + } + } + }, + /** + * Lookup199: pallet_democracy::pallet::Call + **/ + PalletDemocracyCall: { + _enum: { + propose: { + proposal: 'FrameSupportPreimagesBounded', + value: 'Compact', + }, + second: { + proposal: 'Compact', + }, + vote: { + refIndex: 'Compact', + vote: 'PalletDemocracyVoteAccountVote', + }, + emergency_cancel: { + refIndex: 'u32', + }, + external_propose: { + proposal: 'FrameSupportPreimagesBounded', + }, + external_propose_majority: { + proposal: 'FrameSupportPreimagesBounded', + }, + external_propose_default: { + proposal: 'FrameSupportPreimagesBounded', + }, + fast_track: { + proposalHash: 'H256', + votingPeriod: 'u32', + delay: 'u32', + }, + veto_external: { + proposalHash: 'H256', + }, + cancel_referendum: { + refIndex: 'Compact', + }, + delegate: { + to: 'MultiAddress', + conviction: 'PalletDemocracyConviction', + balance: 'u128', + }, + undelegate: 'Null', + clear_public_proposals: 'Null', + unlock: { + target: 'MultiAddress', + }, + remove_vote: { + index: 'u32', + }, + remove_other_vote: { + target: 'MultiAddress', + index: 'u32', + }, + blacklist: { + proposalHash: 'H256', + maybeRefIndex: 'Option', + }, + cancel_proposal: { + propIndex: 'Compact', + }, + set_metadata: { + owner: 'PalletDemocracyMetadataOwner', + maybeHash: 'Option' + } + } + }, + /** + * Lookup200: pallet_democracy::conviction::Conviction + **/ + PalletDemocracyConviction: { + _enum: ['None', 'Locked1x', 'Locked2x', 'Locked3x', 'Locked4x', 'Locked5x', 'Locked6x'] + }, + /** + * Lookup203: pallet_collective::pallet::Call + **/ + PalletCollectiveCall: { + _enum: { + set_members: { + newMembers: 'Vec', + prime: 'Option', + oldCount: 'u32', + }, + execute: { + proposal: 'Call', + lengthBound: 'Compact', + }, + propose: { + threshold: 'Compact', + proposal: 'Call', + lengthBound: 'Compact', + }, + vote: { + proposal: 'H256', + index: 'Compact', + approve: 'bool', + }, + __Unused4: 'Null', + disapprove_proposal: { + proposalHash: 'H256', + }, + close: { + proposalHash: 'H256', + index: 'Compact', + proposalWeightBound: 'SpWeightsWeightV2Weight', + lengthBound: 'Compact' + } + } + }, + /** + * Lookup205: pallet_membership::pallet::Call + **/ + PalletMembershipCall: { + _enum: { + add_member: { + who: 'MultiAddress', + }, + remove_member: { + who: 'MultiAddress', + }, + swap_member: { + remove: 'MultiAddress', + add: 'MultiAddress', + }, + reset_members: { + members: 'Vec', + }, + change_key: { + _alias: { + new_: 'new', + }, + new_: 'MultiAddress', + }, + set_prime: { + who: 'MultiAddress', + }, + clear_prime: 'Null' + } + }, + /** + * Lookup207: pallet_ranked_collective::pallet::Call + **/ + PalletRankedCollectiveCall: { + _enum: { + add_member: { + who: 'MultiAddress', + }, + promote_member: { + who: 'MultiAddress', + }, + demote_member: { + who: 'MultiAddress', + }, + remove_member: { + who: 'MultiAddress', + minRank: 'u16', + }, + vote: { + poll: 'u32', + aye: 'bool', + }, + cleanup_poll: { + pollIndex: 'u32', + max: 'u32' + } + } + }, + /** + * Lookup208: pallet_referenda::pallet::Call + **/ + PalletReferendaCall: { + _enum: { + submit: { + proposalOrigin: 'OpalRuntimeOriginCaller', + proposal: 'FrameSupportPreimagesBounded', + enactmentMoment: 'FrameSupportScheduleDispatchTime', + }, + place_decision_deposit: { + index: 'u32', + }, + refund_decision_deposit: { + index: 'u32', + }, + cancel: { + index: 'u32', + }, + kill: { + index: 'u32', + }, + nudge_referendum: { + index: 'u32', + }, + one_fewer_deciding: { + track: 'u16', + }, + refund_submission_deposit: { + index: 'u32', + }, + set_metadata: { + index: 'u32', + maybeHash: 'Option' + } + } + }, + /** + * Lookup209: opal_runtime::OriginCaller + **/ + OpalRuntimeOriginCaller: { + _enum: { + system: 'FrameSupportDispatchRawOrigin', + __Unused1: 'Null', + __Unused2: 'Null', + __Unused3: 'Null', + __Unused4: 'Null', + __Unused5: 'Null', + __Unused6: 'Null', + Void: 'SpCoreVoid', + __Unused8: 'Null', + __Unused9: 'Null', + __Unused10: 'Null', + __Unused11: 'Null', + __Unused12: 'Null', + __Unused13: 'Null', + __Unused14: 'Null', + __Unused15: 'Null', + __Unused16: 'Null', + __Unused17: 'Null', + __Unused18: 'Null', + __Unused19: 'Null', + __Unused20: 'Null', + __Unused21: 'Null', + __Unused22: 'Null', + __Unused23: 'Null', + __Unused24: 'Null', + __Unused25: 'Null', + __Unused26: 'Null', + __Unused27: 'Null', + __Unused28: 'Null', + __Unused29: 'Null', + __Unused30: 'Null', + __Unused31: 'Null', + __Unused32: 'Null', + __Unused33: 'Null', + __Unused34: 'Null', + __Unused35: 'Null', + __Unused36: 'Null', + __Unused37: 'Null', + __Unused38: 'Null', + __Unused39: 'Null', + __Unused40: 'Null', + __Unused41: 'Null', + __Unused42: 'Null', + Council: 'PalletCollectiveRawOrigin', + TechnicalCommittee: 'PalletCollectiveRawOrigin', + __Unused45: 'Null', + __Unused46: 'Null', + __Unused47: 'Null', + __Unused48: 'Null', + __Unused49: 'Null', + __Unused50: 'Null', + PolkadotXcm: 'PalletXcmOrigin', + CumulusXcm: 'CumulusPalletXcmOrigin', + __Unused53: 'Null', + __Unused54: 'Null', + __Unused55: 'Null', + __Unused56: 'Null', + __Unused57: 'Null', + __Unused58: 'Null', + __Unused59: 'Null', + __Unused60: 'Null', + __Unused61: 'Null', + __Unused62: 'Null', + __Unused63: 'Null', + __Unused64: 'Null', + __Unused65: 'Null', + __Unused66: 'Null', + __Unused67: 'Null', + __Unused68: 'Null', + __Unused69: 'Null', + __Unused70: 'Null', + __Unused71: 'Null', + __Unused72: 'Null', + __Unused73: 'Null', + __Unused74: 'Null', + __Unused75: 'Null', + __Unused76: 'Null', + __Unused77: 'Null', + __Unused78: 'Null', + __Unused79: 'Null', + __Unused80: 'Null', + __Unused81: 'Null', + __Unused82: 'Null', + __Unused83: 'Null', + __Unused84: 'Null', + __Unused85: 'Null', + __Unused86: 'Null', + __Unused87: 'Null', + __Unused88: 'Null', + __Unused89: 'Null', + __Unused90: 'Null', + __Unused91: 'Null', + __Unused92: 'Null', + __Unused93: 'Null', + __Unused94: 'Null', + __Unused95: 'Null', + __Unused96: 'Null', + __Unused97: 'Null', + __Unused98: 'Null', + Origins: 'PalletGovOriginsOrigin', + __Unused100: 'Null', + Ethereum: 'PalletEthereumRawOrigin' + } + }, + /** + * Lookup210: frame_support::dispatch::RawOrigin + **/ + FrameSupportDispatchRawOrigin: { + _enum: { + Root: 'Null', + Signed: 'AccountId32', + None: 'Null' + } + }, + /** + * Lookup211: pallet_collective::RawOrigin + **/ + PalletCollectiveRawOrigin: { + _enum: { + Members: '(u32,u32)', + Member: 'AccountId32', + _Phantom: 'Null' + } + }, + /** + * Lookup213: pallet_gov_origins::pallet::Origin + **/ + PalletGovOriginsOrigin: { + _enum: ['FellowshipProposition'] + }, + /** + * Lookup214: pallet_xcm::pallet::Origin + **/ + PalletXcmOrigin: { + _enum: { + Xcm: 'StagingXcmV3MultiLocation', + Response: 'StagingXcmV3MultiLocation' + } + }, + /** + * Lookup215: cumulus_pallet_xcm::pallet::Origin + **/ + CumulusPalletXcmOrigin: { + _enum: { + Relay: 'Null', + SiblingParachain: 'u32' + } + }, + /** + * Lookup216: pallet_ethereum::RawOrigin + **/ + PalletEthereumRawOrigin: { + _enum: { + EthereumTransaction: 'H160' + } + }, + /** + * Lookup218: sp_core::Void + **/ + SpCoreVoid: 'Null', + /** + * Lookup219: frame_support::traits::schedule::DispatchTime + **/ + FrameSupportScheduleDispatchTime: { + _enum: { + At: 'u32', + After: 'u32' + } + }, + /** + * Lookup220: pallet_scheduler::pallet::Call + **/ + PalletSchedulerCall: { + _enum: { + schedule: { + when: 'u32', + maybePeriodic: 'Option<(u32,u32)>', + priority: 'u8', + call: 'Call', + }, + cancel: { + when: 'u32', + index: 'u32', + }, + schedule_named: { + id: '[u8;32]', + when: 'u32', + maybePeriodic: 'Option<(u32,u32)>', + priority: 'u8', + call: 'Call', + }, + cancel_named: { + id: '[u8;32]', + }, + schedule_after: { + after: 'u32', + maybePeriodic: 'Option<(u32,u32)>', + priority: 'u8', + call: 'Call', + }, + schedule_named_after: { + id: '[u8;32]', + after: 'u32', + maybePeriodic: 'Option<(u32,u32)>', + priority: 'u8', + call: 'Call' + } + } + }, + /** + * Lookup223: cumulus_pallet_xcmp_queue::pallet::Call + **/ + CumulusPalletXcmpQueueCall: { + _enum: { + service_overweight: { + index: 'u64', + weightLimit: 'SpWeightsWeightV2Weight', + }, + suspend_xcm_execution: 'Null', + resume_xcm_execution: 'Null', + update_suspend_threshold: { + _alias: { + new_: 'new', + }, + new_: 'u32', + }, + update_drop_threshold: { + _alias: { + new_: 'new', + }, + new_: 'u32', + }, + update_resume_threshold: { + _alias: { + new_: 'new', + }, + new_: 'u32', + }, + update_threshold_weight: { + _alias: { + new_: 'new', + }, + new_: 'SpWeightsWeightV2Weight', + }, + update_weight_restrict_decay: { + _alias: { + new_: 'new', + }, + new_: 'SpWeightsWeightV2Weight', + }, + update_xcmp_max_individual_weight: { + _alias: { + new_: 'new', + }, + new_: 'SpWeightsWeightV2Weight' + } + } + }, + /** + * Lookup224: pallet_xcm::pallet::Call + **/ + PalletXcmCall: { + _enum: { + send: { + dest: 'StagingXcmVersionedMultiLocation', + message: 'StagingXcmVersionedXcm', + }, + teleport_assets: { + dest: 'StagingXcmVersionedMultiLocation', + beneficiary: 'StagingXcmVersionedMultiLocation', + assets: 'StagingXcmVersionedMultiAssets', + feeAssetItem: 'u32', + }, + reserve_transfer_assets: { + dest: 'StagingXcmVersionedMultiLocation', + beneficiary: 'StagingXcmVersionedMultiLocation', + assets: 'StagingXcmVersionedMultiAssets', + feeAssetItem: 'u32', + }, + execute: { + message: 'StagingXcmVersionedXcm', + maxWeight: 'SpWeightsWeightV2Weight', + }, + force_xcm_version: { + location: 'StagingXcmV3MultiLocation', + version: 'u32', + }, + force_default_xcm_version: { + maybeXcmVersion: 'Option', + }, + force_subscribe_version_notify: { + location: 'StagingXcmVersionedMultiLocation', + }, + force_unsubscribe_version_notify: { + location: 'StagingXcmVersionedMultiLocation', + }, + limited_reserve_transfer_assets: { + dest: 'StagingXcmVersionedMultiLocation', + beneficiary: 'StagingXcmVersionedMultiLocation', + assets: 'StagingXcmVersionedMultiAssets', + feeAssetItem: 'u32', + weightLimit: 'StagingXcmV3WeightLimit', + }, + limited_teleport_assets: { + dest: 'StagingXcmVersionedMultiLocation', + beneficiary: 'StagingXcmVersionedMultiLocation', + assets: 'StagingXcmVersionedMultiAssets', + feeAssetItem: 'u32', + weightLimit: 'StagingXcmV3WeightLimit', + }, + force_suspension: { + suspended: 'bool' + } + } + }, + /** + * Lookup225: staging_xcm::VersionedXcm + **/ + StagingXcmVersionedXcm: { + _enum: { + __Unused0: 'Null', + __Unused1: 'Null', + V2: 'StagingXcmV2Xcm', + V3: 'StagingXcmV3Xcm' + } + }, + /** + * Lookup226: staging_xcm::v2::Xcm + **/ + StagingXcmV2Xcm: 'Vec', + /** + * Lookup228: staging_xcm::v2::Instruction + **/ + StagingXcmV2Instruction: { + _enum: { + WithdrawAsset: 'StagingXcmV2MultiassetMultiAssets', + ReserveAssetDeposited: 'StagingXcmV2MultiassetMultiAssets', + ReceiveTeleportedAsset: 'StagingXcmV2MultiassetMultiAssets', + QueryResponse: { + queryId: 'Compact', + response: 'StagingXcmV2Response', + maxWeight: 'Compact', + }, + TransferAsset: { + assets: 'StagingXcmV2MultiassetMultiAssets', + beneficiary: 'StagingXcmV2MultiLocation', + }, + TransferReserveAsset: { + assets: 'StagingXcmV2MultiassetMultiAssets', + dest: 'StagingXcmV2MultiLocation', + xcm: 'StagingXcmV2Xcm', + }, + Transact: { + originType: 'StagingXcmV2OriginKind', + requireWeightAtMost: 'Compact', + call: 'StagingXcmDoubleEncoded', + }, + HrmpNewChannelOpenRequest: { + sender: 'Compact', + maxMessageSize: 'Compact', + maxCapacity: 'Compact', + }, + HrmpChannelAccepted: { + recipient: 'Compact', + }, + HrmpChannelClosing: { + initiator: 'Compact', + sender: 'Compact', + recipient: 'Compact', + }, + ClearOrigin: 'Null', + DescendOrigin: 'StagingXcmV2MultilocationJunctions', + ReportError: { + queryId: 'Compact', + dest: 'StagingXcmV2MultiLocation', + maxResponseWeight: 'Compact', + }, + DepositAsset: { + assets: 'StagingXcmV2MultiassetMultiAssetFilter', + maxAssets: 'Compact', + beneficiary: 'StagingXcmV2MultiLocation', + }, + DepositReserveAsset: { + assets: 'StagingXcmV2MultiassetMultiAssetFilter', + maxAssets: 'Compact', + dest: 'StagingXcmV2MultiLocation', + xcm: 'StagingXcmV2Xcm', + }, + ExchangeAsset: { + give: 'StagingXcmV2MultiassetMultiAssetFilter', + receive: 'StagingXcmV2MultiassetMultiAssets', + }, + InitiateReserveWithdraw: { + assets: 'StagingXcmV2MultiassetMultiAssetFilter', + reserve: 'StagingXcmV2MultiLocation', + xcm: 'StagingXcmV2Xcm', + }, + InitiateTeleport: { + assets: 'StagingXcmV2MultiassetMultiAssetFilter', + dest: 'StagingXcmV2MultiLocation', + xcm: 'StagingXcmV2Xcm', + }, + QueryHolding: { + queryId: 'Compact', + dest: 'StagingXcmV2MultiLocation', + assets: 'StagingXcmV2MultiassetMultiAssetFilter', + maxResponseWeight: 'Compact', + }, + BuyExecution: { + fees: 'StagingXcmV2MultiAsset', + weightLimit: 'StagingXcmV2WeightLimit', + }, + RefundSurplus: 'Null', + SetErrorHandler: 'StagingXcmV2Xcm', + SetAppendix: 'StagingXcmV2Xcm', + ClearError: 'Null', + ClaimAsset: { + assets: 'StagingXcmV2MultiassetMultiAssets', + ticket: 'StagingXcmV2MultiLocation', + }, + Trap: 'Compact', + SubscribeVersion: { + queryId: 'Compact', + maxResponseWeight: 'Compact', + }, + UnsubscribeVersion: 'Null' + } + }, + /** + * Lookup229: staging_xcm::v2::Response + **/ + StagingXcmV2Response: { + _enum: { + Null: 'Null', + Assets: 'StagingXcmV2MultiassetMultiAssets', + ExecutionResult: 'Option<(u32,StagingXcmV2TraitsError)>', + Version: 'u32' + } + }, + /** + * Lookup232: staging_xcm::v2::traits::Error + **/ + StagingXcmV2TraitsError: { + _enum: { + Overflow: 'Null', + Unimplemented: 'Null', + UntrustedReserveLocation: 'Null', + UntrustedTeleportLocation: 'Null', + MultiLocationFull: 'Null', + MultiLocationNotInvertible: 'Null', + BadOrigin: 'Null', + InvalidLocation: 'Null', + AssetNotFound: 'Null', + FailedToTransactAsset: 'Null', + NotWithdrawable: 'Null', + LocationCannotHold: 'Null', + ExceedsMaxMessageSize: 'Null', + DestinationUnsupported: 'Null', + Transport: 'Null', + Unroutable: 'Null', + UnknownClaim: 'Null', + FailedToDecode: 'Null', + MaxWeightInvalid: 'Null', + NotHoldingFees: 'Null', + TooExpensive: 'Null', + Trap: 'u64', + UnhandledXcmVersion: 'Null', + WeightLimitReached: 'u64', + Barrier: 'Null', + WeightNotComputable: 'Null' + } + }, + /** + * Lookup233: staging_xcm::v2::OriginKind + **/ + StagingXcmV2OriginKind: { + _enum: ['Native', 'SovereignAccount', 'Superuser', 'Xcm'] + }, + /** + * Lookup234: staging_xcm::double_encoded::DoubleEncoded + **/ + StagingXcmDoubleEncoded: { + encoded: 'Bytes' + }, + /** + * Lookup235: staging_xcm::v2::multiasset::MultiAssetFilter + **/ + StagingXcmV2MultiassetMultiAssetFilter: { + _enum: { + Definite: 'StagingXcmV2MultiassetMultiAssets', + Wild: 'StagingXcmV2MultiassetWildMultiAsset' + } + }, + /** + * Lookup236: staging_xcm::v2::multiasset::WildMultiAsset + **/ + StagingXcmV2MultiassetWildMultiAsset: { + _enum: { + All: 'Null', + AllOf: { + id: 'StagingXcmV2MultiassetAssetId', + fun: 'StagingXcmV2MultiassetWildFungibility' + } + } + }, + /** + * Lookup237: staging_xcm::v2::multiasset::WildFungibility + **/ + StagingXcmV2MultiassetWildFungibility: { + _enum: ['Fungible', 'NonFungible'] + }, + /** + * Lookup238: staging_xcm::v2::WeightLimit + **/ + StagingXcmV2WeightLimit: { + _enum: { + Unlimited: 'Null', + Limited: 'Compact' + } + }, + /** + * Lookup239: staging_xcm::v3::Xcm + **/ + StagingXcmV3Xcm: 'Vec', + /** + * Lookup241: staging_xcm::v3::Instruction + **/ + StagingXcmV3Instruction: { + _enum: { + WithdrawAsset: 'StagingXcmV3MultiassetMultiAssets', + ReserveAssetDeposited: 'StagingXcmV3MultiassetMultiAssets', + ReceiveTeleportedAsset: 'StagingXcmV3MultiassetMultiAssets', + QueryResponse: { + queryId: 'Compact', + response: 'StagingXcmV3Response', + maxWeight: 'SpWeightsWeightV2Weight', + querier: 'Option', + }, + TransferAsset: { + assets: 'StagingXcmV3MultiassetMultiAssets', + beneficiary: 'StagingXcmV3MultiLocation', + }, + TransferReserveAsset: { + assets: 'StagingXcmV3MultiassetMultiAssets', + dest: 'StagingXcmV3MultiLocation', + xcm: 'StagingXcmV3Xcm', + }, + Transact: { + originKind: 'StagingXcmV2OriginKind', + requireWeightAtMost: 'SpWeightsWeightV2Weight', + call: 'StagingXcmDoubleEncoded', + }, + HrmpNewChannelOpenRequest: { + sender: 'Compact', + maxMessageSize: 'Compact', + maxCapacity: 'Compact', + }, + HrmpChannelAccepted: { + recipient: 'Compact', + }, + HrmpChannelClosing: { + initiator: 'Compact', + sender: 'Compact', + recipient: 'Compact', + }, + ClearOrigin: 'Null', + DescendOrigin: 'StagingXcmV3Junctions', + ReportError: 'StagingXcmV3QueryResponseInfo', + DepositAsset: { + assets: 'StagingXcmV3MultiassetMultiAssetFilter', + beneficiary: 'StagingXcmV3MultiLocation', + }, + DepositReserveAsset: { + assets: 'StagingXcmV3MultiassetMultiAssetFilter', + dest: 'StagingXcmV3MultiLocation', + xcm: 'StagingXcmV3Xcm', + }, + ExchangeAsset: { + give: 'StagingXcmV3MultiassetMultiAssetFilter', + want: 'StagingXcmV3MultiassetMultiAssets', + maximal: 'bool', + }, + InitiateReserveWithdraw: { + assets: 'StagingXcmV3MultiassetMultiAssetFilter', + reserve: 'StagingXcmV3MultiLocation', + xcm: 'StagingXcmV3Xcm', + }, + InitiateTeleport: { + assets: 'StagingXcmV3MultiassetMultiAssetFilter', + dest: 'StagingXcmV3MultiLocation', + xcm: 'StagingXcmV3Xcm', + }, + ReportHolding: { + responseInfo: 'StagingXcmV3QueryResponseInfo', + assets: 'StagingXcmV3MultiassetMultiAssetFilter', + }, + BuyExecution: { + fees: 'StagingXcmV3MultiAsset', + weightLimit: 'StagingXcmV3WeightLimit', + }, + RefundSurplus: 'Null', + SetErrorHandler: 'StagingXcmV3Xcm', + SetAppendix: 'StagingXcmV3Xcm', + ClearError: 'Null', + ClaimAsset: { + assets: 'StagingXcmV3MultiassetMultiAssets', + ticket: 'StagingXcmV3MultiLocation', + }, + Trap: 'Compact', + SubscribeVersion: { + queryId: 'Compact', + maxResponseWeight: 'SpWeightsWeightV2Weight', + }, + UnsubscribeVersion: 'Null', + BurnAsset: 'StagingXcmV3MultiassetMultiAssets', + ExpectAsset: 'StagingXcmV3MultiassetMultiAssets', + ExpectOrigin: 'Option', + ExpectError: 'Option<(u32,StagingXcmV3TraitsError)>', + ExpectTransactStatus: 'StagingXcmV3MaybeErrorCode', + QueryPallet: { + moduleName: 'Bytes', + responseInfo: 'StagingXcmV3QueryResponseInfo', + }, + ExpectPallet: { + index: 'Compact', + name: 'Bytes', + moduleName: 'Bytes', + crateMajor: 'Compact', + minCrateMinor: 'Compact', + }, + ReportTransactStatus: 'StagingXcmV3QueryResponseInfo', + ClearTransactStatus: 'Null', + UniversalOrigin: 'StagingXcmV3Junction', + ExportMessage: { + network: 'StagingXcmV3JunctionNetworkId', + destination: 'StagingXcmV3Junctions', + xcm: 'StagingXcmV3Xcm', + }, + LockAsset: { + asset: 'StagingXcmV3MultiAsset', + unlocker: 'StagingXcmV3MultiLocation', + }, + UnlockAsset: { + asset: 'StagingXcmV3MultiAsset', + target: 'StagingXcmV3MultiLocation', + }, + NoteUnlockable: { + asset: 'StagingXcmV3MultiAsset', + owner: 'StagingXcmV3MultiLocation', + }, + RequestUnlock: { + asset: 'StagingXcmV3MultiAsset', + locker: 'StagingXcmV3MultiLocation', + }, + SetFeesMode: { + jitWithdraw: 'bool', + }, + SetTopic: '[u8;32]', + ClearTopic: 'Null', + AliasOrigin: 'StagingXcmV3MultiLocation', + UnpaidExecution: { + weightLimit: 'StagingXcmV3WeightLimit', + checkOrigin: 'Option' + } + } + }, + /** + * Lookup242: staging_xcm::v3::Response + **/ + StagingXcmV3Response: { + _enum: { + Null: 'Null', + Assets: 'StagingXcmV3MultiassetMultiAssets', + ExecutionResult: 'Option<(u32,StagingXcmV3TraitsError)>', + Version: 'u32', + PalletsInfo: 'Vec', + DispatchResult: 'StagingXcmV3MaybeErrorCode' + } + }, + /** + * Lookup245: staging_xcm::v3::traits::Error + **/ + StagingXcmV3TraitsError: { + _enum: { + Overflow: 'Null', + Unimplemented: 'Null', + UntrustedReserveLocation: 'Null', + UntrustedTeleportLocation: 'Null', + LocationFull: 'Null', + LocationNotInvertible: 'Null', + BadOrigin: 'Null', + InvalidLocation: 'Null', + AssetNotFound: 'Null', + FailedToTransactAsset: 'Null', + NotWithdrawable: 'Null', + LocationCannotHold: 'Null', + ExceedsMaxMessageSize: 'Null', + DestinationUnsupported: 'Null', + Transport: 'Null', + Unroutable: 'Null', + UnknownClaim: 'Null', + FailedToDecode: 'Null', + MaxWeightInvalid: 'Null', + NotHoldingFees: 'Null', + TooExpensive: 'Null', + Trap: 'u64', + ExpectationFalse: 'Null', + PalletNotFound: 'Null', + NameMismatch: 'Null', + VersionIncompatible: 'Null', + HoldingWouldOverflow: 'Null', + ExportError: 'Null', + ReanchorFailed: 'Null', + NoDeal: 'Null', + FeesNotMet: 'Null', + LockError: 'Null', + NoPermission: 'Null', + Unanchored: 'Null', + NotDepositable: 'Null', + UnhandledXcmVersion: 'Null', + WeightLimitReached: 'SpWeightsWeightV2Weight', + Barrier: 'Null', + WeightNotComputable: 'Null', + ExceedsStackLimit: 'Null' + } + }, + /** + * Lookup247: staging_xcm::v3::PalletInfo + **/ + StagingXcmV3PalletInfo: { + index: 'Compact', + name: 'Bytes', + moduleName: 'Bytes', + major: 'Compact', + minor: 'Compact', + patch: 'Compact' + }, + /** + * Lookup250: staging_xcm::v3::MaybeErrorCode + **/ + StagingXcmV3MaybeErrorCode: { + _enum: { + Success: 'Null', + Error: 'Bytes', + TruncatedError: 'Bytes' + } + }, + /** + * Lookup253: staging_xcm::v3::QueryResponseInfo + **/ + StagingXcmV3QueryResponseInfo: { + destination: 'StagingXcmV3MultiLocation', + queryId: 'Compact', + maxWeight: 'SpWeightsWeightV2Weight' + }, + /** + * Lookup254: staging_xcm::v3::multiasset::MultiAssetFilter + **/ + StagingXcmV3MultiassetMultiAssetFilter: { + _enum: { + Definite: 'StagingXcmV3MultiassetMultiAssets', + Wild: 'StagingXcmV3MultiassetWildMultiAsset' + } + }, + /** + * Lookup255: staging_xcm::v3::multiasset::WildMultiAsset + **/ + StagingXcmV3MultiassetWildMultiAsset: { + _enum: { + All: 'Null', + AllOf: { + id: 'StagingXcmV3MultiassetAssetId', + fun: 'StagingXcmV3MultiassetWildFungibility', + }, + AllCounted: 'Compact', + AllOfCounted: { + id: 'StagingXcmV3MultiassetAssetId', + fun: 'StagingXcmV3MultiassetWildFungibility', + count: 'Compact' + } + } + }, + /** + * Lookup256: staging_xcm::v3::multiasset::WildFungibility + **/ + StagingXcmV3MultiassetWildFungibility: { + _enum: ['Fungible', 'NonFungible'] + }, + /** + * Lookup265: cumulus_pallet_xcm::pallet::Call + **/ + CumulusPalletXcmCall: 'Null', + /** + * Lookup266: cumulus_pallet_dmp_queue::pallet::Call + **/ + CumulusPalletDmpQueueCall: { + _enum: { + service_overweight: { + index: 'u64', + weightLimit: 'SpWeightsWeightV2Weight' + } + } + }, + /** + * Lookup267: pallet_inflation::pallet::Call + **/ + PalletInflationCall: { + _enum: { + start_inflation: { + inflationStartRelayBlock: 'u32' + } + } + }, + /** + * Lookup268: pallet_unique::pallet::Call + **/ + PalletUniqueCall: { + _enum: { + create_collection: { + collectionName: 'Vec', + collectionDescription: 'Vec', + tokenPrefix: 'Bytes', + mode: 'UpDataStructsCollectionMode', + }, + create_collection_ex: { + data: 'UpDataStructsCreateCollectionData', + }, + destroy_collection: { + collectionId: 'u32', + }, + add_to_allow_list: { + collectionId: 'u32', + address: 'PalletEvmAccountBasicCrossAccountIdRepr', + }, + remove_from_allow_list: { + collectionId: 'u32', + address: 'PalletEvmAccountBasicCrossAccountIdRepr', + }, + change_collection_owner: { + collectionId: 'u32', + newOwner: 'AccountId32', + }, + add_collection_admin: { + collectionId: 'u32', + newAdminId: 'PalletEvmAccountBasicCrossAccountIdRepr', + }, + remove_collection_admin: { + collectionId: 'u32', + accountId: 'PalletEvmAccountBasicCrossAccountIdRepr', + }, + set_collection_sponsor: { + collectionId: 'u32', + newSponsor: 'AccountId32', + }, + confirm_sponsorship: { + collectionId: 'u32', + }, + remove_collection_sponsor: { + collectionId: 'u32', + }, + create_item: { + collectionId: 'u32', + owner: 'PalletEvmAccountBasicCrossAccountIdRepr', + data: 'UpDataStructsCreateItemData', + }, + create_multiple_items: { + collectionId: 'u32', + owner: 'PalletEvmAccountBasicCrossAccountIdRepr', + itemsData: 'Vec', + }, + set_collection_properties: { + collectionId: 'u32', + properties: 'Vec', + }, + delete_collection_properties: { + collectionId: 'u32', + propertyKeys: 'Vec', + }, + set_token_properties: { + collectionId: 'u32', + tokenId: 'u32', + properties: 'Vec', + }, + delete_token_properties: { + collectionId: 'u32', + tokenId: 'u32', + propertyKeys: 'Vec', + }, + set_token_property_permissions: { + collectionId: 'u32', + propertyPermissions: 'Vec', + }, + create_multiple_items_ex: { + collectionId: 'u32', + data: 'UpDataStructsCreateItemExData', + }, + set_transfers_enabled_flag: { + collectionId: 'u32', + value: 'bool', + }, + burn_item: { + collectionId: 'u32', + itemId: 'u32', + value: 'u128', + }, + burn_from: { + collectionId: 'u32', + from: 'PalletEvmAccountBasicCrossAccountIdRepr', + itemId: 'u32', + value: 'u128', + }, + transfer: { + recipient: 'PalletEvmAccountBasicCrossAccountIdRepr', + collectionId: 'u32', + itemId: 'u32', + value: 'u128', + }, + approve: { + spender: 'PalletEvmAccountBasicCrossAccountIdRepr', + collectionId: 'u32', + itemId: 'u32', + amount: 'u128', + }, + approve_from: { + from: 'PalletEvmAccountBasicCrossAccountIdRepr', + to: 'PalletEvmAccountBasicCrossAccountIdRepr', + collectionId: 'u32', + itemId: 'u32', + amount: 'u128', + }, + transfer_from: { + from: 'PalletEvmAccountBasicCrossAccountIdRepr', + recipient: 'PalletEvmAccountBasicCrossAccountIdRepr', + collectionId: 'u32', + itemId: 'u32', + value: 'u128', + }, + set_collection_limits: { + collectionId: 'u32', + newLimit: 'UpDataStructsCollectionLimits', + }, + set_collection_permissions: { + collectionId: 'u32', + newPermission: 'UpDataStructsCollectionPermissions', + }, + repartition: { + collectionId: 'u32', + tokenId: 'u32', + amount: 'u128', + }, + set_allowance_for_all: { + collectionId: 'u32', + operator: 'PalletEvmAccountBasicCrossAccountIdRepr', + approve: 'bool', + }, + force_repair_collection: { + collectionId: 'u32', + }, + force_repair_item: { + collectionId: 'u32', + itemId: 'u32' + } + } + }, + /** + * Lookup273: up_data_structs::CollectionMode + **/ + UpDataStructsCollectionMode: { + _enum: { + NFT: 'Null', + Fungible: 'u8', + ReFungible: 'Null' + } + }, + /** + * Lookup274: up_data_structs::CreateCollectionData> + **/ + UpDataStructsCreateCollectionData: { + mode: 'UpDataStructsCollectionMode', + access: 'Option', + name: 'Vec', + description: 'Vec', + tokenPrefix: 'Bytes', + limits: 'Option', + permissions: 'Option', + tokenPropertyPermissions: 'Vec', + properties: 'Vec', + adminList: 'Vec', + pendingSponsor: 'Option', + flags: '[u8;1]' + }, + /** + * Lookup275: pallet_evm::account::BasicCrossAccountIdRepr + **/ + PalletEvmAccountBasicCrossAccountIdRepr: { + _enum: { + Substrate: 'AccountId32', + Ethereum: 'H160' + } + }, + /** + * Lookup277: up_data_structs::AccessMode + **/ + UpDataStructsAccessMode: { + _enum: ['Normal', 'AllowList'] + }, + /** + * Lookup279: up_data_structs::CollectionLimits + **/ + UpDataStructsCollectionLimits: { + accountTokenOwnershipLimit: 'Option', + sponsoredDataSize: 'Option', + sponsoredDataRateLimit: 'Option', + tokenLimit: 'Option', + sponsorTransferTimeout: 'Option', + sponsorApproveTimeout: 'Option', + ownerCanTransfer: 'Option', + ownerCanDestroy: 'Option', + transfersEnabled: 'Option' + }, + /** + * Lookup281: up_data_structs::SponsoringRateLimit + **/ + UpDataStructsSponsoringRateLimit: { + _enum: { + SponsoringDisabled: 'Null', + Blocks: 'u32' + } + }, + /** + * Lookup284: up_data_structs::CollectionPermissions + **/ + UpDataStructsCollectionPermissions: { + access: 'Option', + mintMode: 'Option', + nesting: 'Option' + }, + /** + * Lookup286: up_data_structs::NestingPermissions + **/ + UpDataStructsNestingPermissions: { + tokenOwner: 'bool', + collectionAdmin: 'bool', + restricted: 'Option' + }, + /** + * Lookup288: up_data_structs::OwnerRestrictedSet + **/ + UpDataStructsOwnerRestrictedSet: 'BTreeSet', + /** + * Lookup294: up_data_structs::PropertyKeyPermission + **/ + UpDataStructsPropertyKeyPermission: { + key: 'Bytes', + permission: 'UpDataStructsPropertyPermission' + }, + /** + * Lookup296: up_data_structs::PropertyPermission + **/ + UpDataStructsPropertyPermission: { + mutable: 'bool', + collectionAdmin: 'bool', + tokenOwner: 'bool' + }, + /** + * Lookup299: up_data_structs::Property + **/ + UpDataStructsProperty: { + key: 'Bytes', + value: 'Bytes' + }, + /** + * Lookup304: up_data_structs::CreateItemData + **/ + UpDataStructsCreateItemData: { + _enum: { + NFT: 'UpDataStructsCreateNftData', + Fungible: 'UpDataStructsCreateFungibleData', + ReFungible: 'UpDataStructsCreateReFungibleData' + } + }, + /** + * Lookup305: up_data_structs::CreateNftData + **/ + UpDataStructsCreateNftData: { + properties: 'Vec' + }, + /** + * Lookup306: up_data_structs::CreateFungibleData + **/ + UpDataStructsCreateFungibleData: { + value: 'u128' + }, + /** + * Lookup307: up_data_structs::CreateReFungibleData + **/ + UpDataStructsCreateReFungibleData: { + pieces: 'u128', + properties: 'Vec' + }, + /** + * Lookup311: up_data_structs::CreateItemExData> + **/ + UpDataStructsCreateItemExData: { + _enum: { + NFT: 'Vec', + Fungible: 'BTreeMap', + RefungibleMultipleItems: 'Vec', + RefungibleMultipleOwners: 'UpDataStructsCreateRefungibleExMultipleOwners' + } + }, + /** + * Lookup313: up_data_structs::CreateNftExData> + **/ + UpDataStructsCreateNftExData: { + properties: 'Vec', + owner: 'PalletEvmAccountBasicCrossAccountIdRepr' + }, + /** + * Lookup320: up_data_structs::CreateRefungibleExSingleOwner> + **/ + UpDataStructsCreateRefungibleExSingleOwner: { + user: 'PalletEvmAccountBasicCrossAccountIdRepr', + pieces: 'u128', + properties: 'Vec' + }, + /** + * Lookup322: up_data_structs::CreateRefungibleExMultipleOwners> + **/ + UpDataStructsCreateRefungibleExMultipleOwners: { + users: 'BTreeMap', + properties: 'Vec' + }, + /** + * Lookup323: pallet_configuration::pallet::Call + **/ + PalletConfigurationCall: { + _enum: { + set_weight_to_fee_coefficient_override: { + coeff: 'Option', + }, + set_min_gas_price_override: { + coeff: 'Option', + }, + __Unused2: 'Null', + set_app_promotion_configuration_override: { + configuration: 'PalletConfigurationAppPromotionConfiguration', + }, + set_collator_selection_desired_collators: { + max: 'Option', + }, + set_collator_selection_license_bond: { + amount: 'Option', + }, + set_collator_selection_kick_threshold: { + threshold: 'Option' + } + } + }, + /** + * Lookup325: pallet_configuration::AppPromotionConfiguration + **/ + PalletConfigurationAppPromotionConfiguration: { + recalculationInterval: 'Option', + pendingInterval: 'Option', + intervalIncome: 'Option', + maxStakersPerCalculation: 'Option' + }, + /** + * Lookup330: pallet_structure::pallet::Call + **/ + PalletStructureCall: 'Null', + /** + * Lookup331: pallet_app_promotion::pallet::Call + **/ + PalletAppPromotionCall: { + _enum: { + set_admin_address: { + admin: 'PalletEvmAccountBasicCrossAccountIdRepr', + }, + stake: { + amount: 'u128', + }, + unstake_all: 'Null', + sponsor_collection: { + collectionId: 'u32', + }, + stop_sponsoring_collection: { + collectionId: 'u32', + }, + sponsor_contract: { + contractId: 'H160', + }, + stop_sponsoring_contract: { + contractId: 'H160', + }, + payout_stakers: { + stakersNumber: 'Option', + }, + unstake_partial: { + amount: 'u128', + }, + force_unstake: { + pendingBlocks: 'Vec' + } + } + }, + /** + * Lookup333: pallet_foreign_assets::module::Call + **/ + PalletForeignAssetsModuleCall: { + _enum: { + register_foreign_asset: { + owner: 'AccountId32', + location: 'StagingXcmVersionedMultiLocation', + metadata: 'PalletForeignAssetsModuleAssetMetadata', + }, + update_foreign_asset: { + foreignAssetId: 'u32', + location: 'StagingXcmVersionedMultiLocation', + metadata: 'PalletForeignAssetsModuleAssetMetadata' + } + } + }, + /** + * Lookup334: pallet_foreign_assets::module::AssetMetadata + **/ + PalletForeignAssetsModuleAssetMetadata: { + name: 'Bytes', + symbol: 'Bytes', + decimals: 'u8', + minimalBalance: 'u128' + }, + /** + * Lookup337: pallet_evm::pallet::Call + **/ + PalletEvmCall: { + _enum: { + withdraw: { + address: 'H160', + value: 'u128', + }, + call: { + source: 'H160', + target: 'H160', + input: 'Bytes', + value: 'U256', + gasLimit: 'u64', + maxFeePerGas: 'U256', + maxPriorityFeePerGas: 'Option', + nonce: 'Option', + accessList: 'Vec<(H160,Vec)>', + }, + create: { + source: 'H160', + init: 'Bytes', + value: 'U256', + gasLimit: 'u64', + maxFeePerGas: 'U256', + maxPriorityFeePerGas: 'Option', + nonce: 'Option', + accessList: 'Vec<(H160,Vec)>', + }, + create2: { + source: 'H160', + init: 'Bytes', + salt: 'H256', + value: 'U256', + gasLimit: 'u64', + maxFeePerGas: 'U256', + maxPriorityFeePerGas: 'Option', + nonce: 'Option', + accessList: 'Vec<(H160,Vec)>' + } + } + }, + /** + * Lookup344: pallet_ethereum::pallet::Call + **/ + PalletEthereumCall: { + _enum: { + transact: { + transaction: 'EthereumTransactionTransactionV2' + } + } + }, + /** + * Lookup345: ethereum::transaction::TransactionV2 + **/ + EthereumTransactionTransactionV2: { + _enum: { + Legacy: 'EthereumTransactionLegacyTransaction', + EIP2930: 'EthereumTransactionEip2930Transaction', + EIP1559: 'EthereumTransactionEip1559Transaction' + } + }, + /** + * Lookup346: ethereum::transaction::LegacyTransaction + **/ + EthereumTransactionLegacyTransaction: { + nonce: 'U256', + gasPrice: 'U256', + gasLimit: 'U256', + action: 'EthereumTransactionTransactionAction', + value: 'U256', + input: 'Bytes', + signature: 'EthereumTransactionTransactionSignature' + }, + /** + * Lookup347: ethereum::transaction::TransactionAction + **/ + EthereumTransactionTransactionAction: { + _enum: { + Call: 'H160', + Create: 'Null' + } + }, + /** + * Lookup348: ethereum::transaction::TransactionSignature + **/ + EthereumTransactionTransactionSignature: { + v: 'u64', + r: 'H256', + s: 'H256' + }, + /** + * Lookup350: ethereum::transaction::EIP2930Transaction + **/ + EthereumTransactionEip2930Transaction: { + chainId: 'u64', + nonce: 'U256', + gasPrice: 'U256', + gasLimit: 'U256', + action: 'EthereumTransactionTransactionAction', + value: 'U256', + input: 'Bytes', + accessList: 'Vec', + oddYParity: 'bool', + r: 'H256', + s: 'H256' + }, + /** + * Lookup352: ethereum::transaction::AccessListItem + **/ + EthereumTransactionAccessListItem: { + address: 'H160', + storageKeys: 'Vec' + }, + /** + * Lookup353: ethereum::transaction::EIP1559Transaction + **/ + EthereumTransactionEip1559Transaction: { + chainId: 'u64', + nonce: 'U256', + maxPriorityFeePerGas: 'U256', + maxFeePerGas: 'U256', + gasLimit: 'U256', + action: 'EthereumTransactionTransactionAction', + value: 'U256', + input: 'Bytes', + accessList: 'Vec', + oddYParity: 'bool', + r: 'H256', + s: 'H256' + }, + /** + * Lookup354: pallet_evm_contract_helpers::pallet::Call + **/ + PalletEvmContractHelpersCall: { + _enum: { + migrate_from_self_sponsoring: { + addresses: 'Vec' + } + } + }, + /** + * Lookup356: pallet_evm_migration::pallet::Call + **/ + PalletEvmMigrationCall: { + _enum: { + begin: { + address: 'H160', + }, + set_data: { + address: 'H160', + data: 'Vec<(H256,H256)>', + }, + finish: { + address: 'H160', + code: 'Bytes', + }, + insert_eth_logs: { + logs: 'Vec', + }, + insert_events: { + events: 'Vec', + }, + remove_rmrk_data: 'Null' + } + }, + /** + * Lookup360: ethereum::log::Log + **/ + EthereumLog: { + address: 'H160', + topics: 'Vec', + data: 'Bytes' + }, + /** + * Lookup361: pallet_maintenance::pallet::Call + **/ + PalletMaintenanceCall: { + _enum: ['enable', 'disable'] + }, + /** + * Lookup362: pallet_utility::pallet::Call + **/ + PalletUtilityCall: { + _enum: { + batch: { + calls: 'Vec', + }, + as_derivative: { + index: 'u16', + call: 'Call', + }, + batch_all: { + calls: 'Vec', + }, + dispatch_as: { + asOrigin: 'OpalRuntimeOriginCaller', + call: 'Call', + }, + force_batch: { + calls: 'Vec', + }, + with_weight: { + call: 'Call', + weight: 'SpWeightsWeightV2Weight' + } + } + }, + /** + * Lookup364: pallet_test_utils::pallet::Call + **/ + PalletTestUtilsCall: { + _enum: { + enable: 'Null', + set_test_value: { + value: 'u32', + }, + set_test_value_and_rollback: { + value: 'u32', + }, + inc_test_value: 'Null', + just_take_fee: 'Null', + batch_all: { + calls: 'Vec' + } + } + }, + /** + * Lookup366: pallet_scheduler::pallet::Event + **/ + PalletSchedulerEvent: { + _enum: { + Scheduled: { + when: 'u32', + index: 'u32', + }, + Canceled: { + when: 'u32', + index: 'u32', + }, + Dispatched: { + task: '(u32,u32)', + id: 'Option<[u8;32]>', + result: 'Result', + }, + CallUnavailable: { + task: '(u32,u32)', + id: 'Option<[u8;32]>', + }, + PeriodicFailed: { + task: '(u32,u32)', + id: 'Option<[u8;32]>', + }, + PermanentlyOverweight: { + task: '(u32,u32)', + id: 'Option<[u8;32]>' + } + } + }, + /** + * Lookup367: cumulus_pallet_xcmp_queue::pallet::Event + **/ + CumulusPalletXcmpQueueEvent: { + _enum: { + Success: { + messageHash: '[u8;32]', + messageId: '[u8;32]', + weight: 'SpWeightsWeightV2Weight', + }, + Fail: { + messageHash: '[u8;32]', + messageId: '[u8;32]', + error: 'StagingXcmV3TraitsError', + weight: 'SpWeightsWeightV2Weight', + }, + BadVersion: { + messageHash: '[u8;32]', + }, + BadFormat: { + messageHash: '[u8;32]', + }, + XcmpMessageSent: { + messageHash: '[u8;32]', + }, + OverweightEnqueued: { + sender: 'u32', + sentAt: 'u32', + index: 'u64', + required: 'SpWeightsWeightV2Weight', + }, + OverweightServiced: { + index: 'u64', + used: 'SpWeightsWeightV2Weight' + } + } + }, + /** + * Lookup368: pallet_xcm::pallet::Event + **/ + PalletXcmEvent: { + _enum: { + Attempted: { + outcome: 'StagingXcmV3TraitsOutcome', + }, + Sent: { + origin: 'StagingXcmV3MultiLocation', + destination: 'StagingXcmV3MultiLocation', + message: 'StagingXcmV3Xcm', + messageId: '[u8;32]', + }, + UnexpectedResponse: { + origin: 'StagingXcmV3MultiLocation', + queryId: 'u64', + }, + ResponseReady: { + queryId: 'u64', + response: 'StagingXcmV3Response', + }, + Notified: { + queryId: 'u64', + palletIndex: 'u8', + callIndex: 'u8', + }, + NotifyOverweight: { + queryId: 'u64', + palletIndex: 'u8', + callIndex: 'u8', + actualWeight: 'SpWeightsWeightV2Weight', + maxBudgetedWeight: 'SpWeightsWeightV2Weight', + }, + NotifyDispatchError: { + queryId: 'u64', + palletIndex: 'u8', + callIndex: 'u8', + }, + NotifyDecodeFailed: { + queryId: 'u64', + palletIndex: 'u8', + callIndex: 'u8', + }, + InvalidResponder: { + origin: 'StagingXcmV3MultiLocation', + queryId: 'u64', + expectedLocation: 'Option', + }, + InvalidResponderVersion: { + origin: 'StagingXcmV3MultiLocation', + queryId: 'u64', + }, + ResponseTaken: { + queryId: 'u64', + }, + AssetsTrapped: { + _alias: { + hash_: 'hash', + }, + hash_: 'H256', + origin: 'StagingXcmV3MultiLocation', + assets: 'StagingXcmVersionedMultiAssets', + }, + VersionChangeNotified: { + destination: 'StagingXcmV3MultiLocation', + result: 'u32', + cost: 'StagingXcmV3MultiassetMultiAssets', + messageId: '[u8;32]', + }, + SupportedVersionChanged: { + location: 'StagingXcmV3MultiLocation', + version: 'u32', + }, + NotifyTargetSendFail: { + location: 'StagingXcmV3MultiLocation', + queryId: 'u64', + error: 'StagingXcmV3TraitsError', + }, + NotifyTargetMigrationFail: { + location: 'StagingXcmVersionedMultiLocation', + queryId: 'u64', + }, + InvalidQuerierVersion: { + origin: 'StagingXcmV3MultiLocation', + queryId: 'u64', + }, + InvalidQuerier: { + origin: 'StagingXcmV3MultiLocation', + queryId: 'u64', + expectedQuerier: 'StagingXcmV3MultiLocation', + maybeActualQuerier: 'Option', + }, + VersionNotifyStarted: { + destination: 'StagingXcmV3MultiLocation', + cost: 'StagingXcmV3MultiassetMultiAssets', + messageId: '[u8;32]', + }, + VersionNotifyRequested: { + destination: 'StagingXcmV3MultiLocation', + cost: 'StagingXcmV3MultiassetMultiAssets', + messageId: '[u8;32]', + }, + VersionNotifyUnrequested: { + destination: 'StagingXcmV3MultiLocation', + cost: 'StagingXcmV3MultiassetMultiAssets', + messageId: '[u8;32]', + }, + FeesPaid: { + paying: 'StagingXcmV3MultiLocation', + fees: 'StagingXcmV3MultiassetMultiAssets', + }, + AssetsClaimed: { + _alias: { + hash_: 'hash', + }, + hash_: 'H256', + origin: 'StagingXcmV3MultiLocation', + assets: 'StagingXcmVersionedMultiAssets' + } + } + }, + /** + * Lookup369: staging_xcm::v3::traits::Outcome + **/ + StagingXcmV3TraitsOutcome: { + _enum: { + Complete: 'SpWeightsWeightV2Weight', + Incomplete: '(SpWeightsWeightV2Weight,StagingXcmV3TraitsError)', + Error: 'StagingXcmV3TraitsError' + } + }, + /** + * Lookup370: cumulus_pallet_xcm::pallet::Event + **/ + CumulusPalletXcmEvent: { + _enum: { + InvalidFormat: '[u8;32]', + UnsupportedVersion: '[u8;32]', + ExecutedDownward: '([u8;32],StagingXcmV3TraitsOutcome)' + } + }, + /** + * Lookup371: cumulus_pallet_dmp_queue::pallet::Event + **/ + CumulusPalletDmpQueueEvent: { + _enum: { + InvalidFormat: { + messageHash: '[u8;32]', + }, + UnsupportedVersion: { + messageHash: '[u8;32]', + }, + ExecutedDownward: { + messageHash: '[u8;32]', + messageId: '[u8;32]', + outcome: 'StagingXcmV3TraitsOutcome', + }, + WeightExhausted: { + messageHash: '[u8;32]', + messageId: '[u8;32]', + remainingWeight: 'SpWeightsWeightV2Weight', + requiredWeight: 'SpWeightsWeightV2Weight', + }, + OverweightEnqueued: { + messageHash: '[u8;32]', + messageId: '[u8;32]', + overweightIndex: 'u64', + requiredWeight: 'SpWeightsWeightV2Weight', + }, + OverweightServiced: { + overweightIndex: 'u64', + weightUsed: 'SpWeightsWeightV2Weight', + }, + MaxMessagesExhausted: { + messageHash: '[u8;32]' + } + } + }, + /** + * Lookup372: pallet_configuration::pallet::Event + **/ + PalletConfigurationEvent: { + _enum: { + NewDesiredCollators: { + desiredCollators: 'Option', + }, + NewCollatorLicenseBond: { + bondCost: 'Option', + }, + NewCollatorKickThreshold: { + lengthInBlocks: 'Option' + } + } + }, + /** + * Lookup373: pallet_common::pallet::Event + **/ + PalletCommonEvent: { + _enum: { + CollectionCreated: '(u32,u8,AccountId32)', + CollectionDestroyed: 'u32', + ItemCreated: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,u128)', + ItemDestroyed: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,u128)', + Transfer: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,u128)', + Approved: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,u128)', + ApprovedForAll: '(u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,bool)', + CollectionPropertySet: '(u32,Bytes)', + CollectionPropertyDeleted: '(u32,Bytes)', + TokenPropertySet: '(u32,u32,Bytes)', + TokenPropertyDeleted: '(u32,u32,Bytes)', + PropertyPermissionSet: '(u32,Bytes)', + AllowListAddressAdded: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)', + AllowListAddressRemoved: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)', + CollectionAdminAdded: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)', + CollectionAdminRemoved: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)', + CollectionLimitSet: 'u32', + CollectionOwnerChanged: '(u32,AccountId32)', + CollectionPermissionSet: 'u32', + CollectionSponsorSet: '(u32,AccountId32)', + SponsorshipConfirmed: '(u32,AccountId32)', + CollectionSponsorRemoved: 'u32' + } + }, + /** + * Lookup374: pallet_structure::pallet::Event + **/ + PalletStructureEvent: { + _enum: { + Executed: 'Result' + } + }, + /** + * Lookup375: pallet_app_promotion::pallet::Event + **/ + PalletAppPromotionEvent: { + _enum: { + StakingRecalculation: '(AccountId32,u128,u128)', + Stake: '(AccountId32,u128)', + Unstake: '(AccountId32,u128)', + SetAdmin: 'AccountId32' + } + }, + /** + * Lookup376: pallet_foreign_assets::module::Event + **/ + PalletForeignAssetsModuleEvent: { + _enum: { + ForeignAssetRegistered: { + assetId: 'u32', + assetAddress: 'StagingXcmV3MultiLocation', + metadata: 'PalletForeignAssetsModuleAssetMetadata', + }, + ForeignAssetUpdated: { + assetId: 'u32', + assetAddress: 'StagingXcmV3MultiLocation', + metadata: 'PalletForeignAssetsModuleAssetMetadata', + }, + AssetRegistered: { + assetId: 'PalletForeignAssetsAssetId', + metadata: 'PalletForeignAssetsModuleAssetMetadata', + }, + AssetUpdated: { + assetId: 'PalletForeignAssetsAssetId', + metadata: 'PalletForeignAssetsModuleAssetMetadata' + } + } + }, + /** + * Lookup377: pallet_evm::pallet::Event + **/ + PalletEvmEvent: { + _enum: { + Log: { + log: 'EthereumLog', + }, + Created: { + address: 'H160', + }, + CreatedFailed: { + address: 'H160', + }, + Executed: { + address: 'H160', + }, + ExecutedFailed: { + address: 'H160' + } + } + }, + /** + * Lookup378: pallet_ethereum::pallet::Event + **/ + PalletEthereumEvent: { + _enum: { + Executed: { + from: 'H160', + to: 'H160', + transactionHash: 'H256', + exitReason: 'EvmCoreErrorExitReason', + extraData: 'Bytes' + } + } + }, + /** + * Lookup379: evm_core::error::ExitReason + **/ + EvmCoreErrorExitReason: { + _enum: { + Succeed: 'EvmCoreErrorExitSucceed', + Error: 'EvmCoreErrorExitError', + Revert: 'EvmCoreErrorExitRevert', + Fatal: 'EvmCoreErrorExitFatal' + } + }, + /** + * Lookup380: evm_core::error::ExitSucceed + **/ + EvmCoreErrorExitSucceed: { + _enum: ['Stopped', 'Returned', 'Suicided'] + }, + /** + * Lookup381: evm_core::error::ExitError + **/ + EvmCoreErrorExitError: { + _enum: { + StackUnderflow: 'Null', + StackOverflow: 'Null', + InvalidJump: 'Null', + InvalidRange: 'Null', + DesignatedInvalid: 'Null', + CallTooDeep: 'Null', + CreateCollision: 'Null', + CreateContractLimit: 'Null', + OutOfOffset: 'Null', + OutOfGas: 'Null', + OutOfFund: 'Null', + PCUnderflow: 'Null', + CreateEmpty: 'Null', + Other: 'Text', + MaxNonce: 'Null', + InvalidCode: 'u8' + } + }, + /** + * Lookup385: evm_core::error::ExitRevert + **/ + EvmCoreErrorExitRevert: { + _enum: ['Reverted'] + }, + /** + * Lookup386: evm_core::error::ExitFatal + **/ + EvmCoreErrorExitFatal: { + _enum: { + NotSupported: 'Null', + UnhandledInterrupt: 'Null', + CallErrorAsFatal: 'EvmCoreErrorExitError', + Other: 'Text' + } + }, + /** + * Lookup387: pallet_evm_contract_helpers::pallet::Event + **/ + PalletEvmContractHelpersEvent: { + _enum: { + ContractSponsorSet: '(H160,AccountId32)', + ContractSponsorshipConfirmed: '(H160,AccountId32)', + ContractSponsorRemoved: 'H160' + } + }, + /** + * Lookup388: pallet_evm_migration::pallet::Event + **/ + PalletEvmMigrationEvent: { + _enum: ['TestEvent'] + }, + /** + * Lookup389: pallet_maintenance::pallet::Event + **/ + PalletMaintenanceEvent: { + _enum: ['MaintenanceEnabled', 'MaintenanceDisabled'] + }, + /** + * Lookup390: pallet_utility::pallet::Event + **/ + PalletUtilityEvent: { + _enum: { + BatchInterrupted: { + index: 'u32', + error: 'SpRuntimeDispatchError', + }, + BatchCompleted: 'Null', + BatchCompletedWithErrors: 'Null', + ItemCompleted: 'Null', + ItemFailed: { + error: 'SpRuntimeDispatchError', + }, + DispatchedAs: { + result: 'Result' + } + } + }, + /** + * Lookup391: pallet_test_utils::pallet::Event + **/ + PalletTestUtilsEvent: { + _enum: ['ValueIsSet', 'ShouldRollback', 'BatchCompleted'] + }, + /** + * Lookup392: frame_system::Phase + **/ + FrameSystemPhase: { + _enum: { + ApplyExtrinsic: 'u32', + Finalization: 'Null', + Initialization: 'Null' + } + }, + /** + * Lookup394: frame_system::LastRuntimeUpgradeInfo + **/ + FrameSystemLastRuntimeUpgradeInfo: { + specVersion: 'Compact', + specName: 'Text' + }, + /** + * Lookup395: frame_system::limits::BlockWeights + **/ + FrameSystemLimitsBlockWeights: { + baseBlock: 'SpWeightsWeightV2Weight', + maxBlock: 'SpWeightsWeightV2Weight', + perClass: 'FrameSupportDispatchPerDispatchClassWeightsPerClass' + }, + /** + * Lookup396: frame_support::dispatch::PerDispatchClass + **/ + FrameSupportDispatchPerDispatchClassWeightsPerClass: { + normal: 'FrameSystemLimitsWeightsPerClass', + operational: 'FrameSystemLimitsWeightsPerClass', + mandatory: 'FrameSystemLimitsWeightsPerClass' + }, + /** + * Lookup397: frame_system::limits::WeightsPerClass + **/ + FrameSystemLimitsWeightsPerClass: { + baseExtrinsic: 'SpWeightsWeightV2Weight', + maxExtrinsic: 'Option', + maxTotal: 'Option', + reserved: 'Option' + }, + /** + * Lookup399: frame_system::limits::BlockLength + **/ + FrameSystemLimitsBlockLength: { + max: 'FrameSupportDispatchPerDispatchClassU32' + }, + /** + * Lookup400: frame_support::dispatch::PerDispatchClass + **/ + FrameSupportDispatchPerDispatchClassU32: { + normal: 'u32', + operational: 'u32', + mandatory: 'u32' + }, + /** + * Lookup401: sp_weights::RuntimeDbWeight + **/ + SpWeightsRuntimeDbWeight: { + read: 'u64', + write: 'u64' + }, + /** + * Lookup402: sp_version::RuntimeVersion + **/ + SpVersionRuntimeVersion: { + specName: 'Text', + implName: 'Text', + authoringVersion: 'u32', + specVersion: 'u32', + implVersion: 'u32', + apis: 'Vec<([u8;8],u32)>', + transactionVersion: 'u32', + stateVersion: 'u8' + }, + /** + * Lookup406: frame_system::pallet::Error + **/ + FrameSystemError: { + _enum: ['InvalidSpecName', 'SpecVersionNeedsToIncrease', 'FailedToExtractRuntimeVersion', 'NonDefaultComposite', 'NonZeroRefCount', 'CallFiltered'] + }, + /** + * Lookup408: cumulus_pallet_parachain_system::unincluded_segment::Ancestor + **/ + CumulusPalletParachainSystemUnincludedSegmentAncestor: { + usedBandwidth: 'CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth', + paraHeadHash: 'Option', + consumedGoAheadSignal: 'Option' + }, + /** + * Lookup409: cumulus_pallet_parachain_system::unincluded_segment::UsedBandwidth + **/ + CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth: { + umpMsgCount: 'u32', + umpTotalBytes: 'u32', + hrmpOutgoing: 'BTreeMap' + }, + /** + * Lookup411: cumulus_pallet_parachain_system::unincluded_segment::HrmpChannelUpdate + **/ + CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate: { + msgCount: 'u32', + totalBytes: 'u32' + }, + /** + * Lookup415: polkadot_primitives::v5::UpgradeGoAhead + **/ + PolkadotPrimitivesV5UpgradeGoAhead: { + _enum: ['Abort', 'GoAhead'] + }, + /** + * Lookup416: cumulus_pallet_parachain_system::unincluded_segment::SegmentTracker + **/ + CumulusPalletParachainSystemUnincludedSegmentSegmentTracker: { + usedBandwidth: 'CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth', + hrmpWatermark: 'Option', + consumedGoAheadSignal: 'Option' + }, + /** + * Lookup418: polkadot_primitives::v5::UpgradeRestriction + **/ + PolkadotPrimitivesV5UpgradeRestriction: { + _enum: ['Present'] + }, + /** + * Lookup419: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot + **/ + CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: { + dmqMqcHead: 'H256', + relayDispatchQueueRemainingCapacity: 'CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity', + ingressChannels: 'Vec<(u32,PolkadotPrimitivesV5AbridgedHrmpChannel)>', + egressChannels: 'Vec<(u32,PolkadotPrimitivesV5AbridgedHrmpChannel)>' + }, + /** + * Lookup420: cumulus_pallet_parachain_system::relay_state_snapshot::RelayDispatchQueueRemainingCapacity + **/ + CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity: { + remainingCount: 'u32', + remainingSize: 'u32' + }, + /** + * Lookup423: polkadot_primitives::v5::AbridgedHrmpChannel + **/ + PolkadotPrimitivesV5AbridgedHrmpChannel: { + maxCapacity: 'u32', + maxTotalSize: 'u32', + maxMessageSize: 'u32', + msgCount: 'u32', + totalSize: 'u32', + mqcHead: 'Option' + }, + /** + * Lookup424: polkadot_primitives::v5::AbridgedHostConfiguration + **/ + PolkadotPrimitivesV5AbridgedHostConfiguration: { + maxCodeSize: 'u32', + maxHeadDataSize: 'u32', + maxUpwardQueueCount: 'u32', + maxUpwardQueueSize: 'u32', + maxUpwardMessageSize: 'u32', + maxUpwardMessageNumPerCandidate: 'u32', + hrmpMaxMessageNumPerCandidate: 'u32', + validationUpgradeCooldown: 'u32', + validationUpgradeDelay: 'u32', + asyncBackingParams: 'PolkadotPrimitivesVstagingAsyncBackingParams' + }, + /** + * Lookup425: polkadot_primitives::vstaging::AsyncBackingParams + **/ + PolkadotPrimitivesVstagingAsyncBackingParams: { + maxCandidateDepth: 'u32', + allowedAncestryLen: 'u32' + }, + /** + * Lookup431: polkadot_core_primitives::OutboundHrmpMessage + **/ + PolkadotCorePrimitivesOutboundHrmpMessage: { + recipient: 'u32', + data: 'Bytes' + }, + /** + * Lookup432: cumulus_pallet_parachain_system::CodeUpgradeAuthorization + **/ + CumulusPalletParachainSystemCodeUpgradeAuthorization: { + codeHash: 'H256', + checkVersion: 'bool' + }, + /** + * Lookup433: cumulus_pallet_parachain_system::pallet::Error + **/ + CumulusPalletParachainSystemError: { + _enum: ['OverlappingUpgrades', 'ProhibitedByPolkadot', 'TooBig', 'ValidationDataNotAvailable', 'HostConfigurationNotAvailable', 'NotScheduled', 'NothingAuthorized', 'Unauthorized'] + }, + /** + * Lookup435: pallet_collator_selection::pallet::Error + **/ + PalletCollatorSelectionError: { + _enum: ['TooManyCandidates', 'Unknown', 'Permission', 'AlreadyHoldingLicense', 'NoLicense', 'AlreadyCandidate', 'NotCandidate', 'TooManyInvulnerables', 'TooFewInvulnerables', 'AlreadyInvulnerable', 'NotInvulnerable', 'NoAssociatedValidatorId', 'ValidatorNotRegistered'] + }, + /** + * Lookup439: sp_core::crypto::KeyTypeId + **/ + SpCoreCryptoKeyTypeId: '[u8;4]', + /** + * Lookup440: pallet_session::pallet::Error + **/ + PalletSessionError: { + _enum: ['InvalidProof', 'NoAssociatedValidatorId', 'DuplicatedKey', 'NoKeys', 'NoAccount'] + }, + /** + * Lookup446: pallet_balances::types::BalanceLock + **/ + PalletBalancesBalanceLock: { + id: '[u8;8]', + amount: 'u128', + reasons: 'PalletBalancesReasons' + }, + /** + * Lookup447: pallet_balances::types::Reasons + **/ + PalletBalancesReasons: { + _enum: ['Fee', 'Misc', 'All'] + }, + /** + * Lookup450: pallet_balances::types::ReserveData + **/ + PalletBalancesReserveData: { + id: '[u8;16]', + amount: 'u128' + }, + /** + * Lookup454: opal_runtime::RuntimeHoldReason + **/ + OpalRuntimeRuntimeHoldReason: { + _enum: { + __Unused0: 'Null', + __Unused1: 'Null', + __Unused2: 'Null', + __Unused3: 'Null', + __Unused4: 'Null', + __Unused5: 'Null', + __Unused6: 'Null', + __Unused7: 'Null', + __Unused8: 'Null', + __Unused9: 'Null', + __Unused10: 'Null', + __Unused11: 'Null', + __Unused12: 'Null', + __Unused13: 'Null', + __Unused14: 'Null', + __Unused15: 'Null', + __Unused16: 'Null', + __Unused17: 'Null', + __Unused18: 'Null', + __Unused19: 'Null', + __Unused20: 'Null', + __Unused21: 'Null', + __Unused22: 'Null', + CollatorSelection: 'PalletCollatorSelectionHoldReason' + } + }, + /** + * Lookup455: pallet_collator_selection::pallet::HoldReason + **/ + PalletCollatorSelectionHoldReason: { + _enum: ['LicenseBond'] + }, + /** + * Lookup458: pallet_balances::types::IdAmount + **/ + PalletBalancesIdAmount: { + id: '[u8;16]', + amount: 'u128' + }, + /** + * Lookup460: pallet_balances::pallet::Error + **/ + PalletBalancesError: { + _enum: ['VestingBalance', 'LiquidityRestrictions', 'InsufficientBalance', 'ExistentialDeposit', 'Expendability', 'ExistingVestingSchedule', 'DeadAccount', 'TooManyReserves', 'TooManyHolds', 'TooManyFreezes'] + }, + /** + * Lookup462: pallet_transaction_payment::Releases + **/ + PalletTransactionPaymentReleases: { + _enum: ['V1Ancient', 'V2'] + }, + /** + * Lookup463: pallet_treasury::Proposal + **/ + PalletTreasuryProposal: { + proposer: 'AccountId32', + value: 'u128', + beneficiary: 'AccountId32', + bond: 'u128' + }, + /** + * Lookup466: frame_support::PalletId + **/ + FrameSupportPalletId: '[u8;8]', + /** + * Lookup467: pallet_treasury::pallet::Error + **/ + PalletTreasuryError: { + _enum: ['InsufficientProposersBalance', 'InvalidIndex', 'TooManyApprovals', 'InsufficientPermission', 'ProposalNotApproved'] + }, + /** + * Lookup468: pallet_sudo::pallet::Error + **/ + PalletSudoError: { + _enum: ['RequireSudo'] + }, + /** + * Lookup470: orml_vesting::module::Error + **/ + OrmlVestingModuleError: { + _enum: ['ZeroVestingPeriod', 'ZeroVestingPeriodCount', 'InsufficientBalanceToLock', 'TooManyVestingSchedules', 'AmountLow', 'MaxVestingSchedulesExceeded'] + }, + /** + * Lookup471: orml_xtokens::module::Error + **/ + OrmlXtokensModuleError: { + _enum: ['AssetHasNoReserve', 'NotCrossChainTransfer', 'InvalidDest', 'NotCrossChainTransferableCurrency', 'UnweighableMessage', 'XcmExecutionFailed', 'CannotReanchor', 'InvalidAncestry', 'InvalidAsset', 'DestinationNotInvertible', 'BadVersion', 'DistinctReserveForAssetAndFee', 'ZeroFee', 'ZeroAmount', 'TooManyAssetsBeingSent', 'AssetIndexNonExistent', 'FeeNotEnough', 'NotSupportedMultiLocation', 'MinXcmFeeNotDefined'] + }, + /** + * Lookup474: orml_tokens::BalanceLock + **/ + OrmlTokensBalanceLock: { + id: '[u8;8]', + amount: 'u128' + }, + /** + * Lookup476: orml_tokens::AccountData + **/ + OrmlTokensAccountData: { + free: 'u128', + reserved: 'u128', + frozen: 'u128' + }, + /** + * Lookup478: orml_tokens::ReserveData + **/ + OrmlTokensReserveData: { + id: 'Null', + amount: 'u128' + }, + /** + * Lookup480: orml_tokens::module::Error + **/ + OrmlTokensModuleError: { + _enum: ['BalanceTooLow', 'AmountIntoBalanceFailed', 'LiquidityRestrictions', 'MaxLocksExceeded', 'KeepAlive', 'ExistentialDeposit', 'DeadAccount', 'TooManyReserves'] + }, + /** + * Lookup485: pallet_identity::types::RegistrarInfo + **/ + PalletIdentityRegistrarInfo: { + account: 'AccountId32', + fee: 'u128', + fields: 'PalletIdentityBitFlags' + }, + /** + * Lookup487: pallet_identity::pallet::Error + **/ + PalletIdentityError: { + _enum: ['TooManySubAccounts', 'NotFound', 'NotNamed', 'EmptyIndex', 'FeeChanged', 'NoIdentity', 'StickyJudgement', 'JudgementGiven', 'InvalidJudgement', 'InvalidIndex', 'InvalidTarget', 'TooManyFields', 'TooManyRegistrars', 'AlreadyClaimed', 'NotSub', 'NotOwned', 'JudgementForDifferentIdentity', 'JudgementPaymentFailed'] + }, + /** + * Lookup488: pallet_preimage::RequestStatus + **/ + PalletPreimageRequestStatus: { + _enum: { + Unrequested: { + deposit: '(AccountId32,u128)', + len: 'u32', + }, + Requested: { + deposit: 'Option<(AccountId32,u128)>', + count: 'u32', + len: 'Option' + } + } + }, + /** + * Lookup493: pallet_preimage::pallet::Error + **/ + PalletPreimageError: { + _enum: ['TooBig', 'AlreadyNoted', 'NotAuthorized', 'NotNoted', 'Requested', 'NotRequested'] + }, + /** + * Lookup499: pallet_democracy::types::ReferendumInfo, Balance> + **/ + PalletDemocracyReferendumInfo: { + _enum: { + Ongoing: 'PalletDemocracyReferendumStatus', + Finished: { + approved: 'bool', + end: 'u32' + } + } + }, + /** + * Lookup500: pallet_democracy::types::ReferendumStatus, Balance> + **/ + PalletDemocracyReferendumStatus: { + end: 'u32', + proposal: 'FrameSupportPreimagesBounded', + threshold: 'PalletDemocracyVoteThreshold', + delay: 'u32', + tally: 'PalletDemocracyTally' + }, + /** + * Lookup501: pallet_democracy::types::Tally + **/ + PalletDemocracyTally: { + ayes: 'u128', + nays: 'u128', + turnout: 'u128' + }, + /** + * Lookup502: pallet_democracy::vote::Voting + **/ + PalletDemocracyVoteVoting: { + _enum: { + Direct: { + votes: 'Vec<(u32,PalletDemocracyVoteAccountVote)>', + delegations: 'PalletDemocracyDelegations', + prior: 'PalletDemocracyVotePriorLock', + }, + Delegating: { + balance: 'u128', + target: 'AccountId32', + conviction: 'PalletDemocracyConviction', + delegations: 'PalletDemocracyDelegations', + prior: 'PalletDemocracyVotePriorLock' + } + } + }, + /** + * Lookup506: pallet_democracy::types::Delegations + **/ + PalletDemocracyDelegations: { + votes: 'u128', + capital: 'u128' + }, + /** + * Lookup507: pallet_democracy::vote::PriorLock + **/ + PalletDemocracyVotePriorLock: '(u32,u128)', + /** + * Lookup510: pallet_democracy::pallet::Error + **/ + PalletDemocracyError: { + _enum: ['ValueLow', 'ProposalMissing', 'AlreadyCanceled', 'DuplicateProposal', 'ProposalBlacklisted', 'NotSimpleMajority', 'InvalidHash', 'NoProposal', 'AlreadyVetoed', 'ReferendumInvalid', 'NoneWaiting', 'NotVoter', 'NoPermission', 'AlreadyDelegating', 'InsufficientFunds', 'NotDelegating', 'VotesExist', 'InstantNotAllowed', 'Nonsense', 'WrongUpperBound', 'MaxVotesReached', 'TooMany', 'VotingPeriodLow', 'PreimageNotExist'] + }, + /** + * Lookup512: pallet_collective::Votes + **/ + PalletCollectiveVotes: { + index: 'u32', + threshold: 'u32', + ayes: 'Vec', + nays: 'Vec', + end: 'u32' + }, + /** + * Lookup513: pallet_collective::pallet::Error + **/ + PalletCollectiveError: { + _enum: ['NotMember', 'DuplicateProposal', 'ProposalMissing', 'WrongIndex', 'DuplicateVote', 'AlreadyInitialized', 'TooEarly', 'TooManyProposals', 'WrongProposalWeight', 'WrongProposalLength', 'PrimeAccountNotMember'] + }, + /** + * Lookup517: pallet_membership::pallet::Error + **/ + PalletMembershipError: { + _enum: ['AlreadyMember', 'NotMember', 'TooManyMembers'] + }, + /** + * Lookup520: pallet_ranked_collective::MemberRecord + **/ + PalletRankedCollectiveMemberRecord: { + rank: 'u16' + }, + /** + * Lookup525: pallet_ranked_collective::pallet::Error + **/ + PalletRankedCollectiveError: { + _enum: ['AlreadyMember', 'NotMember', 'NotPolling', 'Ongoing', 'NoneRemaining', 'Corruption', 'RankTooLow', 'InvalidWitness', 'NoPermission'] + }, + /** + * Lookup526: pallet_referenda::types::ReferendumInfo, Balance, pallet_ranked_collective::Tally, sp_core::crypto::AccountId32, ScheduleAddress> + **/ + PalletReferendaReferendumInfo: { + _enum: { + Ongoing: 'PalletReferendaReferendumStatus', + Approved: '(u32,Option,Option)', + Rejected: '(u32,Option,Option)', + Cancelled: '(u32,Option,Option)', + TimedOut: '(u32,Option,Option)', + Killed: 'u32' + } + }, + /** + * Lookup527: pallet_referenda::types::ReferendumStatus, Balance, pallet_ranked_collective::Tally, sp_core::crypto::AccountId32, ScheduleAddress> + **/ + PalletReferendaReferendumStatus: { + track: 'u16', + origin: 'OpalRuntimeOriginCaller', + proposal: 'FrameSupportPreimagesBounded', + enactment: 'FrameSupportScheduleDispatchTime', + submitted: 'u32', + submissionDeposit: 'PalletReferendaDeposit', + decisionDeposit: 'Option', + deciding: 'Option', + tally: 'PalletRankedCollectiveTally', + inQueue: 'bool', + alarm: 'Option<(u32,(u32,u32))>' + }, + /** + * Lookup528: pallet_referenda::types::Deposit + **/ + PalletReferendaDeposit: { + who: 'AccountId32', + amount: 'u128' + }, + /** + * Lookup531: pallet_referenda::types::DecidingStatus + **/ + PalletReferendaDecidingStatus: { + since: 'u32', + confirming: 'Option' + }, + /** + * Lookup537: pallet_referenda::types::TrackInfo + **/ + PalletReferendaTrackInfo: { + name: 'Text', + maxDeciding: 'u32', + decisionDeposit: 'u128', + preparePeriod: 'u32', + decisionPeriod: 'u32', + confirmPeriod: 'u32', + minEnactmentPeriod: 'u32', + minApproval: 'PalletReferendaCurve', + minSupport: 'PalletReferendaCurve' + }, + /** + * Lookup538: pallet_referenda::types::Curve + **/ + PalletReferendaCurve: { + _enum: { + LinearDecreasing: { + length: 'Perbill', + floor: 'Perbill', + ceil: 'Perbill', + }, + SteppedDecreasing: { + begin: 'Perbill', + end: 'Perbill', + step: 'Perbill', + period: 'Perbill', + }, + Reciprocal: { + factor: 'i64', + xOffset: 'i64', + yOffset: 'i64' + } + } + }, + /** + * Lookup541: pallet_referenda::pallet::Error + **/ + PalletReferendaError: { + _enum: ['NotOngoing', 'HasDeposit', 'BadTrack', 'Full', 'QueueEmpty', 'BadReferendum', 'NothingToDo', 'NoTrack', 'Unfinished', 'NoPermission', 'NoDeposit', 'BadStatus', 'PreimageNotExist'] + }, + /** + * Lookup544: pallet_scheduler::Scheduled, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32> + **/ + PalletSchedulerScheduled: { + maybeId: 'Option<[u8;32]>', + priority: 'u8', + call: 'FrameSupportPreimagesBounded', + maybePeriodic: 'Option<(u32,u32)>', + origin: 'OpalRuntimeOriginCaller' + }, + /** + * Lookup546: pallet_scheduler::pallet::Error + **/ + PalletSchedulerError: { + _enum: ['FailedToSchedule', 'NotFound', 'TargetBlockNumberInPast', 'RescheduleNoChange', 'Named'] + }, + /** + * Lookup548: cumulus_pallet_xcmp_queue::InboundChannelDetails + **/ + CumulusPalletXcmpQueueInboundChannelDetails: { + sender: 'u32', + state: 'CumulusPalletXcmpQueueInboundState', + messageMetadata: 'Vec<(u32,PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat)>' + }, + /** + * Lookup549: cumulus_pallet_xcmp_queue::InboundState + **/ + CumulusPalletXcmpQueueInboundState: { + _enum: ['Ok', 'Suspended'] + }, + /** + * Lookup552: polkadot_parachain_primitives::primitives::XcmpMessageFormat + **/ + PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat: { + _enum: ['ConcatenatedVersionedXcm', 'ConcatenatedEncodedBlob', 'Signals'] + }, + /** + * Lookup555: cumulus_pallet_xcmp_queue::OutboundChannelDetails + **/ + CumulusPalletXcmpQueueOutboundChannelDetails: { + recipient: 'u32', + state: 'CumulusPalletXcmpQueueOutboundState', + signalsExist: 'bool', + firstIndex: 'u16', + lastIndex: 'u16' + }, + /** + * Lookup556: cumulus_pallet_xcmp_queue::OutboundState + **/ + CumulusPalletXcmpQueueOutboundState: { + _enum: ['Ok', 'Suspended'] + }, + /** + * Lookup558: cumulus_pallet_xcmp_queue::QueueConfigData + **/ + CumulusPalletXcmpQueueQueueConfigData: { + suspendThreshold: 'u32', + dropThreshold: 'u32', + resumeThreshold: 'u32', + thresholdWeight: 'SpWeightsWeightV2Weight', + weightRestrictDecay: 'SpWeightsWeightV2Weight', + xcmpMaxIndividualWeight: 'SpWeightsWeightV2Weight' + }, + /** + * Lookup560: cumulus_pallet_xcmp_queue::pallet::Error + **/ + CumulusPalletXcmpQueueError: { + _enum: ['FailedToSend', 'BadXcmOrigin', 'BadXcm', 'BadOverweightIndex', 'WeightOverLimit'] + }, + /** + * Lookup561: pallet_xcm::pallet::QueryStatus + **/ + PalletXcmQueryStatus: { + _enum: { + Pending: { + responder: 'StagingXcmVersionedMultiLocation', + maybeMatchQuerier: 'Option', + maybeNotify: 'Option<(u8,u8)>', + timeout: 'u32', + }, + VersionNotifier: { + origin: 'StagingXcmVersionedMultiLocation', + isActive: 'bool', + }, + Ready: { + response: 'StagingXcmVersionedResponse', + at: 'u32' + } + } + }, + /** + * Lookup565: staging_xcm::VersionedResponse + **/ + StagingXcmVersionedResponse: { + _enum: { + __Unused0: 'Null', + __Unused1: 'Null', + V2: 'StagingXcmV2Response', + V3: 'StagingXcmV3Response' + } + }, + /** + * Lookup571: pallet_xcm::pallet::VersionMigrationStage + **/ + PalletXcmVersionMigrationStage: { + _enum: { + MigrateSupportedVersion: 'Null', + MigrateVersionNotifiers: 'Null', + NotifyCurrentTargets: 'Option', + MigrateAndNotifyOldTargets: 'Null' + } + }, + /** + * Lookup574: staging_xcm::VersionedAssetId + **/ + StagingXcmVersionedAssetId: { + _enum: { + __Unused0: 'Null', + __Unused1: 'Null', + __Unused2: 'Null', + V3: 'StagingXcmV3MultiassetAssetId' + } + }, + /** + * Lookup575: pallet_xcm::pallet::RemoteLockedFungibleRecord + **/ + PalletXcmRemoteLockedFungibleRecord: { + amount: 'u128', + owner: 'StagingXcmVersionedMultiLocation', + locker: 'StagingXcmVersionedMultiLocation', + consumers: 'Vec<(Null,u128)>' + }, + /** + * Lookup582: pallet_xcm::pallet::Error + **/ + PalletXcmError: { + _enum: ['Unreachable', 'SendFailure', 'Filtered', 'UnweighableMessage', 'DestinationNotInvertible', 'Empty', 'CannotReanchor', 'TooManyAssets', 'InvalidOrigin', 'BadVersion', 'BadLocation', 'NoSubscription', 'AlreadySubscribed', 'InvalidAsset', 'LowBalance', 'TooManyLocks', 'AccountNotSovereign', 'FeesNotMet', 'LockNotFound', 'InUse'] + }, + /** + * Lookup583: cumulus_pallet_xcm::pallet::Error + **/ + CumulusPalletXcmError: 'Null', + /** + * Lookup584: cumulus_pallet_dmp_queue::ConfigData + **/ + CumulusPalletDmpQueueConfigData: { + maxIndividual: 'SpWeightsWeightV2Weight' + }, + /** + * Lookup585: cumulus_pallet_dmp_queue::PageIndexData + **/ + CumulusPalletDmpQueuePageIndexData: { + beginUsed: 'u32', + endUsed: 'u32', + overweightCount: 'u64' + }, + /** + * Lookup588: cumulus_pallet_dmp_queue::pallet::Error + **/ + CumulusPalletDmpQueueError: { + _enum: ['Unknown', 'OverLimit'] + }, + /** + * Lookup592: pallet_unique::pallet::Error + **/ + PalletUniqueError: { + _enum: ['CollectionDecimalPointLimitExceeded', 'EmptyArgument', 'RepartitionCalledOnNonRefungibleCollection'] + }, + /** + * Lookup593: pallet_configuration::pallet::Error + **/ + PalletConfigurationError: { + _enum: ['InconsistentConfiguration'] + }, + /** + * Lookup594: up_data_structs::Collection + **/ + UpDataStructsCollection: { + owner: 'AccountId32', + mode: 'UpDataStructsCollectionMode', + name: 'Vec', + description: 'Vec', + tokenPrefix: 'Bytes', + sponsorship: 'UpDataStructsSponsorshipStateAccountId32', + limits: 'UpDataStructsCollectionLimits', + permissions: 'UpDataStructsCollectionPermissions', + flags: '[u8;1]' + }, + /** + * Lookup595: up_data_structs::SponsorshipState + **/ + UpDataStructsSponsorshipStateAccountId32: { + _enum: { + Disabled: 'Null', + Unconfirmed: 'AccountId32', + Confirmed: 'AccountId32' + } + }, + /** + * Lookup596: up_data_structs::Properties + **/ + UpDataStructsProperties: { + map: 'UpDataStructsPropertiesMapBoundedVec', + consumedSpace: 'u32', + reserved: 'u32' + }, + /** + * Lookup597: up_data_structs::PropertiesMap> + **/ + UpDataStructsPropertiesMapBoundedVec: 'BTreeMap', + /** + * Lookup602: up_data_structs::PropertiesMap + **/ + UpDataStructsPropertiesMapPropertyPermission: 'BTreeMap', + /** + * Lookup609: up_data_structs::CollectionStats + **/ + UpDataStructsCollectionStats: { + created: 'u32', + destroyed: 'u32', + alive: 'u32' + }, + /** + * Lookup610: up_data_structs::TokenChild + **/ + UpDataStructsTokenChild: { + token: 'u32', + collection: 'u32' + }, + /** + * Lookup611: PhantomType::up_data_structs + **/ + PhantomTypeUpDataStructs: '[(UpDataStructsTokenData,UpDataStructsRpcCollection,UpPovEstimateRpcPovInfo);0]', + /** + * Lookup613: up_data_structs::TokenData> + **/ + UpDataStructsTokenData: { + properties: 'Vec', + owner: 'Option', + pieces: 'u128' + }, + /** + * Lookup614: up_data_structs::RpcCollection + **/ + UpDataStructsRpcCollection: { + owner: 'AccountId32', + mode: 'UpDataStructsCollectionMode', + name: 'Vec', + description: 'Vec', + tokenPrefix: 'Bytes', + sponsorship: 'UpDataStructsSponsorshipStateAccountId32', + limits: 'UpDataStructsCollectionLimits', + permissions: 'UpDataStructsCollectionPermissions', + tokenPropertyPermissions: 'Vec', + properties: 'Vec', + readOnly: 'bool', + flags: 'UpDataStructsRpcCollectionFlags' + }, + /** + * Lookup615: up_data_structs::RpcCollectionFlags + **/ + UpDataStructsRpcCollectionFlags: { + foreign: 'bool', + erc721metadata: 'bool' + }, + /** + * Lookup616: up_pov_estimate_rpc::PovInfo + **/ + UpPovEstimateRpcPovInfo: { + proofSize: 'u64', + compactProofSize: 'u64', + compressedProofSize: 'u64', + results: 'Vec, SpRuntimeTransactionValidityTransactionValidityError>>', + keyValues: 'Vec' + }, + /** + * Lookup619: sp_runtime::transaction_validity::TransactionValidityError + **/ + SpRuntimeTransactionValidityTransactionValidityError: { + _enum: { + Invalid: 'SpRuntimeTransactionValidityInvalidTransaction', + Unknown: 'SpRuntimeTransactionValidityUnknownTransaction' + } + }, + /** + * Lookup620: sp_runtime::transaction_validity::InvalidTransaction + **/ + SpRuntimeTransactionValidityInvalidTransaction: { + _enum: { + Call: 'Null', + Payment: 'Null', + Future: 'Null', + Stale: 'Null', + BadProof: 'Null', + AncientBirthBlock: 'Null', + ExhaustsResources: 'Null', + Custom: 'u8', + BadMandatory: 'Null', + MandatoryValidation: 'Null', + BadSigner: 'Null' + } + }, + /** + * Lookup621: sp_runtime::transaction_validity::UnknownTransaction + **/ + SpRuntimeTransactionValidityUnknownTransaction: { + _enum: { + CannotLookup: 'Null', + NoUnsignedValidator: 'Null', + Custom: 'u8' + } + }, + /** + * Lookup623: up_pov_estimate_rpc::TrieKeyValue + **/ + UpPovEstimateRpcTrieKeyValue: { + key: 'Bytes', + value: 'Bytes' + }, + /** + * Lookup625: pallet_common::pallet::Error + **/ + PalletCommonError: { + _enum: ['CollectionNotFound', 'MustBeTokenOwner', 'NoPermission', 'CantDestroyNotEmptyCollection', 'PublicMintingNotAllowed', 'AddressNotInAllowlist', 'CollectionNameLimitExceeded', 'CollectionDescriptionLimitExceeded', 'CollectionTokenPrefixLimitExceeded', 'TotalCollectionsLimitExceeded', 'CollectionAdminCountExceeded', 'CollectionLimitBoundsExceeded', 'OwnerPermissionsCantBeReverted', 'TransferNotAllowed', 'AccountTokenLimitExceeded', 'CollectionTokenLimitExceeded', 'MetadataFlagFrozen', 'TokenNotFound', 'TokenValueTooLow', 'ApprovedValueTooLow', 'CantApproveMoreThanOwned', 'AddressIsNotEthMirror', 'AddressIsZero', 'UnsupportedOperation', 'NotSufficientFounds', 'UserIsNotAllowedToNest', 'SourceCollectionIsNotAllowedToNest', 'CollectionFieldSizeExceeded', 'NoSpaceForProperty', 'PropertyLimitReached', 'PropertyKeyIsTooLong', 'InvalidCharacterInPropertyKey', 'EmptyPropertyKey', 'CollectionIsExternal', 'CollectionIsInternal', 'ConfirmSponsorshipFail', 'UserIsNotCollectionAdmin', 'FungibleItemsHaveNoId'] + }, + /** + * Lookup627: pallet_fungible::pallet::Error + **/ + PalletFungibleError: { + _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsDontHaveData', 'FungibleDisallowsNesting', 'SettingPropertiesNotAllowed', 'SettingAllowanceForAllNotAllowed', 'FungibleTokensAreAlwaysValid'] + }, + /** + * Lookup632: pallet_refungible::pallet::Error + **/ + PalletRefungibleError: { + _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces', 'RepartitionWhileNotOwningAllPieces', 'RefungibleDisallowsNesting', 'SettingPropertiesNotAllowed'] + }, + /** + * Lookup633: pallet_nonfungible::ItemData> + **/ + PalletNonfungibleItemData: { + owner: 'PalletEvmAccountBasicCrossAccountIdRepr' + }, + /** + * Lookup635: up_data_structs::PropertyScope + **/ + UpDataStructsPropertyScope: { + _enum: ['None', 'Rmrk'] + }, + /** + * Lookup638: pallet_nonfungible::pallet::Error + **/ + PalletNonfungibleError: { + _enum: ['NotNonfungibleDataUsedToMintFungibleCollectionToken', 'NonfungibleItemsHaveNoAmount', 'CantBurnNftWithChildren'] + }, + /** + * Lookup639: pallet_structure::pallet::Error + **/ + PalletStructureError: { + _enum: ['OuroborosDetected', 'DepthLimit', 'BreadthLimit', 'TokenNotFound', 'CantNestTokenUnderCollection'] + }, + /** + * Lookup644: pallet_app_promotion::pallet::Error + **/ + PalletAppPromotionError: { + _enum: ['AdminNotSet', 'NoPermission', 'NotSufficientFunds', 'PendingForBlockOverflow', 'SponsorNotSet', 'InsufficientStakedBalance', 'InconsistencyState'] + }, + /** + * Lookup645: pallet_foreign_assets::module::Error + **/ + PalletForeignAssetsModuleError: { + _enum: ['BadLocation', 'MultiLocationExisted', 'AssetIdNotExists', 'AssetIdExisted'] + }, + /** + * Lookup646: pallet_evm::CodeMetadata + **/ + PalletEvmCodeMetadata: { + _alias: { + size_: 'size', + hash_: 'hash' + }, + size_: 'u64', + hash_: 'H256' + }, + /** + * Lookup648: pallet_evm::pallet::Error + **/ + PalletEvmError: { + _enum: ['BalanceLow', 'FeeOverflow', 'PaymentOverflow', 'WithdrawFailed', 'GasPriceTooLow', 'InvalidNonce', 'GasLimitTooLow', 'GasLimitTooHigh', 'Undefined', 'Reentrancy', 'TransactionMustComeFromEOA'] + }, + /** + * Lookup651: fp_rpc::TransactionStatus + **/ + FpRpcTransactionStatus: { + transactionHash: 'H256', + transactionIndex: 'u32', + from: 'H160', + to: 'Option', + contractAddress: 'Option', + logs: 'Vec', + logsBloom: 'EthbloomBloom' + }, + /** + * Lookup653: ethbloom::Bloom + **/ + EthbloomBloom: '[u8;256]', + /** + * Lookup655: ethereum::receipt::ReceiptV3 + **/ + EthereumReceiptReceiptV3: { + _enum: { + Legacy: 'EthereumReceiptEip658ReceiptData', + EIP2930: 'EthereumReceiptEip658ReceiptData', + EIP1559: 'EthereumReceiptEip658ReceiptData' + } + }, + /** + * Lookup656: ethereum::receipt::EIP658ReceiptData + **/ + EthereumReceiptEip658ReceiptData: { + statusCode: 'u8', + usedGas: 'U256', + logsBloom: 'EthbloomBloom', + logs: 'Vec' + }, + /** + * Lookup657: ethereum::block::Block + **/ + EthereumBlock: { + header: 'EthereumHeader', + transactions: 'Vec', + ommers: 'Vec' + }, + /** + * Lookup658: ethereum::header::Header + **/ + EthereumHeader: { + parentHash: 'H256', + ommersHash: 'H256', + beneficiary: 'H160', + stateRoot: 'H256', + transactionsRoot: 'H256', + receiptsRoot: 'H256', + logsBloom: 'EthbloomBloom', + difficulty: 'U256', + number: 'U256', + gasLimit: 'U256', + gasUsed: 'U256', + timestamp: 'u64', + extraData: 'Bytes', + mixHash: 'H256', + nonce: 'EthereumTypesHashH64' + }, + /** + * Lookup659: ethereum_types::hash::H64 + **/ + EthereumTypesHashH64: '[u8;8]', + /** + * Lookup664: pallet_ethereum::pallet::Error + **/ + PalletEthereumError: { + _enum: ['InvalidSignature', 'PreLogExists'] + }, + /** + * Lookup665: pallet_evm_coder_substrate::pallet::Error + **/ + PalletEvmCoderSubstrateError: { + _enum: ['OutOfGas', 'OutOfFund'] + }, + /** + * Lookup666: up_data_structs::SponsorshipState> + **/ + UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: { + _enum: { + Disabled: 'Null', + Unconfirmed: 'PalletEvmAccountBasicCrossAccountIdRepr', + Confirmed: 'PalletEvmAccountBasicCrossAccountIdRepr' + } + }, + /** + * Lookup667: pallet_evm_contract_helpers::SponsoringModeT + **/ + PalletEvmContractHelpersSponsoringModeT: { + _enum: ['Disabled', 'Allowlisted', 'Generous'] + }, + /** + * Lookup673: pallet_evm_contract_helpers::pallet::Error + **/ + PalletEvmContractHelpersError: { + _enum: ['NoPermission', 'NoPendingSponsor', 'TooManyMethodsHaveSponsoredLimit'] + }, + /** + * Lookup674: pallet_evm_migration::pallet::Error + **/ + PalletEvmMigrationError: { + _enum: ['AccountNotEmpty', 'AccountIsNotMigrating', 'BadEvent'] + }, + /** + * Lookup675: pallet_maintenance::pallet::Error + **/ + PalletMaintenanceError: 'Null', + /** + * Lookup676: pallet_utility::pallet::Error + **/ + PalletUtilityError: { + _enum: ['TooManyCalls'] + }, + /** + * Lookup677: pallet_test_utils::pallet::Error + **/ + PalletTestUtilsError: { + _enum: ['TestPalletDisabled', 'TriggerRollback'] + }, + /** + * Lookup679: sp_runtime::MultiSignature + **/ + SpRuntimeMultiSignature: { + _enum: { + Ed25519: 'SpCoreEd25519Signature', + Sr25519: 'SpCoreSr25519Signature', + Ecdsa: 'SpCoreEcdsaSignature' + } + }, + /** + * Lookup680: sp_core::ed25519::Signature + **/ + SpCoreEd25519Signature: '[u8;64]', + /** + * Lookup682: sp_core::sr25519::Signature + **/ + SpCoreSr25519Signature: '[u8;64]', + /** + * Lookup683: sp_core::ecdsa::Signature + **/ + SpCoreEcdsaSignature: '[u8;65]', + /** + * Lookup686: frame_system::extensions::check_spec_version::CheckSpecVersion + **/ + FrameSystemExtensionsCheckSpecVersion: 'Null', + /** + * Lookup687: frame_system::extensions::check_tx_version::CheckTxVersion + **/ + FrameSystemExtensionsCheckTxVersion: 'Null', + /** + * Lookup688: frame_system::extensions::check_genesis::CheckGenesis + **/ + FrameSystemExtensionsCheckGenesis: 'Null', + /** + * Lookup691: frame_system::extensions::check_nonce::CheckNonce + **/ + FrameSystemExtensionsCheckNonce: 'Compact', + /** + * Lookup692: frame_system::extensions::check_weight::CheckWeight + **/ + FrameSystemExtensionsCheckWeight: 'Null', + /** + * Lookup693: opal_runtime::runtime_common::maintenance::CheckMaintenance + **/ + OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: 'Null', + /** + * Lookup694: opal_runtime::runtime_common::identity::DisableIdentityCalls + **/ + OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls: 'Null', + /** + * Lookup695: pallet_template_transaction_payment::ChargeTransactionPayment + **/ + PalletTemplateTransactionPaymentChargeTransactionPayment: 'Compact', + /** + * Lookup696: opal_runtime::Runtime + **/ + OpalRuntimeRuntime: 'Null', + /** + * Lookup697: pallet_ethereum::FakeTransactionFinalizer + **/ + PalletEthereumFakeTransactionFinalizer: 'Null' +}; --- a/js-packages/types/package.json +++ b/js-packages/types/package.json @@ -9,8 +9,7 @@ "type": "module", "version": "1.0.0", "main": "index.js", - "dependencies": { - "rxjs": "^7.8.1", - "tslib": "^2.6.2" + "devDependencies": { + "@polkadot/typegen": "^10.10.1" } } --- /dev/null +++ b/js-packages/types/povinfo/definitions.ts @@ -0,0 +1,40 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +type RpcParam = { + name: string; + type: string; + isOptional?: true; +}; + +const atParam = {name: 'at', type: 'Hash', isOptional: true}; + +const fun = (description: string, params: RpcParam[], type: string) => ({ + description, + params: [...params, atParam], + type, +}); + +export default { + types: {}, + rpc: { + estimateExtrinsicPoV: fun( + 'Estimate PoV size of encoded signed extrinsics', + [{name: 'encodedXt', type: 'Vec'}], + 'UpPovEstimateRpcPovInfo', + ), + }, +}; --- /dev/null +++ b/js-packages/types/povinfo/index.ts @@ -0,0 +1,4 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +export * from './types.js'; --- /dev/null +++ b/js-packages/types/povinfo/types.ts @@ -0,0 +1,4 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +export type PHANTOM_POVINFO = 'povinfo'; --- a/js-packages/types/src/.gitignore +++ /dev/null @@ -1 +0,0 @@ -metadata.json --- a/js-packages/types/src/appPromotion/definitions.ts +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -type RpcParam = { - name: string; - type: string; - isOptional?: true; -}; - -const CROSS_ACCOUNT_ID_TYPE = 'PalletEvmAccountBasicCrossAccountIdRepr'; - -const crossAccountParam = (name = 'account') => ({name, type: CROSS_ACCOUNT_ID_TYPE}); -const atParam = {name: 'at', type: 'Hash', isOptional: true}; - -const fun = (description: string, params: RpcParam[], type: string) => ({ - description, - params: [...params, atParam], - type, -}); - -export default { - types: {}, - rpc: { - totalStaked: fun( - 'Returns the total amount of staked tokens', - [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}], - 'u128', - ), - totalStakedPerBlock: fun( - 'Returns the total amount of staked tokens per block when staked', - [crossAccountParam('staker')], - 'Vec<(u32, u128)>', - ), - pendingUnstake: fun( - 'Returns the total amount of unstaked tokens', - [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}], - 'u128', - ), - pendingUnstakePerBlock: fun( - 'Returns the total amount of unstaked tokens per block', - [crossAccountParam('staker')], - 'Vec<(u32, u128)>', - ), - }, -}; --- a/js-packages/types/src/appPromotion/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -export * from './types.js'; --- a/js-packages/types/src/appPromotion/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -export type PHANTOM_APPPROMOTION = 'appPromotion'; --- a/js-packages/types/src/augment-api-consts.ts +++ /dev/null @@ -1,552 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-chain`, do not edit -/* eslint-disable */ - -// import type lookup before we augment - in some environments -// this is required to allow for ambient/previous definitions -import '@polkadot/api-base/types/consts'; - -import type { ApiTypes, AugmentedConst } from '@polkadot/api-base/types'; -import type { Option, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; -import type { Codec, ITuple } from '@polkadot/types-codec/types'; -import type { H160, Perbill, Permill } from '@polkadot/types/interfaces/runtime'; -import type { FrameSupportPalletId, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, PalletReferendaTrackInfo, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, UpDataStructsCollectionLimits } from '@polkadot/types/lookup'; - -export type __AugmentedConst = AugmentedConst; - -declare module '@polkadot/api-base/types/consts' { - interface AugmentedConsts { - appPromotion: { - /** - * Freeze identifier used by the pallet - **/ - freezeIdentifier: U8aFixed & AugmentedConst; - /** - * Rate of return for interval in blocks defined in `RecalculationInterval`. - **/ - intervalIncome: Perbill & AugmentedConst; - /** - * Decimals for the `Currency`. - **/ - nominal: u128 & AugmentedConst; - /** - * The app's pallet id, used for deriving its sovereign account address. - **/ - palletId: FrameSupportPalletId & AugmentedConst; - /** - * In parachain blocks. - **/ - pendingInterval: u32 & AugmentedConst; - /** - * In relay blocks. - **/ - recalculationInterval: u32 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - balances: { - /** - * The minimum amount required to keep an account open. MUST BE GREATER THAN ZERO! - * - * If you *really* need it to be zero, you can enable the feature `insecure_zero_ed` for - * this pallet. However, you do so at your own risk: this will open up a major DoS vector. - * In case you have multiple sources of provider references, you may also get unexpected - * behaviour if you set this to zero. - * - * Bottom line: Do yourself a favour and make it at least one! - **/ - existentialDeposit: u128 & AugmentedConst; - /** - * The maximum number of individual freeze locks that can exist on an account at any time. - **/ - maxFreezes: u32 & AugmentedConst; - /** - * The maximum number of holds that can exist on an account at any time. - **/ - maxHolds: u32 & AugmentedConst; - /** - * The maximum number of locks that should exist on an account. - * Not strictly enforced, but used for weight estimation. - **/ - maxLocks: u32 & AugmentedConst; - /** - * The maximum number of named reserves that can exist on an account. - **/ - maxReserves: u32 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - common: { - /** - * Maximum admins per collection. - **/ - collectionAdminsLimit: u32 & AugmentedConst; - /** - * Set price to create a collection. - **/ - collectionCreationPrice: u128 & AugmentedConst; - /** - * Address under which the CollectionHelper contract would be available. - **/ - contractAddress: H160 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - configuration: { - appPromotionDailyRate: Perbill & AugmentedConst; - dayRelayBlocks: u32 & AugmentedConst; - defaultCollatorSelectionKickThreshold: u32 & AugmentedConst; - defaultCollatorSelectionLicenseBond: u128 & AugmentedConst; - defaultCollatorSelectionMaxCollators: u32 & AugmentedConst; - defaultMinGasPrice: u64 & AugmentedConst; - defaultWeightToFeeCoefficient: u64 & AugmentedConst; - maxXcmAllowedLocations: u32 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - council: { - /** - * The maximum weight of a dispatch call that can be proposed and executed. - **/ - maxProposalWeight: SpWeightsWeightV2Weight & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - democracy: { - /** - * Period in blocks where an external proposal may not be re-submitted after being vetoed. - **/ - cooloffPeriod: u32 & AugmentedConst; - /** - * The period between a proposal being approved and enacted. - * - * It should generally be a little more than the unstake period to ensure that - * voting stakers have an opportunity to remove themselves from the system in the case - * where they are on the losing side of a vote. - **/ - enactmentPeriod: u32 & AugmentedConst; - /** - * Minimum voting period allowed for a fast-track referendum. - **/ - fastTrackVotingPeriod: u32 & AugmentedConst; - /** - * Indicator for whether an emergency origin is even allowed to happen. Some chains may - * want to set this permanently to `false`, others may want to condition it on things such - * as an upgrade having happened recently. - **/ - instantAllowed: bool & AugmentedConst; - /** - * How often (in blocks) new public referenda are launched. - **/ - launchPeriod: u32 & AugmentedConst; - /** - * The maximum number of items which can be blacklisted. - **/ - maxBlacklisted: u32 & AugmentedConst; - /** - * The maximum number of deposits a public proposal may have at any time. - **/ - maxDeposits: u32 & AugmentedConst; - /** - * The maximum number of public proposals that can exist at any time. - **/ - maxProposals: u32 & AugmentedConst; - /** - * The maximum number of votes for an account. - * - * Also used to compute weight, an overly big value can - * lead to extrinsic with very big weight: see `delegate` for instance. - **/ - maxVotes: u32 & AugmentedConst; - /** - * The minimum amount to be used as a deposit for a public referendum proposal. - **/ - minimumDeposit: u128 & AugmentedConst; - /** - * The minimum period of vote locking. - * - * It should be no shorter than enactment period to ensure that in the case of an approval, - * those successful voters are locked into the consequences that their votes entail. - **/ - voteLockingPeriod: u32 & AugmentedConst; - /** - * How often (in blocks) to check for new votes. - **/ - votingPeriod: u32 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - evmContractHelpers: { - /** - * Address, under which magic contract will be available - **/ - contractAddress: H160 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - fellowshipReferenda: { - /** - * Quantization level for the referendum wakeup scheduler. A higher number will result in - * fewer storage reads/writes needed for smaller voters, but also result in delays to the - * automatic referendum status changes. Explicit servicing instructions are unaffected. - **/ - alarmInterval: u32 & AugmentedConst; - /** - * Maximum size of the referendum queue for a single track. - **/ - maxQueued: u32 & AugmentedConst; - /** - * The minimum amount to be used as a deposit for a public referendum proposal. - **/ - submissionDeposit: u128 & AugmentedConst; - /** - * Information concerning the different referendum tracks. - **/ - tracks: Vec> & AugmentedConst; - /** - * The number of blocks after submission that a referendum must begin being decided by. - * Once this passes, then anyone may cancel the referendum. - **/ - undecidingTimeout: u32 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - identity: { - /** - * The amount held on deposit for a registered identity - **/ - basicDeposit: u128 & AugmentedConst; - /** - * The amount held on deposit per additional field for a registered identity. - **/ - fieldDeposit: u128 & AugmentedConst; - /** - * Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O - * required to access an identity, but can be pretty high. - **/ - maxAdditionalFields: u32 & AugmentedConst; - /** - * Maxmimum number of registrars allowed in the system. Needed to bound the complexity - * of, e.g., updating judgements. - **/ - maxRegistrars: u32 & AugmentedConst; - /** - * The maximum number of sub-accounts allowed per identified account. - **/ - maxSubAccounts: u32 & AugmentedConst; - /** - * The amount held on deposit for a registered subaccount. This should account for the fact - * that one storage item's value will increase by the size of an account ID, and there will - * be another trie item whose value is the size of an account ID plus 32 bytes. - **/ - subAccountDeposit: u128 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - inflation: { - /** - * Number of blocks that pass between treasury balance updates due to inflation - **/ - inflationBlockInterval: u32 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - scheduler: { - /** - * The maximum weight that may be scheduled per block for any dispatchables. - **/ - maximumWeight: SpWeightsWeightV2Weight & AugmentedConst; - /** - * The maximum number of scheduled calls in the queue for a single block. - * - * NOTE: - * + Dependent pallets' benchmarks might require a higher limit for the setting. Set a - * higher limit under `runtime-benchmarks` feature. - **/ - maxScheduledPerBlock: u32 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - stateTrieMigration: { - /** - * Maximal number of bytes that a key can have. - * - * FRAME itself does not limit the key length. - * The concrete value must therefore depend on your storage usage. - * A [`frame_support::storage::StorageNMap`] for example can have an arbitrary number of - * keys which are then hashed and concatenated, resulting in arbitrarily long keys. - * - * Use the *state migration RPC* to retrieve the length of the longest key in your - * storage: - * - * The migration will halt with a `Halted` event if this value is too small. - * Since there is no real penalty from over-estimating, it is advised to use a large - * value. The default is 512 byte. - * - * Some key lengths for reference: - * - [`frame_support::storage::StorageValue`]: 32 byte - * - [`frame_support::storage::StorageMap`]: 64 byte - * - [`frame_support::storage::StorageDoubleMap`]: 96 byte - * - * For more info see - * - **/ - maxKeyLen: u32 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - system: { - /** - * Maximum number of block number to block hash mappings to keep (oldest pruned first). - **/ - blockHashCount: u32 & AugmentedConst; - /** - * The maximum length of a block (in bytes). - **/ - blockLength: FrameSystemLimitsBlockLength & AugmentedConst; - /** - * Block & extrinsics weights: base values and limits. - **/ - blockWeights: FrameSystemLimitsBlockWeights & AugmentedConst; - /** - * The weight of runtime database operations the runtime can invoke. - **/ - dbWeight: SpWeightsRuntimeDbWeight & AugmentedConst; - /** - * The designated SS58 prefix of this chain. - * - * This replaces the "ss58Format" property declared in the chain spec. Reason is - * that the runtime should know about the prefix in order to make use of it as - * an identifier of the chain. - **/ - ss58Prefix: u16 & AugmentedConst; - /** - * Get the chain's current version. - **/ - version: SpVersionRuntimeVersion & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - technicalCommittee: { - /** - * The maximum weight of a dispatch call that can be proposed and executed. - **/ - maxProposalWeight: SpWeightsWeightV2Weight & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - timestamp: { - /** - * The minimum period between blocks. Beware that this is different to the *expected* - * period that the block production apparatus provides. Your chosen consensus system will - * generally work with this to determine a sensible block time. e.g. For Aura, it will be - * double this period on default settings. - **/ - minimumPeriod: u64 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - tokens: { - maxLocks: u32 & AugmentedConst; - /** - * The maximum number of named reserves that can exist on an account. - **/ - maxReserves: u32 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - transactionPayment: { - /** - * A fee mulitplier for `Operational` extrinsics to compute "virtual tip" to boost their - * `priority` - * - * This value is multipled by the `final_fee` to obtain a "virtual tip" that is later - * added to a tip component in regular `priority` calculations. - * It means that a `Normal` transaction can front-run a similarly-sized `Operational` - * extrinsic (with no tip), by including a tip value greater than the virtual tip. - * - * ```rust,ignore - * // For `Normal` - * let priority = priority_calc(tip); - * - * // For `Operational` - * let virtual_tip = (inclusion_fee + tip) * OperationalFeeMultiplier; - * let priority = priority_calc(tip + virtual_tip); - * ``` - * - * Note that since we use `final_fee` the multiplier applies also to the regular `tip` - * sent with the transaction. So, not only does the transaction get a priority bump based - * on the `inclusion_fee`, but we also amplify the impact of tips applied to `Operational` - * transactions. - **/ - operationalFeeMultiplier: u8 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - treasury: { - /** - * Percentage of spare funds (if any) that are burnt per spend period. - **/ - burn: Permill & AugmentedConst; - /** - * The maximum number of approvals that can wait in the spending queue. - * - * NOTE: This parameter is also used within the Bounties Pallet extension if enabled. - **/ - maxApprovals: u32 & AugmentedConst; - /** - * The treasury's pallet id, used for deriving its sovereign account ID. - **/ - palletId: FrameSupportPalletId & AugmentedConst; - /** - * Fraction of a proposal's value that should be bonded in order to place the proposal. - * An accepted proposal gets these back. A rejected proposal does not. - **/ - proposalBond: Permill & AugmentedConst; - /** - * Maximum amount of funds that should be placed in a deposit for making a proposal. - **/ - proposalBondMaximum: Option & AugmentedConst; - /** - * Minimum amount of funds that should be placed in a deposit for making a proposal. - **/ - proposalBondMinimum: u128 & AugmentedConst; - /** - * Period between successive spends. - **/ - spendPeriod: u32 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - unique: { - /** - * Maximum admins per collection. - **/ - collectionAdminsLimit: u32 & AugmentedConst; - /** - * Default FT collection limit. - **/ - ftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst; - /** - * Maximal length of a collection description. - **/ - maxCollectionDescriptionLength: u32 & AugmentedConst; - /** - * Maximal length of a collection name. - **/ - maxCollectionNameLength: u32 & AugmentedConst; - /** - * Maximum size for all collection properties. - **/ - maxCollectionPropertiesSize: u32 & AugmentedConst; - /** - * A maximum number of token properties. - **/ - maxPropertiesPerItem: u32 & AugmentedConst; - /** - * Maximal length of a property key. - **/ - maxPropertyKeyLength: u32 & AugmentedConst; - /** - * Maximal length of a property value. - **/ - maxPropertyValueLength: u32 & AugmentedConst; - /** - * Maximal length of a token prefix. - **/ - maxTokenPrefixLength: u32 & AugmentedConst; - /** - * Maximum size of all token properties. - **/ - maxTokenPropertiesSize: u32 & AugmentedConst; - /** - * A maximum number of levels of depth in the token nesting tree. - **/ - nestingBudget: u32 & AugmentedConst; - /** - * Default NFT collection limit. - **/ - nftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst; - /** - * Default RFT collection limit. - **/ - rftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - utility: { - /** - * The limit on the number of batched calls. - **/ - batchedCallsLimit: u32 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - vesting: { - /** - * The minimum amount transferred to call `vested_transfer`. - **/ - minVestedTransfer: u128 & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - xTokens: { - /** - * Base XCM weight. - * - * The actually weight for an XCM message is `T::BaseXcmWeight + - * T::Weigher::weight(&msg)`. - **/ - baseXcmWeight: SpWeightsWeightV2Weight & AugmentedConst; - /** - * Self chain location. - **/ - selfLocation: StagingXcmV3MultiLocation & AugmentedConst; - /** - * Generic const - **/ - [key: string]: Codec; - }; - } // AugmentedConsts -} // declare module --- a/js-packages/types/src/augment-api-errors.ts +++ /dev/null @@ -1,1521 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-chain`, do not edit -/* eslint-disable */ - -// import type lookup before we augment - in some environments -// this is required to allow for ambient/previous definitions -import '@polkadot/api-base/types/errors'; - -import type { ApiTypes, AugmentedError } from '@polkadot/api-base/types'; - -export type __AugmentedError = AugmentedError; - -declare module '@polkadot/api-base/types/errors' { - interface AugmentedErrors { - appPromotion: { - /** - * Error due to action requiring admin to be set. - **/ - AdminNotSet: AugmentedError; - /** - * Errors caused by incorrect state of a staker in context of the pallet. - **/ - InconsistencyState: AugmentedError; - /** - * Errors caused by insufficient staked balance. - **/ - InsufficientStakedBalance: AugmentedError; - /** - * No permission to perform an action. - **/ - NoPermission: AugmentedError; - /** - * Insufficient funds to perform an action. - **/ - NotSufficientFunds: AugmentedError; - /** - * Occurs when a pending unstake cannot be added in this block. PENDING_LIMIT_PER_BLOCK` limits exceeded. - **/ - PendingForBlockOverflow: AugmentedError; - /** - * The error is due to the fact that the collection/contract must already be sponsored in order to perform the action. - **/ - SponsorNotSet: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - balances: { - /** - * Beneficiary account must pre-exist. - **/ - DeadAccount: AugmentedError; - /** - * Value too low to create account due to existential deposit. - **/ - ExistentialDeposit: AugmentedError; - /** - * A vesting schedule already exists for this account. - **/ - ExistingVestingSchedule: AugmentedError; - /** - * Transfer/payment would kill account. - **/ - Expendability: AugmentedError; - /** - * Balance too low to send value. - **/ - InsufficientBalance: AugmentedError; - /** - * Account liquidity restrictions prevent withdrawal. - **/ - LiquidityRestrictions: AugmentedError; - /** - * Number of freezes exceed `MaxFreezes`. - **/ - TooManyFreezes: AugmentedError; - /** - * Number of holds exceed `MaxHolds`. - **/ - TooManyHolds: AugmentedError; - /** - * Number of named reserves exceed `MaxReserves`. - **/ - TooManyReserves: AugmentedError; - /** - * Vesting balance too high to send value. - **/ - VestingBalance: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - collatorSelection: { - /** - * User is already a candidate - **/ - AlreadyCandidate: AugmentedError; - /** - * User already holds license to collate - **/ - AlreadyHoldingLicense: AugmentedError; - /** - * User is already an Invulnerable - **/ - AlreadyInvulnerable: AugmentedError; - /** - * Account has no associated validator ID - **/ - NoAssociatedValidatorId: AugmentedError; - /** - * User does not hold a license to collate - **/ - NoLicense: AugmentedError; - /** - * User is not a candidate - **/ - NotCandidate: AugmentedError; - /** - * User is not an Invulnerable - **/ - NotInvulnerable: AugmentedError; - /** - * Permission issue - **/ - Permission: AugmentedError; - /** - * Too few invulnerables - **/ - TooFewInvulnerables: AugmentedError; - /** - * Too many candidates - **/ - TooManyCandidates: AugmentedError; - /** - * Too many invulnerables - **/ - TooManyInvulnerables: AugmentedError; - /** - * Unknown error - **/ - Unknown: AugmentedError; - /** - * Validator ID is not yet registered - **/ - ValidatorNotRegistered: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - common: { - /** - * Account token limit exceeded per collection - **/ - AccountTokenLimitExceeded: AugmentedError; - /** - * Only spending from eth mirror could be approved - **/ - AddressIsNotEthMirror: AugmentedError; - /** - * Can't transfer tokens to ethereum zero address - **/ - AddressIsZero: AugmentedError; - /** - * Address is not in allow list. - **/ - AddressNotInAllowlist: AugmentedError; - /** - * Requested value is more than the approved - **/ - ApprovedValueTooLow: AugmentedError; - /** - * Tried to approve more than owned - **/ - CantApproveMoreThanOwned: AugmentedError; - /** - * Destroying only empty collections is allowed - **/ - CantDestroyNotEmptyCollection: AugmentedError; - /** - * Exceeded max admin count - **/ - CollectionAdminCountExceeded: AugmentedError; - /** - * Collection description can not be longer than 255 char. - **/ - CollectionDescriptionLimitExceeded: AugmentedError; - /** - * Tried to store more data than allowed in collection field - **/ - CollectionFieldSizeExceeded: AugmentedError; - /** - * Tried to access an external collection with an internal API - **/ - CollectionIsExternal: AugmentedError; - /** - * Tried to access an internal collection with an external API - **/ - CollectionIsInternal: AugmentedError; - /** - * Collection limit bounds per collection exceeded - **/ - CollectionLimitBoundsExceeded: AugmentedError; - /** - * Collection name can not be longer than 63 char. - **/ - CollectionNameLimitExceeded: AugmentedError; - /** - * This collection does not exist. - **/ - CollectionNotFound: AugmentedError; - /** - * Collection token limit exceeded - **/ - CollectionTokenLimitExceeded: AugmentedError; - /** - * Token prefix can not be longer than 15 char. - **/ - CollectionTokenPrefixLimitExceeded: AugmentedError; - /** - * This address is not set as sponsor, use setCollectionSponsor first. - **/ - ConfirmSponsorshipFail: AugmentedError; - /** - * Empty property keys are forbidden - **/ - EmptyPropertyKey: AugmentedError; - /** - * Fungible tokens hold no ID, and the default value of TokenId for a fungible collection is 0. - **/ - FungibleItemsHaveNoId: AugmentedError; - /** - * Only ASCII letters, digits, and symbols `_`, `-`, and `.` are allowed - **/ - InvalidCharacterInPropertyKey: AugmentedError; - /** - * Metadata flag frozen - **/ - MetadataFlagFrozen: AugmentedError; - /** - * Sender parameter and item owner must be equal. - **/ - MustBeTokenOwner: AugmentedError; - /** - * No permission to perform action - **/ - NoPermission: AugmentedError; - /** - * Tried to store more property data than allowed - **/ - NoSpaceForProperty: AugmentedError; - /** - * Insufficient funds to perform an action - **/ - NotSufficientFounds: AugmentedError; - /** - * Tried to enable permissions which are only permitted to be disabled - **/ - OwnerPermissionsCantBeReverted: AugmentedError; - /** - * Property key is too long - **/ - PropertyKeyIsTooLong: AugmentedError; - /** - * Tried to store more property keys than allowed - **/ - PropertyLimitReached: AugmentedError; - /** - * Collection is not in mint mode. - **/ - PublicMintingNotAllowed: AugmentedError; - /** - * Only tokens from specific collections may nest tokens under this one - **/ - SourceCollectionIsNotAllowedToNest: AugmentedError; - /** - * Item does not exist - **/ - TokenNotFound: AugmentedError; - /** - * Item is balance not enough - **/ - TokenValueTooLow: AugmentedError; - /** - * Total collections bound exceeded. - **/ - TotalCollectionsLimitExceeded: AugmentedError; - /** - * Collection settings not allowing items transferring - **/ - TransferNotAllowed: AugmentedError; - /** - * The operation is not supported - **/ - UnsupportedOperation: AugmentedError; - /** - * User does not satisfy the nesting rule - **/ - UserIsNotAllowedToNest: AugmentedError; - /** - * The user is not an administrator. - **/ - UserIsNotCollectionAdmin: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - configuration: { - InconsistentConfiguration: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - council: { - /** - * Members are already initialized! - **/ - AlreadyInitialized: AugmentedError; - /** - * Duplicate proposals not allowed - **/ - DuplicateProposal: AugmentedError; - /** - * Duplicate vote ignored - **/ - DuplicateVote: AugmentedError; - /** - * Account is not a member - **/ - NotMember: AugmentedError; - /** - * Prime account is not a member - **/ - PrimeAccountNotMember: AugmentedError; - /** - * Proposal must exist - **/ - ProposalMissing: AugmentedError; - /** - * The close call was made too early, before the end of the voting. - **/ - TooEarly: AugmentedError; - /** - * There can only be a maximum of `MaxProposals` active proposals. - **/ - TooManyProposals: AugmentedError; - /** - * Mismatched index - **/ - WrongIndex: AugmentedError; - /** - * The given length bound for the proposal was too low. - **/ - WrongProposalLength: AugmentedError; - /** - * The given weight bound for the proposal was too low. - **/ - WrongProposalWeight: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - councilMembership: { - /** - * Already a member. - **/ - AlreadyMember: AugmentedError; - /** - * Not a member. - **/ - NotMember: AugmentedError; - /** - * Too many members. - **/ - TooManyMembers: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - cumulusXcm: { - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - democracy: { - /** - * Cannot cancel the same proposal twice - **/ - AlreadyCanceled: AugmentedError; - /** - * The account is already delegating. - **/ - AlreadyDelegating: AugmentedError; - /** - * Identity may not veto a proposal twice - **/ - AlreadyVetoed: AugmentedError; - /** - * Proposal already made - **/ - DuplicateProposal: AugmentedError; - /** - * The instant referendum origin is currently disallowed. - **/ - InstantNotAllowed: AugmentedError; - /** - * Too high a balance was provided that the account cannot afford. - **/ - InsufficientFunds: AugmentedError; - /** - * Invalid hash - **/ - InvalidHash: AugmentedError; - /** - * Maximum number of votes reached. - **/ - MaxVotesReached: AugmentedError; - /** - * No proposals waiting - **/ - NoneWaiting: AugmentedError; - /** - * Delegation to oneself makes no sense. - **/ - Nonsense: AugmentedError; - /** - * The actor has no permission to conduct the action. - **/ - NoPermission: AugmentedError; - /** - * No external proposal - **/ - NoProposal: AugmentedError; - /** - * The account is not currently delegating. - **/ - NotDelegating: AugmentedError; - /** - * Next external proposal not simple majority - **/ - NotSimpleMajority: AugmentedError; - /** - * The given account did not vote on the referendum. - **/ - NotVoter: AugmentedError; - /** - * The preimage does not exist. - **/ - PreimageNotExist: AugmentedError; - /** - * Proposal still blacklisted - **/ - ProposalBlacklisted: AugmentedError; - /** - * Proposal does not exist - **/ - ProposalMissing: AugmentedError; - /** - * Vote given for invalid referendum - **/ - ReferendumInvalid: AugmentedError; - /** - * Maximum number of items reached. - **/ - TooMany: AugmentedError; - /** - * Value too low - **/ - ValueLow: AugmentedError; - /** - * The account currently has votes attached to it and the operation cannot succeed until - * these are removed, either through `unvote` or `reap_vote`. - **/ - VotesExist: AugmentedError; - /** - * Voting period too low - **/ - VotingPeriodLow: AugmentedError; - /** - * Invalid upper bound. - **/ - WrongUpperBound: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - dmpQueue: { - /** - * The amount of weight given is possibly not enough for executing the message. - **/ - OverLimit: AugmentedError; - /** - * The message index given is unknown. - **/ - Unknown: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - ethereum: { - /** - * Signature is invalid. - **/ - InvalidSignature: AugmentedError; - /** - * Pre-log is present, therefore transact is not allowed. - **/ - PreLogExists: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - evm: { - /** - * Not enough balance to perform action - **/ - BalanceLow: AugmentedError; - /** - * Calculating total fee overflowed - **/ - FeeOverflow: AugmentedError; - /** - * Gas limit is too high. - **/ - GasLimitTooHigh: AugmentedError; - /** - * Gas limit is too low. - **/ - GasLimitTooLow: AugmentedError; - /** - * Gas price is too low. - **/ - GasPriceTooLow: AugmentedError; - /** - * Nonce is invalid - **/ - InvalidNonce: AugmentedError; - /** - * Calculating total payment overflowed - **/ - PaymentOverflow: AugmentedError; - /** - * EVM reentrancy - **/ - Reentrancy: AugmentedError; - /** - * EIP-3607, - **/ - TransactionMustComeFromEOA: AugmentedError; - /** - * Undefined error. - **/ - Undefined: AugmentedError; - /** - * Withdraw fee failed - **/ - WithdrawFailed: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - evmCoderSubstrate: { - OutOfFund: AugmentedError; - OutOfGas: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - evmContractHelpers: { - /** - * No pending sponsor for contract. - **/ - NoPendingSponsor: AugmentedError; - /** - * This method is only executable by contract owner - **/ - NoPermission: AugmentedError; - /** - * Number of methods that sponsored limit is defined for exceeds maximum. - **/ - TooManyMethodsHaveSponsoredLimit: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - evmMigration: { - /** - * Migration of this account is not yet started, or already finished. - **/ - AccountIsNotMigrating: AugmentedError; - /** - * Can only migrate to empty address. - **/ - AccountNotEmpty: AugmentedError; - /** - * Failed to decode event bytes - **/ - BadEvent: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - fellowshipCollective: { - /** - * Account is already a member. - **/ - AlreadyMember: AugmentedError; - /** - * Unexpected error in state. - **/ - Corruption: AugmentedError; - /** - * The information provided is incorrect. - **/ - InvalidWitness: AugmentedError; - /** - * There are no further records to be removed. - **/ - NoneRemaining: AugmentedError; - /** - * The origin is not sufficiently privileged to do the operation. - **/ - NoPermission: AugmentedError; - /** - * Account is not a member. - **/ - NotMember: AugmentedError; - /** - * The given poll index is unknown or has closed. - **/ - NotPolling: AugmentedError; - /** - * The given poll is still ongoing. - **/ - Ongoing: AugmentedError; - /** - * The member's rank is too low to vote. - **/ - RankTooLow: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - fellowshipReferenda: { - /** - * The referendum index provided is invalid in this context. - **/ - BadReferendum: AugmentedError; - /** - * The referendum status is invalid for this operation. - **/ - BadStatus: AugmentedError; - /** - * The track identifier given was invalid. - **/ - BadTrack: AugmentedError; - /** - * There are already a full complement of referenda in progress for this track. - **/ - Full: AugmentedError; - /** - * Referendum's decision deposit is already paid. - **/ - HasDeposit: AugmentedError; - /** - * The deposit cannot be refunded since none was made. - **/ - NoDeposit: AugmentedError; - /** - * The deposit refunder is not the depositor. - **/ - NoPermission: AugmentedError; - /** - * There was nothing to do in the advancement. - **/ - NothingToDo: AugmentedError; - /** - * Referendum is not ongoing. - **/ - NotOngoing: AugmentedError; - /** - * No track exists for the proposal origin. - **/ - NoTrack: AugmentedError; - /** - * The preimage does not exist. - **/ - PreimageNotExist: AugmentedError; - /** - * The queue of the track is empty. - **/ - QueueEmpty: AugmentedError; - /** - * Any deposit cannot be refunded until after the decision is over. - **/ - Unfinished: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - foreignAssets: { - /** - * AssetId exists - **/ - AssetIdExisted: AugmentedError; - /** - * AssetId not exists - **/ - AssetIdNotExists: AugmentedError; - /** - * The given location could not be used (e.g. because it cannot be expressed in the - * desired version of XCM). - **/ - BadLocation: AugmentedError; - /** - * MultiLocation existed - **/ - MultiLocationExisted: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - fungible: { - /** - * Fungible token does not support nesting. - **/ - FungibleDisallowsNesting: AugmentedError; - /** - * Tried to set data for fungible item. - **/ - FungibleItemsDontHaveData: AugmentedError; - /** - * Only a fungible collection could be possibly broken; any fungible token is valid. - **/ - FungibleTokensAreAlwaysValid: AugmentedError; - /** - * Not Fungible item data used to mint in Fungible collection. - **/ - NotFungibleDataUsedToMintFungibleCollectionToken: AugmentedError; - /** - * Setting allowance for all is not allowed. - **/ - SettingAllowanceForAllNotAllowed: AugmentedError; - /** - * Setting item properties is not allowed. - **/ - SettingPropertiesNotAllowed: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - identity: { - /** - * Account ID is already named. - **/ - AlreadyClaimed: AugmentedError; - /** - * Empty index. - **/ - EmptyIndex: AugmentedError; - /** - * Fee is changed. - **/ - FeeChanged: AugmentedError; - /** - * The index is invalid. - **/ - InvalidIndex: AugmentedError; - /** - * Invalid judgement. - **/ - InvalidJudgement: AugmentedError; - /** - * The target is invalid. - **/ - InvalidTarget: AugmentedError; - /** - * The provided judgement was for a different identity. - **/ - JudgementForDifferentIdentity: AugmentedError; - /** - * Judgement given. - **/ - JudgementGiven: AugmentedError; - /** - * Error that occurs when there is an issue paying for judgement. - **/ - JudgementPaymentFailed: AugmentedError; - /** - * No identity found. - **/ - NoIdentity: AugmentedError; - /** - * Account isn't found. - **/ - NotFound: AugmentedError; - /** - * Account isn't named. - **/ - NotNamed: AugmentedError; - /** - * Sub-account isn't owned by sender. - **/ - NotOwned: AugmentedError; - /** - * Sender is not a sub-account. - **/ - NotSub: AugmentedError; - /** - * Sticky judgement. - **/ - StickyJudgement: AugmentedError; - /** - * Too many additional fields. - **/ - TooManyFields: AugmentedError; - /** - * Maximum amount of registrars reached. Cannot add any more. - **/ - TooManyRegistrars: AugmentedError; - /** - * Too many subs-accounts. - **/ - TooManySubAccounts: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - maintenance: { - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - nonfungible: { - /** - * Unable to burn NFT with children - **/ - CantBurnNftWithChildren: AugmentedError; - /** - * Used amount > 1 with NFT - **/ - NonfungibleItemsHaveNoAmount: AugmentedError; - /** - * Not Nonfungible item data used to mint in Nonfungible collection. - **/ - NotNonfungibleDataUsedToMintFungibleCollectionToken: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - parachainSystem: { - /** - * The inherent which supplies the host configuration did not run this block. - **/ - HostConfigurationNotAvailable: AugmentedError; - /** - * No code upgrade has been authorized. - **/ - NothingAuthorized: AugmentedError; - /** - * No validation function upgrade is currently scheduled. - **/ - NotScheduled: AugmentedError; - /** - * Attempt to upgrade validation function while existing upgrade pending. - **/ - OverlappingUpgrades: AugmentedError; - /** - * Polkadot currently prohibits this parachain from upgrading its validation function. - **/ - ProhibitedByPolkadot: AugmentedError; - /** - * The supplied validation function has compiled into a blob larger than Polkadot is - * willing to run. - **/ - TooBig: AugmentedError; - /** - * The given code upgrade has not been authorized. - **/ - Unauthorized: AugmentedError; - /** - * The inherent which supplies the validation data did not run this block. - **/ - ValidationDataNotAvailable: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - polkadotXcm: { - /** - * The given account is not an identifiable sovereign account for any location. - **/ - AccountNotSovereign: AugmentedError; - /** - * The location is invalid since it already has a subscription from us. - **/ - AlreadySubscribed: AugmentedError; - /** - * The given location could not be used (e.g. because it cannot be expressed in the - * desired version of XCM). - **/ - BadLocation: AugmentedError; - /** - * The version of the `Versioned` value used is not able to be interpreted. - **/ - BadVersion: AugmentedError; - /** - * Could not re-anchor the assets to declare the fees for the destination chain. - **/ - CannotReanchor: AugmentedError; - /** - * The destination `MultiLocation` provided cannot be inverted. - **/ - DestinationNotInvertible: AugmentedError; - /** - * The assets to be sent are empty. - **/ - Empty: AugmentedError; - /** - * The operation required fees to be paid which the initiator could not meet. - **/ - FeesNotMet: AugmentedError; - /** - * The message execution fails the filter. - **/ - Filtered: AugmentedError; - /** - * The unlock operation cannot succeed because there are still consumers of the lock. - **/ - InUse: AugmentedError; - /** - * Invalid asset for the operation. - **/ - InvalidAsset: AugmentedError; - /** - * Origin is invalid for sending. - **/ - InvalidOrigin: AugmentedError; - /** - * A remote lock with the corresponding data could not be found. - **/ - LockNotFound: AugmentedError; - /** - * The owner does not own (all) of the asset that they wish to do the operation on. - **/ - LowBalance: AugmentedError; - /** - * The referenced subscription could not be found. - **/ - NoSubscription: AugmentedError; - /** - * There was some other issue (i.e. not to do with routing) in sending the message. - * Perhaps a lack of space for buffering the message. - **/ - SendFailure: AugmentedError; - /** - * Too many assets have been attempted for transfer. - **/ - TooManyAssets: AugmentedError; - /** - * The asset owner has too many locks on the asset. - **/ - TooManyLocks: AugmentedError; - /** - * The desired destination was unreachable, generally because there is a no way of routing - * to it. - **/ - Unreachable: AugmentedError; - /** - * The message's weight could not be determined. - **/ - UnweighableMessage: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - preimage: { - /** - * Preimage has already been noted on-chain. - **/ - AlreadyNoted: AugmentedError; - /** - * The user is not authorized to perform this action. - **/ - NotAuthorized: AugmentedError; - /** - * The preimage cannot be removed since it has not yet been noted. - **/ - NotNoted: AugmentedError; - /** - * The preimage request cannot be removed since no outstanding requests exist. - **/ - NotRequested: AugmentedError; - /** - * A preimage may not be removed when there are outstanding requests. - **/ - Requested: AugmentedError; - /** - * Preimage is too large to store on-chain. - **/ - TooBig: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - refungible: { - /** - * Not Refungible item data used to mint in Refungible collection. - **/ - NotRefungibleDataUsedToMintFungibleCollectionToken: AugmentedError; - /** - * Refungible token can't nest other tokens. - **/ - RefungibleDisallowsNesting: AugmentedError; - /** - * Refungible token can't be repartitioned by user who isn't owns all pieces. - **/ - RepartitionWhileNotOwningAllPieces: AugmentedError; - /** - * Setting item properties is not allowed. - **/ - SettingPropertiesNotAllowed: AugmentedError; - /** - * Maximum refungibility exceeded. - **/ - WrongRefungiblePieces: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - scheduler: { - /** - * Failed to schedule a call - **/ - FailedToSchedule: AugmentedError; - /** - * Attempt to use a non-named function on a named task. - **/ - Named: AugmentedError; - /** - * Cannot find the scheduled call. - **/ - NotFound: AugmentedError; - /** - * Reschedule failed because it does not change scheduled time. - **/ - RescheduleNoChange: AugmentedError; - /** - * Given target block number is in the past. - **/ - TargetBlockNumberInPast: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - session: { - /** - * Registered duplicate key. - **/ - DuplicatedKey: AugmentedError; - /** - * Invalid ownership proof. - **/ - InvalidProof: AugmentedError; - /** - * Key setting account is not live, so it's impossible to associate keys. - **/ - NoAccount: AugmentedError; - /** - * No associated validator ID for account. - **/ - NoAssociatedValidatorId: AugmentedError; - /** - * No keys are associated with this account. - **/ - NoKeys: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - stateTrieMigration: { - /** - * Bad child root provided. - **/ - BadChildRoot: AugmentedError; - /** - * Bad witness data provided. - **/ - BadWitness: AugmentedError; - /** - * A key was longer than the configured maximum. - * - * This means that the migration halted at the current [`Progress`] and - * can be resumed with a larger [`crate::Config::MaxKeyLen`] value. - * Retrying with the same [`crate::Config::MaxKeyLen`] value will not work. - * The value should only be increased to avoid a storage migration for the currently - * stored [`crate::Progress::LastKey`]. - **/ - KeyTooLong: AugmentedError; - /** - * Max signed limits not respected. - **/ - MaxSignedLimits: AugmentedError; - /** - * submitter does not have enough funds. - **/ - NotEnoughFunds: AugmentedError; - /** - * Signed migration is not allowed because the maximum limit is not set yet. - **/ - SignedMigrationNotAllowed: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - structure: { - /** - * While nesting, reached the breadth limit of nesting, exceeding the provided budget. - **/ - BreadthLimit: AugmentedError; - /** - * Tried to nest token under collection contract address, instead of token address - **/ - CantNestTokenUnderCollection: AugmentedError; - /** - * While nesting, reached the depth limit of nesting, exceeding the provided budget. - **/ - DepthLimit: AugmentedError; - /** - * While nesting, encountered an already checked account, detecting a loop. - **/ - OuroborosDetected: AugmentedError; - /** - * Couldn't find the token owner that is itself a token. - **/ - TokenNotFound: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - sudo: { - /** - * Sender must be the Sudo account - **/ - RequireSudo: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - system: { - /** - * The origin filter prevent the call to be dispatched. - **/ - CallFiltered: AugmentedError; - /** - * Failed to extract the runtime version from the new runtime. - * - * Either calling `Core_version` or decoding `RuntimeVersion` failed. - **/ - FailedToExtractRuntimeVersion: AugmentedError; - /** - * The name of specification does not match between the current runtime - * and the new runtime. - **/ - InvalidSpecName: AugmentedError; - /** - * Suicide called when the account has non-default composite data. - **/ - NonDefaultComposite: AugmentedError; - /** - * There is a non-zero reference count preventing the account from being purged. - **/ - NonZeroRefCount: AugmentedError; - /** - * The specification version is not allowed to decrease between the current runtime - * and the new runtime. - **/ - SpecVersionNeedsToIncrease: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - technicalCommittee: { - /** - * Members are already initialized! - **/ - AlreadyInitialized: AugmentedError; - /** - * Duplicate proposals not allowed - **/ - DuplicateProposal: AugmentedError; - /** - * Duplicate vote ignored - **/ - DuplicateVote: AugmentedError; - /** - * Account is not a member - **/ - NotMember: AugmentedError; - /** - * Prime account is not a member - **/ - PrimeAccountNotMember: AugmentedError; - /** - * Proposal must exist - **/ - ProposalMissing: AugmentedError; - /** - * The close call was made too early, before the end of the voting. - **/ - TooEarly: AugmentedError; - /** - * There can only be a maximum of `MaxProposals` active proposals. - **/ - TooManyProposals: AugmentedError; - /** - * Mismatched index - **/ - WrongIndex: AugmentedError; - /** - * The given length bound for the proposal was too low. - **/ - WrongProposalLength: AugmentedError; - /** - * The given weight bound for the proposal was too low. - **/ - WrongProposalWeight: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - technicalCommitteeMembership: { - /** - * Already a member. - **/ - AlreadyMember: AugmentedError; - /** - * Not a member. - **/ - NotMember: AugmentedError; - /** - * Too many members. - **/ - TooManyMembers: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - testUtils: { - TestPalletDisabled: AugmentedError; - TriggerRollback: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - tokens: { - /** - * Cannot convert Amount into Balance type - **/ - AmountIntoBalanceFailed: AugmentedError; - /** - * The balance is too low - **/ - BalanceTooLow: AugmentedError; - /** - * Beneficiary account must pre-exist - **/ - DeadAccount: AugmentedError; - /** - * Value too low to create account due to existential deposit - **/ - ExistentialDeposit: AugmentedError; - /** - * Transfer/payment would kill account - **/ - KeepAlive: AugmentedError; - /** - * Failed because liquidity restrictions due to locking - **/ - LiquidityRestrictions: AugmentedError; - /** - * Failed because the maximum locks was exceeded - **/ - MaxLocksExceeded: AugmentedError; - TooManyReserves: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - treasury: { - /** - * The spend origin is valid but the amount it is allowed to spend is lower than the - * amount to be spent. - **/ - InsufficientPermission: AugmentedError; - /** - * Proposer's balance is too low. - **/ - InsufficientProposersBalance: AugmentedError; - /** - * No proposal or bounty at that index. - **/ - InvalidIndex: AugmentedError; - /** - * Proposal has not been approved. - **/ - ProposalNotApproved: AugmentedError; - /** - * Too many approvals in the queue. - **/ - TooManyApprovals: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - unique: { - /** - * Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`]. - **/ - CollectionDecimalPointLimitExceeded: AugmentedError; - /** - * Length of items properties must be greater than 0. - **/ - EmptyArgument: AugmentedError; - /** - * Repertition is only supported by refungible collection. - **/ - RepartitionCalledOnNonRefungibleCollection: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - utility: { - /** - * Too many calls batched. - **/ - TooManyCalls: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - vesting: { - /** - * The vested transfer amount is too low - **/ - AmountLow: AugmentedError; - /** - * Insufficient amount of balance to lock - **/ - InsufficientBalanceToLock: AugmentedError; - /** - * Failed because the maximum vesting schedules was exceeded - **/ - MaxVestingSchedulesExceeded: AugmentedError; - /** - * This account have too many vesting schedules - **/ - TooManyVestingSchedules: AugmentedError; - /** - * Vesting period is zero - **/ - ZeroVestingPeriod: AugmentedError; - /** - * Number of vests is zero - **/ - ZeroVestingPeriodCount: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - xcmpQueue: { - /** - * Bad overweight index. - **/ - BadOverweightIndex: AugmentedError; - /** - * Bad XCM data. - **/ - BadXcm: AugmentedError; - /** - * Bad XCM origin. - **/ - BadXcmOrigin: AugmentedError; - /** - * Failed to send XCM message. - **/ - FailedToSend: AugmentedError; - /** - * Provided weight is possibly not enough to execute the message. - **/ - WeightOverLimit: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - xTokens: { - /** - * Asset has no reserve location. - **/ - AssetHasNoReserve: AugmentedError; - /** - * The specified index does not exist in a MultiAssets struct. - **/ - AssetIndexNonExistent: AugmentedError; - /** - * The version of the `Versioned` value used is not able to be - * interpreted. - **/ - BadVersion: AugmentedError; - /** - * Could not re-anchor the assets to declare the fees for the - * destination chain. - **/ - CannotReanchor: AugmentedError; - /** - * The destination `MultiLocation` provided cannot be inverted. - **/ - DestinationNotInvertible: AugmentedError; - /** - * We tried sending distinct asset and fee but they have different - * reserve chains. - **/ - DistinctReserveForAssetAndFee: AugmentedError; - /** - * Fee is not enough. - **/ - FeeNotEnough: AugmentedError; - /** - * Could not get ancestry of asset reserve location. - **/ - InvalidAncestry: AugmentedError; - /** - * The MultiAsset is invalid. - **/ - InvalidAsset: AugmentedError; - /** - * Invalid transfer destination. - **/ - InvalidDest: AugmentedError; - /** - * MinXcmFee not registered for certain reserve location - **/ - MinXcmFeeNotDefined: AugmentedError; - /** - * Not cross-chain transfer. - **/ - NotCrossChainTransfer: AugmentedError; - /** - * Currency is not cross-chain transferable. - **/ - NotCrossChainTransferableCurrency: AugmentedError; - /** - * Not supported MultiLocation - **/ - NotSupportedMultiLocation: AugmentedError; - /** - * The number of assets to be sent is over the maximum. - **/ - TooManyAssetsBeingSent: AugmentedError; - /** - * The message's weight could not be determined. - **/ - UnweighableMessage: AugmentedError; - /** - * XCM execution failed. - **/ - XcmExecutionFailed: AugmentedError; - /** - * The transfering asset amount is zero. - **/ - ZeroAmount: AugmentedError; - /** - * The fee is zero. - **/ - ZeroFee: AugmentedError; - /** - * Generic error - **/ - [key: string]: AugmentedError; - }; - } // AugmentedErrors -} // declare module --- a/js-packages/types/src/augment-api-events.ts +++ /dev/null @@ -1,1294 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-chain`, do not edit -/* eslint-disable */ - -// import type lookup before we augment - in some environments -// this is required to allow for ambient/previous definitions -import '@polkadot/api-base/types/events'; - -import type { ApiTypes, AugmentedEvent } from '@polkadot/api-base/types'; -import type { Bytes, Null, Option, Result, U8aFixed, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; -import type { ITuple } from '@polkadot/types-codec/types'; -import type { AccountId32, H160, H256 } from '@polkadot/types/interfaces/runtime'; -import type { EthereumLog, EvmCoreErrorExitReason, FrameSupportDispatchDispatchInfo, FrameSupportPreimagesBounded, FrameSupportTokensMiscBalanceStatus, OrmlVestingVestingSchedule, PalletDemocracyMetadataOwner, PalletDemocracyVoteAccountVote, PalletDemocracyVoteThreshold, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletRankedCollectiveTally, PalletRankedCollectiveVoteRecord, PalletStateTrieMigrationError, PalletStateTrieMigrationMigrationCompute, SpRuntimeDispatchError, SpWeightsWeightV2Weight, StagingXcmV3MultiAsset, StagingXcmV3MultiLocation, StagingXcmV3MultiassetMultiAssets, StagingXcmV3Response, StagingXcmV3TraitsError, StagingXcmV3TraitsOutcome, StagingXcmV3Xcm, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation } from '@polkadot/types/lookup'; - -export type __AugmentedEvent = AugmentedEvent; - -declare module '@polkadot/api-base/types/events' { - interface AugmentedEvents { - appPromotion: { - /** - * The admin was set - * - * # Arguments - * * AccountId: account address of the admin - **/ - SetAdmin: AugmentedEvent; - /** - * Staking was performed - * - * # Arguments - * * AccountId: account of the staker - * * Balance : staking amount - **/ - Stake: AugmentedEvent; - /** - * Staking recalculation was performed - * - * # Arguments - * * AccountId: account of the staker. - * * Balance : recalculation base - * * Balance : total income - **/ - StakingRecalculation: AugmentedEvent; - /** - * Unstaking was performed - * - * # Arguments - * * AccountId: account of the staker - * * Balance : unstaking amount - **/ - Unstake: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - balances: { - /** - * A balance was set by root. - **/ - BalanceSet: AugmentedEvent; - /** - * Some amount was burned from an account. - **/ - Burned: AugmentedEvent; - /** - * Some amount was deposited (e.g. for transaction fees). - **/ - Deposit: AugmentedEvent; - /** - * An account was removed whose balance was non-zero but below ExistentialDeposit, - * resulting in an outright loss. - **/ - DustLost: AugmentedEvent; - /** - * An account was created with some free balance. - **/ - Endowed: AugmentedEvent; - /** - * Some balance was frozen. - **/ - Frozen: AugmentedEvent; - /** - * Total issuance was increased by `amount`, creating a credit to be balanced. - **/ - Issued: AugmentedEvent; - /** - * Some balance was locked. - **/ - Locked: AugmentedEvent; - /** - * Some amount was minted into an account. - **/ - Minted: AugmentedEvent; - /** - * Total issuance was decreased by `amount`, creating a debt to be balanced. - **/ - Rescinded: AugmentedEvent; - /** - * Some balance was reserved (moved from free to reserved). - **/ - Reserved: AugmentedEvent; - /** - * Some balance was moved from the reserve of the first account to the second account. - * Final argument indicates the destination balance type. - **/ - ReserveRepatriated: AugmentedEvent; - /** - * Some amount was restored into an account. - **/ - Restored: AugmentedEvent; - /** - * Some amount was removed from the account (e.g. for misbehavior). - **/ - Slashed: AugmentedEvent; - /** - * Some amount was suspended from an account (it can be restored later). - **/ - Suspended: AugmentedEvent; - /** - * Some balance was thawed. - **/ - Thawed: AugmentedEvent; - /** - * Transfer succeeded. - **/ - Transfer: AugmentedEvent; - /** - * Some balance was unlocked. - **/ - Unlocked: AugmentedEvent; - /** - * Some balance was unreserved (moved from reserved to free). - **/ - Unreserved: AugmentedEvent; - /** - * An account was upgraded. - **/ - Upgraded: AugmentedEvent; - /** - * Some amount was withdrawn from the account (e.g. for transaction fees). - **/ - Withdraw: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - collatorSelection: { - CandidateAdded: AugmentedEvent; - CandidateRemoved: AugmentedEvent; - InvulnerableAdded: AugmentedEvent; - InvulnerableRemoved: AugmentedEvent; - LicenseObtained: AugmentedEvent; - LicenseReleased: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - common: { - /** - * Address was added to the allow list. - **/ - AllowListAddressAdded: AugmentedEvent; - /** - * Address was removed from the allow list. - **/ - AllowListAddressRemoved: AugmentedEvent; - /** - * Amount pieces of token owned by `sender` was approved for `spender`. - **/ - Approved: AugmentedEvent; - /** - * A `sender` approves operations on all owned tokens for `spender`. - **/ - ApprovedForAll: AugmentedEvent; - /** - * Collection admin was added. - **/ - CollectionAdminAdded: AugmentedEvent; - /** - * Collection admin was removed. - **/ - CollectionAdminRemoved: AugmentedEvent; - /** - * New collection was created - **/ - CollectionCreated: AugmentedEvent; - /** - * New collection was destroyed - **/ - CollectionDestroyed: AugmentedEvent; - /** - * Collection limits were set. - **/ - CollectionLimitSet: AugmentedEvent; - /** - * Collection owned was changed. - **/ - CollectionOwnerChanged: AugmentedEvent; - /** - * Collection permissions were set. - **/ - CollectionPermissionSet: AugmentedEvent; - /** - * The property has been deleted. - **/ - CollectionPropertyDeleted: AugmentedEvent; - /** - * The colletion property has been added or edited. - **/ - CollectionPropertySet: AugmentedEvent; - /** - * Collection sponsor was removed. - **/ - CollectionSponsorRemoved: AugmentedEvent; - /** - * Collection sponsor was set. - **/ - CollectionSponsorSet: AugmentedEvent; - /** - * New item was created. - **/ - ItemCreated: AugmentedEvent; - /** - * Collection item was burned. - **/ - ItemDestroyed: AugmentedEvent; - /** - * The token property permission of a collection has been set. - **/ - PropertyPermissionSet: AugmentedEvent; - /** - * New sponsor was confirm. - **/ - SponsorshipConfirmed: AugmentedEvent; - /** - * The token property has been deleted. - **/ - TokenPropertyDeleted: AugmentedEvent; - /** - * The token property has been added or edited. - **/ - TokenPropertySet: AugmentedEvent; - /** - * Item was transferred - **/ - Transfer: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - configuration: { - NewCollatorKickThreshold: AugmentedEvent], { lengthInBlocks: Option }>; - NewCollatorLicenseBond: AugmentedEvent], { bondCost: Option }>; - NewDesiredCollators: AugmentedEvent], { desiredCollators: Option }>; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - council: { - /** - * A motion was approved by the required threshold. - **/ - Approved: AugmentedEvent; - /** - * A proposal was closed because its threshold was reached or after its duration was up. - **/ - Closed: AugmentedEvent; - /** - * A motion was not approved by the required threshold. - **/ - Disapproved: AugmentedEvent; - /** - * A motion was executed; result will be `Ok` if it returned without error. - **/ - Executed: AugmentedEvent], { proposalHash: H256, result: Result }>; - /** - * A single member did some action; result will be `Ok` if it returned without error. - **/ - MemberExecuted: AugmentedEvent], { proposalHash: H256, result: Result }>; - /** - * A motion (given hash) has been proposed (by given account) with a threshold (given - * `MemberCount`). - **/ - Proposed: AugmentedEvent; - /** - * A motion (given hash) has been voted on by given account, leaving - * a tally (yes votes and no votes given respectively as `MemberCount`). - **/ - Voted: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - councilMembership: { - /** - * Phantom member, never used. - **/ - Dummy: AugmentedEvent; - /** - * One of the members' keys changed. - **/ - KeyChanged: AugmentedEvent; - /** - * The given member was added; see the transaction for who. - **/ - MemberAdded: AugmentedEvent; - /** - * The given member was removed; see the transaction for who. - **/ - MemberRemoved: AugmentedEvent; - /** - * The membership was reset; see the transaction for who the new set is. - **/ - MembersReset: AugmentedEvent; - /** - * Two members were swapped; see the transaction for who. - **/ - MembersSwapped: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - cumulusXcm: { - /** - * Downward message executed with the given outcome. - * \[ id, outcome \] - **/ - ExecutedDownward: AugmentedEvent; - /** - * Downward message is invalid XCM. - * \[ id \] - **/ - InvalidFormat: AugmentedEvent; - /** - * Downward message is unsupported version of XCM. - * \[ id \] - **/ - UnsupportedVersion: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - democracy: { - /** - * A proposal_hash has been blacklisted permanently. - **/ - Blacklisted: AugmentedEvent; - /** - * A referendum has been cancelled. - **/ - Cancelled: AugmentedEvent; - /** - * An account has delegated their vote to another account. - **/ - Delegated: AugmentedEvent; - /** - * An external proposal has been tabled. - **/ - ExternalTabled: AugmentedEvent; - /** - * Metadata for a proposal or a referendum has been cleared. - **/ - MetadataCleared: AugmentedEvent; - /** - * Metadata for a proposal or a referendum has been set. - **/ - MetadataSet: AugmentedEvent; - /** - * Metadata has been transferred to new owner. - **/ - MetadataTransferred: AugmentedEvent; - /** - * A proposal has been rejected by referendum. - **/ - NotPassed: AugmentedEvent; - /** - * A proposal has been approved by referendum. - **/ - Passed: AugmentedEvent; - /** - * A proposal got canceled. - **/ - ProposalCanceled: AugmentedEvent; - /** - * A motion has been proposed by a public account. - **/ - Proposed: AugmentedEvent; - /** - * An account has secconded a proposal - **/ - Seconded: AugmentedEvent; - /** - * A referendum has begun. - **/ - Started: AugmentedEvent; - /** - * A public proposal has been tabled for referendum vote. - **/ - Tabled: AugmentedEvent; - /** - * An account has cancelled a previous delegation operation. - **/ - Undelegated: AugmentedEvent; - /** - * An external proposal has been vetoed. - **/ - Vetoed: AugmentedEvent; - /** - * An account has voted in a referendum - **/ - Voted: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - dmpQueue: { - /** - * Downward message executed with the given outcome. - **/ - ExecutedDownward: AugmentedEvent; - /** - * Downward message is invalid XCM. - **/ - InvalidFormat: AugmentedEvent; - /** - * The maximum number of downward messages was reached. - **/ - MaxMessagesExhausted: AugmentedEvent; - /** - * Downward message is overweight and was placed in the overweight queue. - **/ - OverweightEnqueued: AugmentedEvent; - /** - * Downward message from the overweight queue was executed. - **/ - OverweightServiced: AugmentedEvent; - /** - * Downward message is unsupported version of XCM. - **/ - UnsupportedVersion: AugmentedEvent; - /** - * The weight limit for handling downward messages was reached. - **/ - WeightExhausted: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - ethereum: { - /** - * An ethereum transaction was successfully executed. - **/ - Executed: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - evm: { - /** - * A contract has been created at given address. - **/ - Created: AugmentedEvent; - /** - * A contract was attempted to be created, but the execution failed. - **/ - CreatedFailed: AugmentedEvent; - /** - * A contract has been executed successfully with states applied. - **/ - Executed: AugmentedEvent; - /** - * A contract has been executed with errors. States are reverted with only gas fees applied. - **/ - ExecutedFailed: AugmentedEvent; - /** - * Ethereum events from contracts. - **/ - Log: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - evmContractHelpers: { - /** - * Collection sponsor was removed. - **/ - ContractSponsorRemoved: AugmentedEvent; - /** - * Contract sponsor was set. - **/ - ContractSponsorSet: AugmentedEvent; - /** - * New sponsor was confirm. - **/ - ContractSponsorshipConfirmed: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - evmMigration: { - /** - * This event is used in benchmarking and can be used for tests - **/ - TestEvent: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - fellowshipCollective: { - /** - * A member `who` has been added. - **/ - MemberAdded: AugmentedEvent; - /** - * The member `who` of given `rank` has been removed from the collective. - **/ - MemberRemoved: AugmentedEvent; - /** - * The member `who`se rank has been changed to the given `rank`. - **/ - RankChanged: AugmentedEvent; - /** - * The member `who` has voted for the `poll` with the given `vote` leading to an updated - * `tally`. - **/ - Voted: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - fellowshipReferenda: { - /** - * A referendum has been approved and its proposal has been scheduled. - **/ - Approved: AugmentedEvent; - /** - * A referendum has been cancelled. - **/ - Cancelled: AugmentedEvent; - ConfirmAborted: AugmentedEvent; - /** - * A referendum has ended its confirmation phase and is ready for approval. - **/ - Confirmed: AugmentedEvent; - ConfirmStarted: AugmentedEvent; - /** - * The decision deposit has been placed. - **/ - DecisionDepositPlaced: AugmentedEvent; - /** - * The decision deposit has been refunded. - **/ - DecisionDepositRefunded: AugmentedEvent; - /** - * A referendum has moved into the deciding phase. - **/ - DecisionStarted: AugmentedEvent; - /** - * A deposit has been slashaed. - **/ - DepositSlashed: AugmentedEvent; - /** - * A referendum has been killed. - **/ - Killed: AugmentedEvent; - /** - * Metadata for a referendum has been cleared. - **/ - MetadataCleared: AugmentedEvent; - /** - * Metadata for a referendum has been set. - **/ - MetadataSet: AugmentedEvent; - /** - * A proposal has been rejected by referendum. - **/ - Rejected: AugmentedEvent; - /** - * The submission deposit has been refunded. - **/ - SubmissionDepositRefunded: AugmentedEvent; - /** - * A referendum has been submitted. - **/ - Submitted: AugmentedEvent; - /** - * A referendum has been timed out without being decided. - **/ - TimedOut: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - foreignAssets: { - /** - * The asset registered. - **/ - AssetRegistered: AugmentedEvent; - /** - * The asset updated. - **/ - AssetUpdated: AugmentedEvent; - /** - * The foreign asset registered. - **/ - ForeignAssetRegistered: AugmentedEvent; - /** - * The foreign asset updated. - **/ - ForeignAssetUpdated: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - identity: { - /** - * A number of identities and associated info were forcibly inserted. - **/ - IdentitiesInserted: AugmentedEvent; - /** - * A number of identities and all associated info were forcibly removed. - **/ - IdentitiesRemoved: AugmentedEvent; - /** - * A name was cleared, and the given balance returned. - **/ - IdentityCleared: AugmentedEvent; - /** - * A name was removed and the given balance slashed. - **/ - IdentityKilled: AugmentedEvent; - /** - * A name was set or reset (which will remove all judgements). - **/ - IdentitySet: AugmentedEvent; - /** - * A judgement was given by a registrar. - **/ - JudgementGiven: AugmentedEvent; - /** - * A judgement was asked from a registrar. - **/ - JudgementRequested: AugmentedEvent; - /** - * A judgement request was retracted. - **/ - JudgementUnrequested: AugmentedEvent; - /** - * A registrar was added. - **/ - RegistrarAdded: AugmentedEvent; - /** - * A number of identities were forcibly updated with new sub-identities. - **/ - SubIdentitiesInserted: AugmentedEvent; - /** - * A sub-identity was added to an identity and the deposit paid. - **/ - SubIdentityAdded: AugmentedEvent; - /** - * A sub-identity was removed from an identity and the deposit freed. - **/ - SubIdentityRemoved: AugmentedEvent; - /** - * A sub-identity was cleared, and the given deposit repatriated from the - * main identity account to the sub-identity account. - **/ - SubIdentityRevoked: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - maintenance: { - MaintenanceDisabled: AugmentedEvent; - MaintenanceEnabled: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - parachainSystem: { - /** - * Downward messages were processed using the given weight. - **/ - DownwardMessagesProcessed: AugmentedEvent; - /** - * Some downward messages have been received and will be processed. - **/ - DownwardMessagesReceived: AugmentedEvent; - /** - * An upgrade has been authorized. - **/ - UpgradeAuthorized: AugmentedEvent; - /** - * An upward message was sent to the relay chain. - **/ - UpwardMessageSent: AugmentedEvent], { messageHash: Option }>; - /** - * The validation function was applied as of the contained relay chain block number. - **/ - ValidationFunctionApplied: AugmentedEvent; - /** - * The relay-chain aborted the upgrade process. - **/ - ValidationFunctionDiscarded: AugmentedEvent; - /** - * The validation function has been scheduled to apply. - **/ - ValidationFunctionStored: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - polkadotXcm: { - /** - * Some assets have been claimed from an asset trap - **/ - AssetsClaimed: AugmentedEvent; - /** - * Some assets have been placed in an asset trap. - **/ - AssetsTrapped: AugmentedEvent; - /** - * Execution of an XCM message was attempted. - **/ - Attempted: AugmentedEvent; - /** - * Fees were paid from a location for an operation (often for using `SendXcm`). - **/ - FeesPaid: AugmentedEvent; - /** - * Expected query response has been received but the querier location of the response does - * not match the expected. The query remains registered for a later, valid, response to - * be received and acted upon. - **/ - InvalidQuerier: AugmentedEvent], { origin: StagingXcmV3MultiLocation, queryId: u64, expectedQuerier: StagingXcmV3MultiLocation, maybeActualQuerier: Option }>; - /** - * Expected query response has been received but the expected querier location placed in - * storage by this runtime previously cannot be decoded. The query remains registered. - * - * This is unexpected (since a location placed in storage in a previously executing - * runtime should be readable prior to query timeout) and dangerous since the possibly - * valid response will be dropped. Manual governance intervention is probably going to be - * needed. - **/ - InvalidQuerierVersion: AugmentedEvent; - /** - * Expected query response has been received but the origin location of the response does - * not match that expected. The query remains registered for a later, valid, response to - * be received and acted upon. - **/ - InvalidResponder: AugmentedEvent], { origin: StagingXcmV3MultiLocation, queryId: u64, expectedLocation: Option }>; - /** - * Expected query response has been received but the expected origin location placed in - * storage by this runtime previously cannot be decoded. The query remains registered. - * - * This is unexpected (since a location placed in storage in a previously executing - * runtime should be readable prior to query timeout) and dangerous since the possibly - * valid response will be dropped. Manual governance intervention is probably going to be - * needed. - **/ - InvalidResponderVersion: AugmentedEvent; - /** - * Query response has been received and query is removed. The registered notification has - * been dispatched and executed successfully. - **/ - Notified: AugmentedEvent; - /** - * Query response has been received and query is removed. The dispatch was unable to be - * decoded into a `Call`; this might be due to dispatch function having a signature which - * is not `(origin, QueryId, Response)`. - **/ - NotifyDecodeFailed: AugmentedEvent; - /** - * Query response has been received and query is removed. There was a general error with - * dispatching the notification call. - **/ - NotifyDispatchError: AugmentedEvent; - /** - * Query response has been received and query is removed. The registered notification - * could not be dispatched because the dispatch weight is greater than the maximum weight - * originally budgeted by this runtime for the query result. - **/ - NotifyOverweight: AugmentedEvent; - /** - * A given location which had a version change subscription was dropped owing to an error - * migrating the location to our new XCM format. - **/ - NotifyTargetMigrationFail: AugmentedEvent; - /** - * A given location which had a version change subscription was dropped owing to an error - * sending the notification to it. - **/ - NotifyTargetSendFail: AugmentedEvent; - /** - * Query response has been received and is ready for taking with `take_response`. There is - * no registered notification call. - **/ - ResponseReady: AugmentedEvent; - /** - * Received query response has been read and removed. - **/ - ResponseTaken: AugmentedEvent; - /** - * A XCM message was sent. - **/ - Sent: AugmentedEvent; - /** - * The supported version of a location has been changed. This might be through an - * automatic notification or a manual intervention. - **/ - SupportedVersionChanged: AugmentedEvent; - /** - * Query response received which does not match a registered query. This may be because a - * matching query was never registered, it may be because it is a duplicate response, or - * because the query timed out. - **/ - UnexpectedResponse: AugmentedEvent; - /** - * An XCM version change notification message has been attempted to be sent. - * - * The cost of sending it (borne by the chain) is included. - **/ - VersionChangeNotified: AugmentedEvent; - /** - * We have requested that a remote chain send us XCM version change notifications. - **/ - VersionNotifyRequested: AugmentedEvent; - /** - * A remote has requested XCM version change notification from us and we have honored it. - * A version information message is sent to them and its cost is included. - **/ - VersionNotifyStarted: AugmentedEvent; - /** - * We have requested that a remote chain stops sending us XCM version change - * notifications. - **/ - VersionNotifyUnrequested: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - preimage: { - /** - * A preimage has ben cleared. - **/ - Cleared: AugmentedEvent; - /** - * A preimage has been noted. - **/ - Noted: AugmentedEvent; - /** - * A preimage has been requested. - **/ - Requested: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - scheduler: { - /** - * The call for the provided hash was not found so the task has been aborted. - **/ - CallUnavailable: AugmentedEvent, id: Option], { task: ITuple<[u32, u32]>, id: Option }>; - /** - * Canceled some task. - **/ - Canceled: AugmentedEvent; - /** - * Dispatched some task. - **/ - Dispatched: AugmentedEvent, id: Option, result: Result], { task: ITuple<[u32, u32]>, id: Option, result: Result }>; - /** - * The given task was unable to be renewed since the agenda is full at that block. - **/ - PeriodicFailed: AugmentedEvent, id: Option], { task: ITuple<[u32, u32]>, id: Option }>; - /** - * The given task can never be executed since it is overweight. - **/ - PermanentlyOverweight: AugmentedEvent, id: Option], { task: ITuple<[u32, u32]>, id: Option }>; - /** - * Scheduled some task. - **/ - Scheduled: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - session: { - /** - * New session has happened. Note that the argument is the session index, not the - * block number as the type might suggest. - **/ - NewSession: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - stateTrieMigration: { - /** - * The auto migration task finished. - **/ - AutoMigrationFinished: AugmentedEvent; - /** - * Migration got halted due to an error or miss-configuration. - **/ - Halted: AugmentedEvent; - /** - * Given number of `(top, child)` keys were migrated respectively, with the given - * `compute`. - **/ - Migrated: AugmentedEvent; - /** - * Some account got slashed by the given amount. - **/ - Slashed: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - structure: { - /** - * Executed call on behalf of the token. - **/ - Executed: AugmentedEvent]>; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - sudo: { - /** - * The \[sudoer\] just switched identity; the old key is supplied if one existed. - **/ - KeyChanged: AugmentedEvent], { oldSudoer: Option }>; - /** - * A sudo just took place. \[result\] - **/ - Sudid: AugmentedEvent], { sudoResult: Result }>; - /** - * A sudo just took place. \[result\] - **/ - SudoAsDone: AugmentedEvent], { sudoResult: Result }>; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - system: { - /** - * `:code` was updated. - **/ - CodeUpdated: AugmentedEvent; - /** - * An extrinsic failed. - **/ - ExtrinsicFailed: AugmentedEvent; - /** - * An extrinsic completed successfully. - **/ - ExtrinsicSuccess: AugmentedEvent; - /** - * An account was reaped. - **/ - KilledAccount: AugmentedEvent; - /** - * A new account was created. - **/ - NewAccount: AugmentedEvent; - /** - * On on-chain remark happened. - **/ - Remarked: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - technicalCommittee: { - /** - * A motion was approved by the required threshold. - **/ - Approved: AugmentedEvent; - /** - * A proposal was closed because its threshold was reached or after its duration was up. - **/ - Closed: AugmentedEvent; - /** - * A motion was not approved by the required threshold. - **/ - Disapproved: AugmentedEvent; - /** - * A motion was executed; result will be `Ok` if it returned without error. - **/ - Executed: AugmentedEvent], { proposalHash: H256, result: Result }>; - /** - * A single member did some action; result will be `Ok` if it returned without error. - **/ - MemberExecuted: AugmentedEvent], { proposalHash: H256, result: Result }>; - /** - * A motion (given hash) has been proposed (by given account) with a threshold (given - * `MemberCount`). - **/ - Proposed: AugmentedEvent; - /** - * A motion (given hash) has been voted on by given account, leaving - * a tally (yes votes and no votes given respectively as `MemberCount`). - **/ - Voted: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - technicalCommitteeMembership: { - /** - * Phantom member, never used. - **/ - Dummy: AugmentedEvent; - /** - * One of the members' keys changed. - **/ - KeyChanged: AugmentedEvent; - /** - * The given member was added; see the transaction for who. - **/ - MemberAdded: AugmentedEvent; - /** - * The given member was removed; see the transaction for who. - **/ - MemberRemoved: AugmentedEvent; - /** - * The membership was reset; see the transaction for who the new set is. - **/ - MembersReset: AugmentedEvent; - /** - * Two members were swapped; see the transaction for who. - **/ - MembersSwapped: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - testUtils: { - BatchCompleted: AugmentedEvent; - ShouldRollback: AugmentedEvent; - ValueIsSet: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - tokens: { - /** - * A balance was set by root. - **/ - BalanceSet: AugmentedEvent; - /** - * Deposited some balance into an account - **/ - Deposited: AugmentedEvent; - /** - * An account was removed whose balance was non-zero but below - * ExistentialDeposit, resulting in an outright loss. - **/ - DustLost: AugmentedEvent; - /** - * An account was created with some free balance. - **/ - Endowed: AugmentedEvent; - Issued: AugmentedEvent; - /** - * Some free balance was locked. - **/ - Locked: AugmentedEvent; - /** - * Some locked funds were unlocked - **/ - LockRemoved: AugmentedEvent; - /** - * Some funds are locked - **/ - LockSet: AugmentedEvent; - Rescinded: AugmentedEvent; - /** - * Some balance was reserved (moved from free to reserved). - **/ - Reserved: AugmentedEvent; - /** - * Some reserved balance was repatriated (moved from reserved to - * another account). - **/ - ReserveRepatriated: AugmentedEvent; - /** - * Some balances were slashed (e.g. due to mis-behavior) - **/ - Slashed: AugmentedEvent; - /** - * The total issuance of an currency has been set - **/ - TotalIssuanceSet: AugmentedEvent; - /** - * Transfer succeeded. - **/ - Transfer: AugmentedEvent; - /** - * Some locked balance was freed. - **/ - Unlocked: AugmentedEvent; - /** - * Some balance was unreserved (moved from reserved to free). - **/ - Unreserved: AugmentedEvent; - /** - * Some balances were withdrawn (e.g. pay for transaction fee) - **/ - Withdrawn: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - transactionPayment: { - /** - * A transaction fee `actual_fee`, of which `tip` was added to the minimum inclusion fee, - * has been paid by `who`. - **/ - TransactionFeePaid: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - treasury: { - /** - * Some funds have been allocated. - **/ - Awarded: AugmentedEvent; - /** - * Some of our funds have been burnt. - **/ - Burnt: AugmentedEvent; - /** - * Some funds have been deposited. - **/ - Deposit: AugmentedEvent; - /** - * New proposal. - **/ - Proposed: AugmentedEvent; - /** - * A proposal was rejected; funds were slashed. - **/ - Rejected: AugmentedEvent; - /** - * Spending has finished; this is the amount that rolls over until next spend. - **/ - Rollover: AugmentedEvent; - /** - * A new spend proposal has been approved. - **/ - SpendApproved: AugmentedEvent; - /** - * We have ended a spend period and will now allocate funds. - **/ - Spending: AugmentedEvent; - /** - * The inactive funds of the pallet have been updated. - **/ - UpdatedInactive: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - utility: { - /** - * Batch of dispatches completed fully with no error. - **/ - BatchCompleted: AugmentedEvent; - /** - * Batch of dispatches completed but has errors. - **/ - BatchCompletedWithErrors: AugmentedEvent; - /** - * Batch of dispatches did not complete fully. Index of first failing dispatch given, as - * well as the error. - **/ - BatchInterrupted: AugmentedEvent; - /** - * A call was dispatched. - **/ - DispatchedAs: AugmentedEvent], { result: Result }>; - /** - * A single item within a Batch of dispatches has completed with no error. - **/ - ItemCompleted: AugmentedEvent; - /** - * A single item within a Batch of dispatches has completed with error. - **/ - ItemFailed: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - vesting: { - /** - * Claimed vesting. - **/ - Claimed: AugmentedEvent; - /** - * Added new vesting schedule. - **/ - VestingScheduleAdded: AugmentedEvent; - /** - * Updated vesting schedules. - **/ - VestingSchedulesUpdated: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - xcmpQueue: { - /** - * Bad XCM format used. - **/ - BadFormat: AugmentedEvent; - /** - * Bad XCM version used. - **/ - BadVersion: AugmentedEvent; - /** - * Some XCM failed. - **/ - Fail: AugmentedEvent; - /** - * An XCM exceeded the individual message weight budget. - **/ - OverweightEnqueued: AugmentedEvent; - /** - * An XCM from the overweight queue was executed with the given actual weight used. - **/ - OverweightServiced: AugmentedEvent; - /** - * Some XCM was executed ok. - **/ - Success: AugmentedEvent; - /** - * An HRMP message was sent to a sibling parachain. - **/ - XcmpMessageSent: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - xTokens: { - /** - * Transferred `MultiAsset` with fee. - **/ - TransferredMultiAssets: AugmentedEvent; - /** - * Generic event - **/ - [key: string]: AugmentedEvent; - }; - } // AugmentedEvents -} // declare module --- a/js-packages/types/src/augment-api-query.ts +++ /dev/null @@ -1,1442 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-chain`, do not edit -/* eslint-disable */ - -// import type lookup before we augment - in some environments -// this is required to allow for ambient/previous definitions -import '@polkadot/api-base/types/storage'; - -import type { ApiTypes, AugmentedQuery, QueryableStorageEntry } from '@polkadot/api-base/types'; -import type { Data } from '@polkadot/types'; -import type { BTreeMap, Bytes, Option, Struct, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; -import type { AnyNumber, ITuple } from '@polkadot/types-codec/types'; -import type { AccountId32, Call, H160, H256 } from '@polkadot/types/interfaces/runtime'; -import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCodeUpgradeAuthorization, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletParachainSystemUnincludedSegmentAncestor, CumulusPalletParachainSystemUnincludedSegmentSegmentTracker, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportDispatchPerDispatchClassWeight, FrameSupportPreimagesBounded, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OpalRuntimeRuntimeCommonSessionKeys, OpalRuntimeRuntimeHoldReason, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensReserveData, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesIdAmount, PalletBalancesReserveData, PalletCollectiveVotes, PalletConfigurationAppPromotionConfiguration, PalletDemocracyMetadataOwner, PalletDemocracyReferendumInfo, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCodeMetadata, PalletEvmContractHelpersSponsoringModeT, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletIdentityRegistrarInfo, PalletIdentityRegistration, PalletNonfungibleItemData, PalletPreimageRequestStatus, PalletRankedCollectiveMemberRecord, PalletRankedCollectiveVoteRecord, PalletReferendaReferendumInfo, PalletSchedulerScheduled, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletXcmQueryStatus, PalletXcmRemoteLockedFungibleRecord, PalletXcmVersionMigrationStage, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV5AbridgedHostConfiguration, PolkadotPrimitivesV5PersistedValidationData, PolkadotPrimitivesV5UpgradeGoAhead, PolkadotPrimitivesV5UpgradeRestriction, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpRuntimeDigest, SpTrieStorageProof, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, StagingXcmVersionedAssetId, StagingXcmVersionedMultiLocation, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyScope, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild } from '@polkadot/types/lookup'; -import type { Observable } from '@polkadot/types/types'; - -export type __AugmentedQuery = AugmentedQuery unknown>; -export type __QueryableStorageEntry = QueryableStorageEntry; - -declare module '@polkadot/api-base/types/storage' { - interface AugmentedQueries { - appPromotion: { - /** - * Stores the `admin` account. Some extrinsics can only be executed if they were signed by `admin`. - **/ - admin: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Pending unstake records for an `Account`. - * - * * **Key** - Staker account. - * * **Value** - Amount of stakes. - **/ - pendingUnstake: AugmentedQuery Observable>>, [u32]> & QueryableStorageEntry; - /** - * Stores a key for record for which the revenue recalculation was performed. - * If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers. - **/ - previousCalculatedRecord: AugmentedQuery Observable>>, []> & QueryableStorageEntry; - /** - * Stores the amount of tokens staked by account in the blocknumber. - * - * * **Key1** - Staker account. - * * **Key2** - Relay block number when the stake was made. - * * **(Balance, BlockNumber)** - Balance of the stake. - * The number of the relay block in which we must perform the interest recalculation - **/ - staked: AugmentedQuery Observable>, [AccountId32, u32]> & QueryableStorageEntry; - /** - * Stores number of stake records for an `Account`. - * - * * **Key** - Staker account. - * * **Value** - Amount of stakes. - **/ - stakesPerAccount: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; - /** - * Stores the total staked amount. - **/ - totalStaked: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - aura: { - /** - * The current authority set. - **/ - authorities: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The current slot of this block. - * - * This will be set in `on_initialize`. - **/ - currentSlot: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - auraExt: { - /** - * Serves as cache for the authorities. - * - * The authorities in AuRa are overwritten in `on_initialize` when we switch to a new session, - * but we require the old authorities to verify the seal when validating a PoV. This will - * always be updated to the latest AuRa authorities in `on_finalize`. - **/ - authorities: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Current slot paired with a number of authored blocks. - * - * Updated on each block initialization. - **/ - slotInfo: AugmentedQuery Observable>>, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - authorship: { - /** - * Author of current block. - **/ - author: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - balances: { - /** - * The Balances pallet example of storing the balance of an account. - * - * # Example - * - * ```nocompile - * impl pallet_balances::Config for Runtime { - * type AccountStore = StorageMapShim, frame_system::Provider, AccountId, Self::AccountData> - * } - * ``` - * - * You can also store the balance of an account in the `System` pallet. - * - * # Example - * - * ```nocompile - * impl pallet_balances::Config for Runtime { - * type AccountStore = System - * } - * ``` - * - * But this comes with tradeoffs, storing account balances in the system pallet stores - * `frame_system` data alongside the account data contrary to storing account balances in the - * `Balances` pallet, which uses a `StorageMap` to store balances data only. - * NOTE: This is only used in the case that this pallet is used to store balances. - **/ - account: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; - /** - * Freeze locks on account balances. - **/ - freezes: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; - /** - * Holds on account balances. - **/ - holds: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; - /** - * The total units of outstanding deactivated balance in the system. - **/ - inactiveIssuance: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Any liquidity locks on some account balances. - * NOTE: Should only be accessed when setting, changing and freeing a lock. - **/ - locks: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; - /** - * Named reserves on some account balances. - **/ - reserves: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; - /** - * The total units issued in the system. - **/ - totalIssuance: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - collatorSelection: { - /** - * The (community, limited) collation candidates. - **/ - candidates: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The invulnerable, fixed collators. - **/ - invulnerables: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Last block authored by collator. - **/ - lastAuthoredBlock: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; - /** - * The (community) collation license holders. - **/ - licenseDepositOf: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - common: { - /** - * Storage of the amount of collection admins. - **/ - adminAmount: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; - /** - * Allowlisted collection users. - **/ - allowlist: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; - /** - * Storage of collection info. - **/ - collectionById: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; - /** - * Storage of collection properties. - **/ - collectionProperties: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; - /** - * Storage of token property permissions of a collection. - **/ - collectionPropertyPermissions: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; - /** - * Storage of the count of created collections. Essentially contains the last collection ID. - **/ - createdCollectionCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Storage of the count of deleted collections. - **/ - destroyedCollectionCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Not used by code, exists only to provide some types to metadata. - **/ - dummyStorageValue: AugmentedQuery Observable>>, []> & QueryableStorageEntry; - /** - * List of collection admins. - **/ - isAdmin: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - configuration: { - appPromomotionConfigurationOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; - collatorSelectionDesiredCollatorsOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; - collatorSelectionKickThresholdOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; - collatorSelectionLicenseBondOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; - minGasPriceOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; - weightToFeeCoefficientOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - council: { - /** - * The current members of the collective. This is stored sorted (just by value). - **/ - members: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The prime member that helps determine the default vote behavior in case of absentations. - **/ - prime: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Proposals so far. - **/ - proposalCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Actual proposal for a given hash, if it's current. - **/ - proposalOf: AugmentedQuery Observable>, [H256]> & QueryableStorageEntry; - /** - * The hashes of the active proposals. - **/ - proposals: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Votes on a given proposal, if it is ongoing. - **/ - voting: AugmentedQuery Observable>, [H256]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - councilMembership: { - /** - * The current membership, stored as an ordered Vec. - **/ - members: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The current prime member, if one exists. - **/ - prime: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - democracy: { - /** - * A record of who vetoed what. Maps proposal hash to a possible existent block number - * (until when it may not be resubmitted) and who vetoed it. - **/ - blacklist: AugmentedQuery Observable]>>>, [H256]> & QueryableStorageEntry; - /** - * Record of all proposals that have been subject to emergency cancellation. - **/ - cancellations: AugmentedQuery Observable, [H256]> & QueryableStorageEntry; - /** - * Those who have locked a deposit. - * - * TWOX-NOTE: Safe, as increasing integer keys are safe. - **/ - depositOf: AugmentedQuery Observable, u128]>>>, [u32]> & QueryableStorageEntry; - /** - * True if the last referendum tabled was submitted externally. False if it was a public - * proposal. - **/ - lastTabledWasExternal: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The lowest referendum index representing an unbaked referendum. Equal to - * `ReferendumCount` if there isn't a unbaked referendum. - **/ - lowestUnbaked: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * General information concerning any proposal or referendum. - * The `PreimageHash` refers to the preimage of the `Preimages` provider which can be a JSON - * dump or IPFS hash of a JSON file. - * - * Consider a garbage collection for a metadata of finished referendums to `unrequest` (remove) - * large preimages. - **/ - metadataOf: AugmentedQuery Observable>, [PalletDemocracyMetadataOwner]> & QueryableStorageEntry; - /** - * The referendum to be tabled whenever it would be valid to table an external proposal. - * This happens when a referendum needs to be tabled and one of two conditions are met: - * - `LastTabledWasExternal` is `false`; or - * - `PublicProps` is empty. - **/ - nextExternal: AugmentedQuery Observable>>, []> & QueryableStorageEntry; - /** - * The number of (public) proposals that have been made so far. - **/ - publicPropCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The public proposals. Unsorted. The second item is the proposal. - **/ - publicProps: AugmentedQuery Observable>>, []> & QueryableStorageEntry; - /** - * The next free referendum index, aka the number of referenda started so far. - **/ - referendumCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Information concerning any given referendum. - * - * TWOX-NOTE: SAFE as indexes are not under an attacker’s control. - **/ - referendumInfoOf: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; - /** - * All votes for a particular voter. We store the balance for the number of votes that we - * have recorded. The second item is the total amount of delegations, that will be added. - * - * TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway. - **/ - votingOf: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - dmpQueue: { - /** - * The configuration. - **/ - configuration: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Counter for the related counted storage map - **/ - counterForOverweight: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The overweight messages. - **/ - overweight: AugmentedQuery Observable>>, [u64]> & QueryableStorageEntry; - /** - * The page index. - **/ - pageIndex: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The queue pages. - **/ - pages: AugmentedQuery Observable>>, [u32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - ethereum: { - blockHash: AugmentedQuery Observable, [U256]> & QueryableStorageEntry; - /** - * The current Ethereum block. - **/ - currentBlock: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The current Ethereum receipts. - **/ - currentReceipts: AugmentedQuery Observable>>, []> & QueryableStorageEntry; - /** - * The current transaction statuses. - **/ - currentTransactionStatuses: AugmentedQuery Observable>>, []> & QueryableStorageEntry; - /** - * Injected transactions should have unique nonce, here we store current - **/ - injectedNonce: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Current building block's transactions and receipts. - **/ - pending: AugmentedQuery Observable>>, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - evm: { - accountCodes: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; - accountCodesMetadata: AugmentedQuery Observable>, [H160]> & QueryableStorageEntry; - accountStorages: AugmentedQuery Observable, [H160, H256]> & QueryableStorageEntry; - /** - * Written on log, reset after transaction - * Should be empty between transactions - **/ - currentLogs: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - evmContractHelpers: { - /** - * Storage for users that allowed for sponsorship. - * - * ### Usage - * Prefer to delete record from storage if user no more allowed for sponsorship. - * - * * **Key1** - contract address. - * * **Key2** - user that allowed for sponsorship. - * * **Value** - allowance for sponsorship. - **/ - allowlist: AugmentedQuery Observable, [H160, H160]> & QueryableStorageEntry; - /** - * Storege for contracts with [`Allowlisted`](SponsoringModeT::Allowlisted) sponsoring mode. - * - * ### Usage - * Prefer to delete collection from storage if mode chaged to non `Allowlisted`, than set **Value** to **false**. - * - * * **Key** - contract address. - * * **Value** - is contract in [`Allowlisted`](SponsoringModeT::Allowlisted) mode. - **/ - allowlistEnabled: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; - /** - * Store owner for contract. - * - * * **Key** - contract address. - * * **Value** - owner for contract. - **/ - owner: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; - /** - * Deprecated: this storage is deprecated - **/ - selfSponsoring: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; - sponsorBasket: AugmentedQuery Observable>, [H160, H160]> & QueryableStorageEntry; - /** - * Store for contract sponsorship state. - * - * * **Key** - contract address. - * * **Value** - sponsorship state. - **/ - sponsoring: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; - /** - * Storage for last sponsored block. - * - * * **Key1** - contract address. - * * **Key2** - sponsored user address. - * * **Value** - last sponsored block number. - **/ - sponsoringFeeLimit: AugmentedQuery Observable>, [H160]> & QueryableStorageEntry; - /** - * Store for sponsoring mode. - * - * ### Usage - * Prefer to delete collection from storage if mode chaged to [`Disabled`](SponsoringModeT::Disabled). - * - * * **Key** - contract address. - * * **Value** - [`sponsoring mode`](SponsoringModeT). - **/ - sponsoringMode: AugmentedQuery Observable>, [H160]> & QueryableStorageEntry; - /** - * Storage for sponsoring rate limit in blocks. - * - * * **Key** - contract address. - * * **Value** - amount of sponsored blocks. - **/ - sponsoringRateLimit: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - evmMigration: { - migrationPending: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - fellowshipCollective: { - /** - * The index of each ranks's member into the group of members who have at least that rank. - **/ - idToIndex: AugmentedQuery Observable>, [u16, AccountId32]> & QueryableStorageEntry; - /** - * The members in the collective by index. All indices in the range `0..MemberCount` will - * return `Some`, however a member's index is not guaranteed to remain unchanged over time. - **/ - indexToId: AugmentedQuery Observable>, [u16, u32]> & QueryableStorageEntry; - /** - * The number of members in the collective who have at least the rank according to the index - * of the vec. - **/ - memberCount: AugmentedQuery Observable, [u16]> & QueryableStorageEntry; - /** - * The current members of the collective. - **/ - members: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; - /** - * Votes on a given proposal, if it is ongoing. - **/ - voting: AugmentedQuery Observable>, [u32, AccountId32]> & QueryableStorageEntry; - votingCleanup: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - fellowshipReferenda: { - /** - * The number of referenda being decided currently. - **/ - decidingCount: AugmentedQuery Observable, [u16]> & QueryableStorageEntry; - /** - * The metadata is a general information concerning the referendum. - * The `PreimageHash` refers to the preimage of the `Preimages` provider which can be a JSON - * dump or IPFS hash of a JSON file. - * - * Consider a garbage collection for a metadata of finished referendums to `unrequest` (remove) - * large preimages. - **/ - metadataOf: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; - /** - * The next free referendum index, aka the number of referenda started so far. - **/ - referendumCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Information concerning any given referendum. - **/ - referendumInfoFor: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; - /** - * The sorted list of referenda ready to be decided but not yet being decided, ordered by - * conviction-weighted approvals. - * - * This should be empty if `DecidingCount` is less than `TrackInfo::max_deciding`. - **/ - trackQueue: AugmentedQuery Observable>>, [u16]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - foreignAssets: { - /** - * The storages for assets to fungible collection binding - * - **/ - assetBinding: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; - /** - * The storages for AssetMetadatas. - * - * AssetMetadatas: map AssetIds => Option - **/ - assetMetadatas: AugmentedQuery Observable>, [PalletForeignAssetsAssetId]> & QueryableStorageEntry; - /** - * The storages for MultiLocations. - * - * ForeignAssetLocations: map ForeignAssetId => Option - **/ - foreignAssetLocations: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; - /** - * The storages for CurrencyIds. - * - * LocationToCurrencyIds: map MultiLocation => Option - **/ - locationToCurrencyIds: AugmentedQuery Observable>, [StagingXcmV3MultiLocation]> & QueryableStorageEntry; - /** - * Next available Foreign AssetId ID. - * - * NextForeignAssetId: ForeignAssetId - **/ - nextForeignAssetId: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - fungible: { - /** - * Storage for assets delegated to a limited extent to other users. - **/ - allowance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; - /** - * Amount of tokens owned by an account inside a collection. - **/ - balance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; - /** - * Total amount of fungible tokens inside a collection. - **/ - totalSupply: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - identity: { - /** - * Information that is pertinent to identify the entity behind an account. - * - * TWOX-NOTE: OK ― `AccountId` is a secure hash. - **/ - identityOf: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; - /** - * The set of registrars. Not expected to get very big as can only be added through a - * special origin (likely a council motion). - * - * The index into this can be cast to `RegistrarIndex` to get a valid value. - **/ - registrars: AugmentedQuery Observable>>, []> & QueryableStorageEntry; - /** - * Alternative "sub" identities of this account. - * - * The first item is the deposit, the second is a vector of the accounts. - * - * TWOX-NOTE: OK ― `AccountId` is a secure hash. - **/ - subsOf: AugmentedQuery Observable]>>, [AccountId32]> & QueryableStorageEntry; - /** - * The super-identity of an alternative "sub" identity together with its name, within that - * context. If the account is not some other account's sub-identity, then just `None`. - **/ - superOf: AugmentedQuery Observable>>, [AccountId32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - inflation: { - /** - * Current inflation for `InflationBlockInterval` number of blocks - **/ - blockInflation: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Next target (relay) block when inflation will be applied - **/ - nextInflationBlock: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Next target (relay) block when inflation is recalculated - **/ - nextRecalculationBlock: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Relay block when inflation has started - **/ - startBlock: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * starting year total issuance - **/ - startingYearTotalIssuance: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - maintenance: { - enabled: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - nonfungible: { - /** - * Amount of tokens owned by an account in a collection. - **/ - accountBalance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; - /** - * Allowance set by a token owner for another user to perform one of certain transactions on a token. - **/ - allowance: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; - /** - * Operator set by a wallet owner that could perform certain transactions on all tokens in the wallet. - **/ - collectionAllowance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; - /** - * Used to enumerate tokens owned by account. - **/ - owned: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry; - /** - * Custom data of a token that is serialized to bytes, - * primarily reserved for on-chain operations, - * normally obscured from the external users. - * - * Auxiliary properties are slightly different from - * usual [`TokenProperties`] due to an unlimited number - * and separately stored and written-to key-value pairs. - * - * Currently unused. - **/ - tokenAuxProperties: AugmentedQuery Observable>, [u32, u32, UpDataStructsPropertyScope, Bytes]> & QueryableStorageEntry; - /** - * Used to enumerate token's children. - **/ - tokenChildren: AugmentedQuery | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array]) => Observable, [u32, u32, ITuple<[u32, u32]>]> & QueryableStorageEntry]>; - /** - * Token data, used to partially describe a token. - **/ - tokenData: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; - /** - * Map of key-value pairs, describing the metadata of a token. - **/ - tokenProperties: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; - /** - * Amount of burnt tokens in a collection. - **/ - tokensBurnt: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; - /** - * Total amount of minted tokens in a collection. - **/ - tokensMinted: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - parachainInfo: { - parachainId: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - parachainSystem: { - /** - * Storage field that keeps track of bandwidth used by the unincluded segment along with the - * latest the latest HRMP watermark. Used for limiting the acceptance of new blocks with - * respect to relay chain constraints. - **/ - aggregatedUnincludedSegment: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The number of HRMP messages we observed in `on_initialize` and thus used that number for - * announcing the weight of `on_initialize` and `on_finalize`. - **/ - announcedHrmpMessagesPerCandidate: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The next authorized upgrade, if there is one. - **/ - authorizedUpgrade: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * A custom head data that should be returned as result of `validate_block`. - * - * See `Pallet::set_custom_validation_head_data` for more information. - **/ - customValidationHeadData: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Were the validation data set to notify the relay chain? - **/ - didSetValidationCode: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The parachain host configuration that was obtained from the relay parent. - * - * This field is meant to be updated each block with the validation data inherent. Therefore, - * before processing of the inherent, e.g. in `on_initialize` this data may be stale. - * - * This data is also absent from the genesis. - **/ - hostConfiguration: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * HRMP messages that were sent in a block. - * - * This will be cleared in `on_initialize` of each new block. - **/ - hrmpOutboundMessages: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * HRMP watermark that was set in a block. - * - * This will be cleared in `on_initialize` of each new block. - **/ - hrmpWatermark: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The last downward message queue chain head we have observed. - * - * This value is loaded before and saved after processing inbound downward messages carried - * by the system inherent. - **/ - lastDmqMqcHead: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The message queue chain heads we have observed per each channel incoming channel. - * - * This value is loaded before and saved after processing inbound downward messages carried - * by the system inherent. - **/ - lastHrmpMqcHeads: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The relay chain block number associated with the last parachain block. - **/ - lastRelayChainBlockNumber: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Validation code that is set by the parachain and is to be communicated to collator and - * consequently the relay-chain. - * - * This will be cleared in `on_initialize` of each new block if no other pallet already set - * the value. - **/ - newValidationCode: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Upward messages that are still pending and not yet send to the relay chain. - **/ - pendingUpwardMessages: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * In case of a scheduled upgrade, this storage field contains the validation code to be - * applied. - * - * As soon as the relay chain gives us the go-ahead signal, we will overwrite the - * [`:code`][sp_core::storage::well_known_keys::CODE] which will result the next block process - * with the new validation code. This concludes the upgrade process. - **/ - pendingValidationCode: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Number of downward messages processed in a block. - * - * This will be cleared in `on_initialize` of each new block. - **/ - processedDownwardMessages: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The state proof for the last relay parent block. - * - * This field is meant to be updated each block with the validation data inherent. Therefore, - * before processing of the inherent, e.g. in `on_initialize` this data may be stale. - * - * This data is also absent from the genesis. - **/ - relayStateProof: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The snapshot of some state related to messaging relevant to the current parachain as per - * the relay parent. - * - * This field is meant to be updated each block with the validation data inherent. Therefore, - * before processing of the inherent, e.g. in `on_initialize` this data may be stale. - * - * This data is also absent from the genesis. - **/ - relevantMessagingState: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The weight we reserve at the beginning of the block for processing DMP messages. This - * overrides the amount set in the Config trait. - **/ - reservedDmpWeightOverride: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The weight we reserve at the beginning of the block for processing XCMP messages. This - * overrides the amount set in the Config trait. - **/ - reservedXcmpWeightOverride: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Latest included block descendants the runtime accepted. In other words, these are - * ancestors of the currently executing block which have not been included in the observed - * relay-chain state. - * - * The segment length is limited by the capacity returned from the [`ConsensusHook`] configured - * in the pallet. - **/ - unincludedSegment: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Optional upgrade go-ahead signal from the relay-chain. - * - * This storage item is a mirror of the corresponding value for the current parachain from the - * relay-chain. This value is ephemeral which means it doesn't hit the storage. This value is - * set after the inherent. - **/ - upgradeGoAhead: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * An option which indicates if the relay-chain restricts signalling a validation code upgrade. - * In other words, if this is `Some` and [`NewValidationCode`] is `Some` then the produced - * candidate will be invalid. - * - * This storage item is a mirror of the corresponding value for the current parachain from the - * relay-chain. This value is ephemeral which means it doesn't hit the storage. This value is - * set after the inherent. - **/ - upgradeRestrictionSignal: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Upward messages that were sent in a block. - * - * This will be cleared in `on_initialize` of each new block. - **/ - upwardMessages: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The [`PersistedValidationData`] set for this block. - * This value is expected to be set only once per block and it's never stored - * in the trie. - **/ - validationData: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - polkadotXcm: { - /** - * The existing asset traps. - * - * Key is the blake2 256 hash of (origin, versioned `MultiAssets`) pair. Value is the number of - * times this pair has been trapped (usually just 1 if it exists at all). - **/ - assetTraps: AugmentedQuery Observable, [H256]> & QueryableStorageEntry; - /** - * The current migration's stage, if any. - **/ - currentMigration: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Fungible assets which we know are locked on this chain. - **/ - lockedFungibles: AugmentedQuery Observable>>>, [AccountId32]> & QueryableStorageEntry; - /** - * The ongoing queries. - **/ - queries: AugmentedQuery Observable>, [u64]> & QueryableStorageEntry; - /** - * The latest available query index. - **/ - queryCounter: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Fungible assets which we know are locked on a remote chain. - **/ - remoteLockedFungibles: AugmentedQuery Observable>, [u32, AccountId32, StagingXcmVersionedAssetId]> & QueryableStorageEntry; - /** - * Default version to encode XCM when latest version of destination is unknown. If `None`, - * then the destinations whose XCM version is unknown are considered unreachable. - **/ - safeXcmVersion: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The Latest versions that we know various locations support. - **/ - supportedVersion: AugmentedQuery Observable>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry; - /** - * Destinations whose latest XCM version we would like to know. Duplicates not allowed, and - * the `u32` counter is the number of times that a send to the destination has been attempted, - * which is used as a prioritization. - **/ - versionDiscoveryQueue: AugmentedQuery Observable>>, []> & QueryableStorageEntry; - /** - * All locations that we have requested version notifications from. - **/ - versionNotifiers: AugmentedQuery Observable>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry; - /** - * The target locations that are subscribed to our version changes, as well as the most recent - * of our versions we informed them of. - **/ - versionNotifyTargets: AugmentedQuery Observable>>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry; - /** - * Global suspension state of the XCM executor. - **/ - xcmExecutionSuspended: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - preimage: { - preimageFor: AugmentedQuery | [H256 | string | Uint8Array, u32 | AnyNumber | Uint8Array]) => Observable>, [ITuple<[H256, u32]>]> & QueryableStorageEntry]>; - /** - * The request status of a given hash. - **/ - statusFor: AugmentedQuery Observable>, [H256]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - refungible: { - /** - * Amount of tokens (not pieces) partially owned by an account within a collection. - **/ - accountBalance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; - /** - * Allowance set by a token owner for another user to perform one of certain transactions on a number of pieces of a token. - **/ - allowance: AugmentedQuery Observable, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; - /** - * Amount of token pieces owned by account. - **/ - balance: AugmentedQuery Observable, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; - /** - * Spender set by a wallet owner that could perform certain transactions on all tokens in the wallet. - **/ - collectionAllowance: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry; - /** - * Used to enumerate tokens owned by account. - **/ - owned: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry; - /** - * Amount of pieces a refungible token is split into. - **/ - tokenProperties: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; - /** - * Amount of tokens burnt in a collection. - **/ - tokensBurnt: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; - /** - * Total amount of minted tokens in a collection. - **/ - tokensMinted: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; - /** - * Total amount of pieces for token - **/ - totalSupply: AugmentedQuery Observable, [u32, u32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - scheduler: { - /** - * Items to be executed, indexed by the block number that they should be executed on. - **/ - agenda: AugmentedQuery Observable>>, [u32]> & QueryableStorageEntry; - incompleteSince: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Lookup from a name to the block number and index of the task. - * - * For v3 -> v4 the previously unbounded identities are Blake2-256 hashed to form the v4 - * identities. - **/ - lookup: AugmentedQuery Observable>>, [U8aFixed]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - session: { - /** - * Current index of the session. - **/ - currentIndex: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Indices of disabled validators. - * - * The vec is always kept sorted so that we can find whether a given validator is - * disabled using binary search. It gets cleared when `on_session_ending` returns - * a new set of identities. - **/ - disabledValidators: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The owner of a key. The key is the `KeyTypeId` + the encoded key. - **/ - keyOwner: AugmentedQuery | [SpCoreCryptoKeyTypeId | string | Uint8Array, Bytes | string | Uint8Array]) => Observable>, [ITuple<[SpCoreCryptoKeyTypeId, Bytes]>]> & QueryableStorageEntry]>; - /** - * The next session keys for a validator. - **/ - nextKeys: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; - /** - * True if the underlying economic identities or weighting behind the validators - * has changed in the queued validator set. - **/ - queuedChanged: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The queued keys for the next session. When the next session begins, these keys - * will be used to determine the validator's session keys. - **/ - queuedKeys: AugmentedQuery Observable>>, []> & QueryableStorageEntry; - /** - * The current set of validators. - **/ - validators: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - stateTrieMigration: { - /** - * The limits that are imposed on automatic migrations. - * - * If set to None, then no automatic migration happens. - **/ - autoLimits: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Migration progress. - * - * This stores the snapshot of the last migrated keys. It can be set into motion and move - * forward by any of the means provided by this pallet. - **/ - migrationProcess: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The maximum limits that the signed migration could use. - * - * If not set, no signed submission is allowed. - **/ - signedMigrationMaxLimits: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - sudo: { - /** - * The `AccountId` of the sudo key. - **/ - key: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - system: { - /** - * The full account information for a particular account ID. - **/ - account: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; - /** - * Total length (in bytes) for all extrinsics put together, for the current block. - **/ - allExtrinsicsLen: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Map of block numbers to block hashes. - **/ - blockHash: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; - /** - * The current weight for the block. - **/ - blockWeight: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Digest of the current block, also part of the block header. - **/ - digest: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The number of events in the `Events` list. - **/ - eventCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Events deposited for the current block. - * - * NOTE: The item is unbound and should therefore never be read on chain. - * It could otherwise inflate the PoV size of a block. - * - * Events have a large in-memory size. Box the events to not go out-of-memory - * just in case someone still reads them from within the runtime. - **/ - events: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Mapping between a topic (represented by T::Hash) and a vector of indexes - * of events in the `>` list. - * - * All topic vectors have deterministic storage locations depending on the topic. This - * allows light-clients to leverage the changes trie storage tracking mechanism and - * in case of changes fetch the list of events of interest. - * - * The value has the type `(BlockNumberFor, EventIndex)` because if we used only just - * the `EventIndex` then in case if the topic has the same contents on the next block - * no notification will be triggered thus the event might be lost. - **/ - eventTopics: AugmentedQuery Observable>>, [H256]> & QueryableStorageEntry; - /** - * The execution phase of the block. - **/ - executionPhase: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Total extrinsics count for the current block. - **/ - extrinsicCount: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Extrinsics data for the current block (maps an extrinsic's index to its data). - **/ - extrinsicData: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; - /** - * Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened. - **/ - lastRuntimeUpgrade: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The current block number being processed. Set by `execute_block`. - **/ - number: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Hash of the previous block. - **/ - parentHash: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * True if we have upgraded so that AccountInfo contains three types of `RefCount`. False - * (default) if not. - **/ - upgradedToTripleRefCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * True if we have upgraded so that `type RefCount` is `u32`. False (default) if not. - **/ - upgradedToU32RefCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - technicalCommittee: { - /** - * The current members of the collective. This is stored sorted (just by value). - **/ - members: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The prime member that helps determine the default vote behavior in case of absentations. - **/ - prime: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Proposals so far. - **/ - proposalCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Actual proposal for a given hash, if it's current. - **/ - proposalOf: AugmentedQuery Observable>, [H256]> & QueryableStorageEntry; - /** - * The hashes of the active proposals. - **/ - proposals: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Votes on a given proposal, if it is ongoing. - **/ - voting: AugmentedQuery Observable>, [H256]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - technicalCommitteeMembership: { - /** - * The current membership, stored as an ordered Vec. - **/ - members: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The current prime member, if one exists. - **/ - prime: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - testUtils: { - enabled: AugmentedQuery Observable, []> & QueryableStorageEntry; - testValue: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - timestamp: { - /** - * Did the timestamp get updated in this block? - **/ - didUpdate: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Current time for the current block. - **/ - now: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - tokens: { - /** - * The balance of a token type under an account. - * - * NOTE: If the total is ever zero, decrease account ref account. - * - * NOTE: This is only used in the case that this module is used to store - * balances. - **/ - accounts: AugmentedQuery Observable, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry; - /** - * Any liquidity locks of a token type under an account. - * NOTE: Should only be accessed when setting, changing and freeing a lock. - **/ - locks: AugmentedQuery Observable>, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry; - /** - * Named reserves on some account balances. - **/ - reserves: AugmentedQuery Observable>, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry; - /** - * The total issuance of a token type. - **/ - totalIssuance: AugmentedQuery Observable, [PalletForeignAssetsAssetId]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - transactionPayment: { - nextFeeMultiplier: AugmentedQuery Observable, []> & QueryableStorageEntry; - storageVersion: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - treasury: { - /** - * Proposal indices that have been approved but not yet awarded. - **/ - approvals: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The amount which has been reported as inactive to Currency. - **/ - deactivated: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Number of proposals that have been made. - **/ - proposalCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Proposals that have been made. - **/ - proposals: AugmentedQuery Observable>, [u32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - unique: { - /** - * Used for migrations - **/ - chainVersion: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * (Collection id (controlled?2), who created (real)) - * TODO: Off chain worker should remove from this map when collection gets removed - **/ - createItemBasket: AugmentedQuery | [u32 | AnyNumber | Uint8Array, AccountId32 | string | Uint8Array]) => Observable>, [ITuple<[u32, AccountId32]>]> & QueryableStorageEntry]>; - /** - * Last sponsoring of fungible tokens approval in a collection - **/ - fungibleApproveBasket: AugmentedQuery Observable>, [u32, AccountId32]> & QueryableStorageEntry; - /** - * Collection id (controlled?2), owning user (real) - **/ - fungibleTransferBasket: AugmentedQuery Observable>, [u32, AccountId32]> & QueryableStorageEntry; - /** - * Last sponsoring of NFT approval in a collection - **/ - nftApproveBasket: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; - /** - * Collection id (controlled?2), token id (controlled?2) - **/ - nftTransferBasket: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; - /** - * Last sponsoring of RFT approval in a collection - **/ - refungibleApproveBasket: AugmentedQuery Observable>, [u32, u32, AccountId32]> & QueryableStorageEntry; - /** - * Collection id (controlled?2), token id (controlled?2) - **/ - reFungibleTransferBasket: AugmentedQuery Observable>, [u32, u32, AccountId32]> & QueryableStorageEntry; - /** - * Last sponsoring of token property setting // todo:doc rephrase this and the following - **/ - tokenPropertyBasket: AugmentedQuery Observable>, [u32, u32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - vesting: { - /** - * Vesting schedules of an account. - * - * VestingSchedules: map AccountId => Vec - **/ - vestingSchedules: AugmentedQuery Observable>, [AccountId32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - xcmpQueue: { - /** - * Counter for the related counted storage map - **/ - counterForOverweight: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Inbound aggregate XCMP messages. It can only be one per ParaId/block. - **/ - inboundXcmpMessages: AugmentedQuery Observable, [u32, u32]> & QueryableStorageEntry; - /** - * Status of the inbound XCMP channels. - **/ - inboundXcmpStatus: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The messages outbound in a given XCMP channel. - **/ - outboundXcmpMessages: AugmentedQuery Observable, [u32, u16]> & QueryableStorageEntry; - /** - * The non-empty XCMP channels in order of becoming non-empty, and the index of the first - * and last outbound message. If the two indices are equal, then it indicates an empty - * queue and there must be a non-`Ok` `OutboundStatus`. We assume queues grow no greater - * than 65535 items. Queue indices for normal messages begin at one; zero is reserved in - * case of the need to send a high-priority signal message this block. - * The bool is true if there is a signal message waiting to be sent. - **/ - outboundXcmpStatus: AugmentedQuery Observable>, []> & QueryableStorageEntry; - /** - * The messages that exceeded max individual message weight budget. - * - * These message stay in this storage map until they are manually dispatched via - * `service_overweight`. - **/ - overweight: AugmentedQuery Observable>>, [u64]> & QueryableStorageEntry; - /** - * The number of overweight messages ever recorded in `Overweight`. Also doubles as the next - * available free overweight index. - **/ - overweightCount: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * The configuration which controls the dynamics of the outbound queue. - **/ - queueConfig: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Whether or not the XCMP queue is suspended from executing incoming XCMs or not. - **/ - queueSuspended: AugmentedQuery Observable, []> & QueryableStorageEntry; - /** - * Any signal messages waiting to be sent. - **/ - signalMessages: AugmentedQuery Observable, [u32]> & QueryableStorageEntry; - /** - * Generic query - **/ - [key: string]: QueryableStorageEntry; - }; - } // AugmentedQueries -} // declare module --- a/js-packages/types/src/augment-api-rpc.ts +++ /dev/null @@ -1,618 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-chain`, do not edit -/* eslint-disable */ - -// import type lookup before we augment - in some environments -// this is required to allow for ambient/previous definitions -import '@polkadot/rpc-core/types/jsonrpc'; - -import type { PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCollectionLimits, UpDataStructsCollectionStats, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsRpcCollection, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo } from './default/index.js'; -import type { AugmentedRpc } from '@polkadot/rpc-core/types'; -import type { Metadata, StorageKey } from '@polkadot/types'; -import type { Bytes, HashMap, Json, Null, Option, Text, U256, U64, Vec, bool, f64, u32, u64 } from '@polkadot/types-codec'; -import type { AnyNumber, Codec } from '@polkadot/types-codec/types'; -import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author'; -import type { EpochAuthorship } from '@polkadot/types/interfaces/babe'; -import type { BeefySignedCommitment } from '@polkadot/types/interfaces/beefy'; -import type { BlockHash } from '@polkadot/types/interfaces/chain'; -import type { PrefixedStorageKey } from '@polkadot/types/interfaces/childstate'; -import type { AuthorityId } from '@polkadot/types/interfaces/consensus'; -import type { CodeUploadRequest, CodeUploadResult, ContractCallRequest, ContractExecResult, ContractInstantiateResult, InstantiateRequestV1 } from '@polkadot/types/interfaces/contracts'; -import type { BlockStats } from '@polkadot/types/interfaces/dev'; -import type { CreatedBlock } from '@polkadot/types/interfaces/engine'; -import type { EthAccount, EthCallRequest, EthFeeHistory, EthFilter, EthFilterChanges, EthLog, EthReceipt, EthRichBlock, EthSubKind, EthSubParams, EthSyncStatus, EthTransaction, EthTransactionRequest, EthWork } from '@polkadot/types/interfaces/eth'; -import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics'; -import type { EncodedFinalityProofs, JustificationNotification, ReportedRoundStates } from '@polkadot/types/interfaces/grandpa'; -import type { MmrHash, MmrLeafBatchProof } from '@polkadot/types/interfaces/mmr'; -import type { StorageKind } from '@polkadot/types/interfaces/offchain'; -import type { FeeDetails, RuntimeDispatchInfoV1 } from '@polkadot/types/interfaces/payment'; -import type { RpcMethods } from '@polkadot/types/interfaces/rpc'; -import type { AccountId, BlockNumber, H160, H256, H64, Hash, Header, Index, Justification, KeyValue, SignedBlock, StorageData } from '@polkadot/types/interfaces/runtime'; -import type { MigrationStatusResult, ReadProof, RuntimeVersion, TraceBlockResponse } from '@polkadot/types/interfaces/state'; -import type { ApplyExtrinsicResult, ChainProperties, ChainType, Health, NetworkState, NodeRole, PeerInfo, SyncState } from '@polkadot/types/interfaces/system'; -import type { IExtrinsic, Observable } from '@polkadot/types/types'; - -export type __AugmentedRpc = AugmentedRpc<() => unknown>; - -declare module '@polkadot/rpc-core/types/jsonrpc' { - interface RpcInterface { - author: { - /** - * Returns true if the keystore has private keys for the given public key and key type. - **/ - hasKey: AugmentedRpc<(publicKey: Bytes | string | Uint8Array, keyType: Text | string) => Observable>; - /** - * Returns true if the keystore has private keys for the given session public keys. - **/ - hasSessionKeys: AugmentedRpc<(sessionKeys: Bytes | string | Uint8Array) => Observable>; - /** - * Insert a key into the keystore. - **/ - insertKey: AugmentedRpc<(keyType: Text | string, suri: Text | string, publicKey: Bytes | string | Uint8Array) => Observable>; - /** - * Returns all pending extrinsics, potentially grouped by sender - **/ - pendingExtrinsics: AugmentedRpc<() => Observable>>; - /** - * Remove given extrinsic from the pool and temporarily ban it to prevent reimporting - **/ - removeExtrinsic: AugmentedRpc<(bytesOrHash: Vec | (ExtrinsicOrHash | { Hash: any } | { Extrinsic: any } | string | Uint8Array)[]) => Observable>>; - /** - * Generate new session keys and returns the corresponding public keys - **/ - rotateKeys: AugmentedRpc<() => Observable>; - /** - * Submit and subscribe to watch an extrinsic until unsubscribed - **/ - submitAndWatchExtrinsic: AugmentedRpc<(extrinsic: Extrinsic | IExtrinsic | string | Uint8Array) => Observable>; - /** - * Submit a fully formatted extrinsic for block inclusion - **/ - submitExtrinsic: AugmentedRpc<(extrinsic: Extrinsic | IExtrinsic | string | Uint8Array) => Observable>; - }; - babe: { - /** - * Returns data about which slots (primary or secondary) can be claimed in the current epoch with the keys in the keystore - **/ - epochAuthorship: AugmentedRpc<() => Observable>>; - }; - beefy: { - /** - * Returns hash of the latest BEEFY finalized block as seen by this client. - **/ - getFinalizedHead: AugmentedRpc<() => Observable>; - /** - * Returns the block most recently finalized by BEEFY, alongside side its justification. - **/ - subscribeJustifications: AugmentedRpc<() => Observable>; - }; - chain: { - /** - * Get header and body of a relay chain block - **/ - getBlock: AugmentedRpc<(hash?: BlockHash | string | Uint8Array) => Observable>; - /** - * Get the block hash for a specific block - **/ - getBlockHash: AugmentedRpc<(blockNumber?: BlockNumber | AnyNumber | Uint8Array) => Observable>; - /** - * Get hash of the last finalized block in the canon chain - **/ - getFinalizedHead: AugmentedRpc<() => Observable>; - /** - * Retrieves the header for a specific block - **/ - getHeader: AugmentedRpc<(hash?: BlockHash | string | Uint8Array) => Observable
>; - /** - * Retrieves the newest header via subscription - **/ - subscribeAllHeads: AugmentedRpc<() => Observable
>; - /** - * Retrieves the best finalized header via subscription - **/ - subscribeFinalizedHeads: AugmentedRpc<() => Observable
>; - /** - * Retrieves the best header via subscription - **/ - subscribeNewHeads: AugmentedRpc<() => Observable
>; - }; - childstate: { - /** - * Returns the keys with prefix from a child storage, leave empty to get all the keys - **/ - getKeys: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, prefix: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable>>; - /** - * Returns the keys with prefix from a child storage with pagination support - **/ - getKeysPaged: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, prefix: StorageKey | string | Uint8Array | any, count: u32 | AnyNumber | Uint8Array, startKey?: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable>>; - /** - * Returns a child storage entry at a specific block state - **/ - getStorage: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable>>; - /** - * Returns child storage entries for multiple keys at a specific block state - **/ - getStorageEntries: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, keys: Vec | (StorageKey | string | Uint8Array | any)[], at?: Hash | string | Uint8Array) => Observable>>>; - /** - * Returns the hash of a child storage entry at a block state - **/ - getStorageHash: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable>>; - /** - * Returns the size of a child storage entry at a block state - **/ - getStorageSize: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable>>; - }; - contracts: { - /** - * @deprecated Use the runtime interface `api.call.contractsApi.call` instead - * Executes a call to a contract - **/ - call: AugmentedRpc<(callRequest: ContractCallRequest | { origin?: any; dest?: any; value?: any; gasLimit?: any; storageDepositLimit?: any; inputData?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; - /** - * @deprecated Use the runtime interface `api.call.contractsApi.getStorage` instead - * Returns the value under a specified storage key in a contract - **/ - getStorage: AugmentedRpc<(address: AccountId | string | Uint8Array, key: H256 | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>>; - /** - * @deprecated Use the runtime interface `api.call.contractsApi.instantiate` instead - * Instantiate a new contract - **/ - instantiate: AugmentedRpc<(request: InstantiateRequestV1 | { origin?: any; value?: any; gasLimit?: any; code?: any; data?: any; salt?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; - /** - * @deprecated Not available in newer versions of the contracts interfaces - * Returns the projected time a given contract will be able to sustain paying its rent - **/ - rentProjection: AugmentedRpc<(address: AccountId | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>>; - /** - * @deprecated Use the runtime interface `api.call.contractsApi.uploadCode` instead - * Upload new code without instantiating a contract from it - **/ - uploadCode: AugmentedRpc<(uploadRequest: CodeUploadRequest | { origin?: any; code?: any; storageDepositLimit?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; - }; - dev: { - /** - * Reexecute the specified `block_hash` and gather statistics while doing so - **/ - getBlockStats: AugmentedRpc<(at: Hash | string | Uint8Array) => Observable>>; - }; - engine: { - /** - * Instructs the manual-seal authorship task to create a new block - **/ - createBlock: AugmentedRpc<(createEmpty: bool | boolean | Uint8Array, finalize: bool | boolean | Uint8Array, parentHash?: BlockHash | string | Uint8Array) => Observable>; - /** - * Instructs the manual-seal authorship task to finalize a block - **/ - finalizeBlock: AugmentedRpc<(hash: BlockHash | string | Uint8Array, justification?: Justification) => Observable>; - }; - eth: { - /** - * Returns accounts list. - **/ - accounts: AugmentedRpc<() => Observable>>; - /** - * Returns the blockNumber - **/ - blockNumber: AugmentedRpc<() => Observable>; - /** - * Call contract, returning the output data. - **/ - call: AugmentedRpc<(request: EthCallRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; - /** - * Returns the chain ID used for transaction signing at the current best block. None is returned if not available. - **/ - chainId: AugmentedRpc<() => Observable>; - /** - * Returns block author. - **/ - coinbase: AugmentedRpc<() => Observable>; - /** - * Estimate gas needed for execution of given contract. - **/ - estimateGas: AugmentedRpc<(request: EthCallRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; - /** - * Returns fee history for given block count & reward percentiles - **/ - feeHistory: AugmentedRpc<(blockCount: U256 | AnyNumber | Uint8Array, newestBlock: BlockNumber | AnyNumber | Uint8Array, rewardPercentiles: Option> | null | Uint8Array | Vec | (f64)[]) => Observable>; - /** - * Returns current gas price. - **/ - gasPrice: AugmentedRpc<() => Observable>; - /** - * Returns balance of the given account. - **/ - getBalance: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; - /** - * Returns block with given hash. - **/ - getBlockByHash: AugmentedRpc<(hash: H256 | string | Uint8Array, full: bool | boolean | Uint8Array) => Observable>>; - /** - * Returns block with given number. - **/ - getBlockByNumber: AugmentedRpc<(block: BlockNumber | AnyNumber | Uint8Array, full: bool | boolean | Uint8Array) => Observable>>; - /** - * Returns the number of transactions in a block with given hash. - **/ - getBlockTransactionCountByHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable>; - /** - * Returns the number of transactions in a block with given block number. - **/ - getBlockTransactionCountByNumber: AugmentedRpc<(block: BlockNumber | AnyNumber | Uint8Array) => Observable>; - /** - * Returns the code at given address at given time (block number). - **/ - getCode: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; - /** - * Returns filter changes since last poll. - **/ - getFilterChanges: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable>; - /** - * Returns all logs matching given filter (in a range 'from' - 'to'). - **/ - getFilterLogs: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable>>; - /** - * Returns logs matching given filter object. - **/ - getLogs: AugmentedRpc<(filter: EthFilter | { fromBlock?: any; toBlock?: any; blockHash?: any; address?: any; topics?: any } | string | Uint8Array) => Observable>>; - /** - * Returns proof for account and storage. - **/ - getProof: AugmentedRpc<(address: H160 | string | Uint8Array, storageKeys: Vec | (H256 | string | Uint8Array)[], number: BlockNumber | AnyNumber | Uint8Array) => Observable>; - /** - * Returns content of the storage at given address. - **/ - getStorageAt: AugmentedRpc<(address: H160 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; - /** - * Returns transaction at given block hash and index. - **/ - getTransactionByBlockHashAndIndex: AugmentedRpc<(hash: H256 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable>; - /** - * Returns transaction by given block number and index. - **/ - getTransactionByBlockNumberAndIndex: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable>; - /** - * Get transaction by its hash. - **/ - getTransactionByHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable>; - /** - * Returns the number of transactions sent from given address at given time (block number). - **/ - getTransactionCount: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable>; - /** - * Returns transaction receipt by transaction hash. - **/ - getTransactionReceipt: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable>; - /** - * Returns an uncles at given block and index. - **/ - getUncleByBlockHashAndIndex: AugmentedRpc<(hash: H256 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable>; - /** - * Returns an uncles at given block and index. - **/ - getUncleByBlockNumberAndIndex: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable>; - /** - * Returns the number of uncles in a block with given hash. - **/ - getUncleCountByBlockHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable>; - /** - * Returns the number of uncles in a block with given block number. - **/ - getUncleCountByBlockNumber: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array) => Observable>; - /** - * Returns the hash of the current block, the seedHash, and the boundary condition to be met. - **/ - getWork: AugmentedRpc<() => Observable>; - /** - * Returns the number of hashes per second that the node is mining with. - **/ - hashrate: AugmentedRpc<() => Observable>; - /** - * Returns max priority fee per gas - **/ - maxPriorityFeePerGas: AugmentedRpc<() => Observable>; - /** - * Returns true if client is actively mining new blocks. - **/ - mining: AugmentedRpc<() => Observable>; - /** - * Returns id of new block filter. - **/ - newBlockFilter: AugmentedRpc<() => Observable>; - /** - * Returns id of new filter. - **/ - newFilter: AugmentedRpc<(filter: EthFilter | { fromBlock?: any; toBlock?: any; blockHash?: any; address?: any; topics?: any } | string | Uint8Array) => Observable>; - /** - * Returns id of new block filter. - **/ - newPendingTransactionFilter: AugmentedRpc<() => Observable>; - /** - * Returns protocol version encoded as a string (quotes are necessary). - **/ - protocolVersion: AugmentedRpc<() => Observable>; - /** - * Sends signed transaction, returning its hash. - **/ - sendRawTransaction: AugmentedRpc<(bytes: Bytes | string | Uint8Array) => Observable>; - /** - * Sends transaction; will block waiting for signer to return the transaction hash - **/ - sendTransaction: AugmentedRpc<(tx: EthTransactionRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array) => Observable>; - /** - * Used for submitting mining hashrate. - **/ - submitHashrate: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array, hash: H256 | string | Uint8Array) => Observable>; - /** - * Used for submitting a proof-of-work solution. - **/ - submitWork: AugmentedRpc<(nonce: H64 | string | Uint8Array, headerHash: H256 | string | Uint8Array, mixDigest: H256 | string | Uint8Array) => Observable>; - /** - * Subscribe to Eth subscription. - **/ - subscribe: AugmentedRpc<(kind: EthSubKind | 'newHeads' | 'logs' | 'newPendingTransactions' | 'syncing' | number | Uint8Array, params?: EthSubParams | { None: any } | { Logs: any } | string | Uint8Array) => Observable>; - /** - * Returns an object with data about the sync status or false. - **/ - syncing: AugmentedRpc<() => Observable>; - /** - * Uninstalls filter. - **/ - uninstallFilter: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable>; - }; - grandpa: { - /** - * Prove finality for the given block number, returning the Justification for the last block in the set. - **/ - proveFinality: AugmentedRpc<(blockNumber: BlockNumber | AnyNumber | Uint8Array) => Observable>>; - /** - * Returns the state of the current best round state as well as the ongoing background rounds - **/ - roundState: AugmentedRpc<() => Observable>; - /** - * Subscribes to grandpa justifications - **/ - subscribeJustifications: AugmentedRpc<() => Observable>; - }; - mmr: { - /** - * Generate MMR proof for the given block numbers. - **/ - generateProof: AugmentedRpc<(blockNumbers: Vec | (u64 | AnyNumber | Uint8Array)[], bestKnownBlockNumber?: u64 | AnyNumber | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; - /** - * Get the MMR root hash for the current best block. - **/ - root: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable>; - /** - * Verify an MMR proof - **/ - verifyProof: AugmentedRpc<(proof: MmrLeafBatchProof | { blockHash?: any; leaves?: any; proof?: any } | string | Uint8Array) => Observable>; - /** - * Verify an MMR proof statelessly given an mmr_root - **/ - verifyProofStateless: AugmentedRpc<(root: MmrHash | string | Uint8Array, proof: MmrLeafBatchProof | { blockHash?: any; leaves?: any; proof?: any } | string | Uint8Array) => Observable>; - }; - net: { - /** - * Returns true if client is actively listening for network connections. Otherwise false. - **/ - listening: AugmentedRpc<() => Observable>; - /** - * Returns number of peers connected to node. - **/ - peerCount: AugmentedRpc<() => Observable>; - /** - * Returns protocol version. - **/ - version: AugmentedRpc<() => Observable>; - }; - offchain: { - /** - * Get offchain local storage under given key and prefix - **/ - localStorageGet: AugmentedRpc<(kind: StorageKind | 'PERSISTENT' | 'LOCAL' | number | Uint8Array, key: Bytes | string | Uint8Array) => Observable>>; - /** - * Set offchain local storage under given key and prefix - **/ - localStorageSet: AugmentedRpc<(kind: StorageKind | 'PERSISTENT' | 'LOCAL' | number | Uint8Array, key: Bytes | string | Uint8Array, value: Bytes | string | Uint8Array) => Observable>; - }; - payment: { - /** - * @deprecated Use `api.call.transactionPaymentApi.queryFeeDetails` instead - * Query the detailed fee of a given encoded extrinsic - **/ - queryFeeDetails: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; - /** - * @deprecated Use `api.call.transactionPaymentApi.queryInfo` instead - * Retrieves the fee information for an encoded extrinsic - **/ - queryInfo: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; - }; - rpc: { - /** - * Retrieves the list of RPC methods that are exposed by the node - **/ - methods: AugmentedRpc<() => Observable>; - }; - state: { - /** - * Perform a call to a builtin on the chain - **/ - call: AugmentedRpc<(method: Text | string, data: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; - /** - * Retrieves the keys with prefix of a specific child storage - **/ - getChildKeys: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>>; - /** - * Returns proof of storage for child key entries at a specific block state. - **/ - getChildReadProof: AugmentedRpc<(childStorageKey: PrefixedStorageKey | string | Uint8Array, keys: Vec | (StorageKey | string | Uint8Array | any)[], at?: BlockHash | string | Uint8Array) => Observable>; - /** - * Retrieves the child storage for a key - **/ - getChildStorage: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>; - /** - * Retrieves the child storage hash - **/ - getChildStorageHash: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>; - /** - * Retrieves the child storage size - **/ - getChildStorageSize: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>; - /** - * @deprecated Use `api.rpc.state.getKeysPaged` to retrieve keys - * Retrieves the keys with a certain prefix - **/ - getKeys: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>>; - /** - * Returns the keys with prefix with pagination support. - **/ - getKeysPaged: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, count: u32 | AnyNumber | Uint8Array, startKey?: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>>; - /** - * Returns the runtime metadata - **/ - getMetadata: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable>; - /** - * @deprecated Use `api.rpc.state.getKeysPaged` to retrieve keys - * Returns the keys with prefix, leave empty to get all the keys (deprecated: Use getKeysPaged) - **/ - getPairs: AugmentedRpc<(prefix: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>>; - /** - * Returns proof of storage entries at a specific block state - **/ - getReadProof: AugmentedRpc<(keys: Vec | (StorageKey | string | Uint8Array | any)[], at?: BlockHash | string | Uint8Array) => Observable>; - /** - * Get the runtime version - **/ - getRuntimeVersion: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable>; - /** - * Retrieves the storage for a key - **/ - getStorage: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, block?: Hash | Uint8Array | string) => Observable>; - /** - * Retrieves the storage hash - **/ - getStorageHash: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>; - /** - * Retrieves the storage size - **/ - getStorageSize: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable>; - /** - * Query historical storage entries (by key) starting from a start block - **/ - queryStorage: AugmentedRpc<(keys: Vec | (StorageKey | string | Uint8Array | any)[], fromBlock?: Hash | Uint8Array | string, toBlock?: Hash | Uint8Array | string) => Observable<[Hash, T][]>>; - /** - * Query storage entries (by key) starting at block hash given as the second parameter - **/ - queryStorageAt: AugmentedRpc<(keys: Vec | (StorageKey | string | Uint8Array | any)[], at?: Hash | Uint8Array | string) => Observable>; - /** - * Retrieves the runtime version via subscription - **/ - subscribeRuntimeVersion: AugmentedRpc<() => Observable>; - /** - * Subscribes to storage changes for the provided keys - **/ - subscribeStorage: AugmentedRpc<(keys?: Vec | (StorageKey | string | Uint8Array | any)[]) => Observable>; - /** - * Provides a way to trace the re-execution of a single block - **/ - traceBlock: AugmentedRpc<(block: Hash | string | Uint8Array, targets: Option | null | Uint8Array | Text | string, storageKeys: Option | null | Uint8Array | Text | string, methods: Option | null | Uint8Array | Text | string) => Observable>; - /** - * Check current migration state - **/ - trieMigrationStatus: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable>; - }; - syncstate: { - /** - * Returns the json-serialized chainspec running the node, with a sync state. - **/ - genSyncSpec: AugmentedRpc<(raw: bool | boolean | Uint8Array) => Observable>; - }; - system: { - /** - * Retrieves the next accountIndex as available on the node - **/ - accountNextIndex: AugmentedRpc<(accountId: AccountId | string | Uint8Array) => Observable>; - /** - * Adds the supplied directives to the current log filter - **/ - addLogFilter: AugmentedRpc<(directives: Text | string) => Observable>; - /** - * Adds a reserved peer - **/ - addReservedPeer: AugmentedRpc<(peer: Text | string) => Observable>; - /** - * Retrieves the chain - **/ - chain: AugmentedRpc<() => Observable>; - /** - * Retrieves the chain type - **/ - chainType: AugmentedRpc<() => Observable>; - /** - * Dry run an extrinsic at a given block - **/ - dryRun: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; - /** - * Return health status of the node - **/ - health: AugmentedRpc<() => Observable>; - /** - * The addresses include a trailing /p2p/ with the local PeerId, and are thus suitable to be passed to addReservedPeer or as a bootnode address for example - **/ - localListenAddresses: AugmentedRpc<() => Observable>>; - /** - * Returns the base58-encoded PeerId of the node - **/ - localPeerId: AugmentedRpc<() => Observable>; - /** - * Retrieves the node name - **/ - name: AugmentedRpc<() => Observable>; - /** - * Returns current state of the network - **/ - networkState: AugmentedRpc<() => Observable>; - /** - * Returns the roles the node is running as - **/ - nodeRoles: AugmentedRpc<() => Observable>>; - /** - * Returns the currently connected peers - **/ - peers: AugmentedRpc<() => Observable>>; - /** - * Get a custom set of properties as a JSON object, defined in the chain spec - **/ - properties: AugmentedRpc<() => Observable>; - /** - * Remove a reserved peer - **/ - removeReservedPeer: AugmentedRpc<(peerId: Text | string) => Observable>; - /** - * Returns the list of reserved peers - **/ - reservedPeers: AugmentedRpc<() => Observable>>; - /** - * Resets the log filter to Substrate defaults - **/ - resetLogFilter: AugmentedRpc<() => Observable>; - /** - * Returns the state of the syncing of the node - **/ - syncState: AugmentedRpc<() => Observable>; - /** - * Retrieves the version of the node - **/ - version: AugmentedRpc<() => Observable>; - }; - web3: { - /** - * Returns current client version. - **/ - clientVersion: AugmentedRpc<() => Observable>; - /** - * Returns sha3 of the given data - **/ - sha3: AugmentedRpc<(data: Bytes | string | Uint8Array) => Observable>; - }; - } // RpcInterface -} // declare module --- a/js-packages/types/src/augment-api-runtime.ts +++ /dev/null @@ -1,264 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-chain`, do not edit -/* eslint-disable */ - -// import type lookup before we augment - in some environments -// this is required to allow for ambient/previous definitions -import '@polkadot/api-base/types/calls'; - -import type { ApiTypes, AugmentedCall, DecoratedCallBase } from '@polkadot/api-base/types'; -import type { Bytes, Null, Option, Result, U256, Vec, bool, u256, u32, u64 } from '@polkadot/types-codec'; -import type { AnyNumber, ITuple } from '@polkadot/types-codec/types'; -import type { CheckInherentsResult, InherentData } from '@polkadot/types/interfaces/blockbuilder'; -import type { BlockHash } from '@polkadot/types/interfaces/chain'; -import type { AuthorityId } from '@polkadot/types/interfaces/consensus'; -import type { CollationInfo } from '@polkadot/types/interfaces/cumulus'; -import type { BlockV2, EthReceiptV3, EthTransactionStatus, TransactionV2 } from '@polkadot/types/interfaces/eth'; -import type { EvmAccount, EvmCallInfoV2, EvmCreateInfoV2 } from '@polkadot/types/interfaces/evm'; -import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics'; -import type { OpaqueMetadata } from '@polkadot/types/interfaces/metadata'; -import type { FeeDetails, RuntimeDispatchInfo } from '@polkadot/types/interfaces/payment'; -import type { AccountId, Balance, Block, H160, H256, Header, Index, KeyTypeId, Permill, SlotDuration, Weight } from '@polkadot/types/interfaces/runtime'; -import type { RuntimeVersion } from '@polkadot/types/interfaces/state'; -import type { ApplyExtrinsicResult, DispatchError } from '@polkadot/types/interfaces/system'; -import type { TransactionSource, TransactionValidity } from '@polkadot/types/interfaces/txqueue'; -import type { IExtrinsic, Observable } from '@polkadot/types/types'; - -export type __AugmentedCall = AugmentedCall; -export type __DecoratedCallBase = DecoratedCallBase; - -declare module '@polkadot/api-base/types/calls' { - interface AugmentedCalls { - /** 0xbc9d89904f5b923f/1 */ - accountNonceApi: { - /** - * The API to query account nonce (aka transaction index) - **/ - accountNonce: AugmentedCall Observable>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - /** 0xdd718d5cc53262d4/1 */ - auraApi: { - /** - * Return the current set of authorities. - **/ - authorities: AugmentedCall Observable>>; - /** - * Returns the slot duration for Aura. - **/ - slotDuration: AugmentedCall Observable>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - /** 0x40fe3ad401f8959a/6 */ - blockBuilder: { - /** - * Apply the given extrinsic. - **/ - applyExtrinsic: AugmentedCall Observable>; - /** - * Check that the inherents are valid. - **/ - checkInherents: AugmentedCall Observable>; - /** - * Finish the current block. - **/ - finalizeBlock: AugmentedCall Observable
>; - /** - * Generate inherent extrinsics. - **/ - inherentExtrinsics: AugmentedCall Observable>>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - /** 0xea93e3f16f3d6962/2 */ - collectCollationInfo: { - /** - * Collect information about a collation. - **/ - collectCollationInfo: AugmentedCall Observable>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - /** 0xe65b00e46cedd0aa/2 */ - convertTransactionRuntimeApi: { - /** - * Converts an Ethereum-style transaction to Extrinsic - **/ - convertTransaction: AugmentedCall Observable>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - /** 0xdf6acb689907609b/4 */ - core: { - /** - * Execute the given block. - **/ - executeBlock: AugmentedCall Observable>; - /** - * Initialize a block with the given header. - **/ - initializeBlock: AugmentedCall Observable>; - /** - * Returns the version of the runtime. - **/ - version: AugmentedCall Observable>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - /** 0x582211f65bb14b89/5 */ - ethereumRuntimeRPCApi: { - /** - * Returns pallet_evm::Accounts by address. - **/ - accountBasic: AugmentedCall Observable>; - /** - * For a given account address, returns pallet_evm::AccountCodes. - **/ - accountCodeAt: AugmentedCall Observable>; - /** - * Returns the converted FindAuthor::find_author authority id. - **/ - author: AugmentedCall Observable>; - /** - * Returns a frame_ethereum::call response. If `estimate` is true, - **/ - call: AugmentedCall | null | Uint8Array | U256 | AnyNumber, maxPriorityFeePerGas: Option | null | Uint8Array | U256 | AnyNumber, nonce: Option | null | Uint8Array | U256 | AnyNumber, estimate: bool | boolean | Uint8Array, accessList: Option]>>> | null | Uint8Array | Vec]>> | ([H160 | string | Uint8Array, Vec | (H256 | string | Uint8Array)[]])[]) => Observable>>; - /** - * Returns runtime defined pallet_evm::ChainId. - **/ - chainId: AugmentedCall Observable>; - /** - * Returns a frame_ethereum::call response. If `estimate` is true, - **/ - create: AugmentedCall | null | Uint8Array | U256 | AnyNumber, maxPriorityFeePerGas: Option | null | Uint8Array | U256 | AnyNumber, nonce: Option | null | Uint8Array | U256 | AnyNumber, estimate: bool | boolean | Uint8Array, accessList: Option]>>> | null | Uint8Array | Vec]>> | ([H160 | string | Uint8Array, Vec | (H256 | string | Uint8Array)[]])[]) => Observable>>; - /** - * Return all the current data for a block in a single runtime call. - **/ - currentAll: AugmentedCall Observable, Option>, Option>]>>>; - /** - * Return the current block. - **/ - currentBlock: AugmentedCall Observable>; - /** - * Return the current receipt. - **/ - currentReceipts: AugmentedCall Observable>>>; - /** - * Return the current transaction status. - **/ - currentTransactionStatuses: AugmentedCall Observable>>>; - /** - * Return the elasticity multiplier. - **/ - elasticity: AugmentedCall Observable>>; - /** - * Receives a `Vec` and filters all the ethereum transactions. - **/ - extrinsicFilter: AugmentedCall | (Extrinsic | IExtrinsic | string | Uint8Array)[]) => Observable>>; - /** - * Returns FixedGasPrice::min_gas_price - **/ - gasPrice: AugmentedCall Observable>; - /** - * For a given account address and index, returns pallet_evm::AccountStorages. - **/ - storageAt: AugmentedCall Observable>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - /** 0x37e397fc7c91f5e4/2 */ - metadata: { - /** - * Returns the metadata of a runtime - **/ - metadata: AugmentedCall Observable>; - /** - * Returns the metadata at a given version. - **/ - metadataAtVersion: AugmentedCall Observable>>; - /** - * Returns the supported metadata versions. - **/ - metadataVersions: AugmentedCall Observable>>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - /** 0xf78b278be53f454c/2 */ - offchainWorkerApi: { - /** - * Starts the off-chain task for given block header. - **/ - offchainWorker: AugmentedCall Observable>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - /** 0xab3c0572291feb8b/1 */ - sessionKeys: { - /** - * Decode the given public session keys. - **/ - decodeSessionKeys: AugmentedCall Observable>>>>; - /** - * Generate a set of session keys with optionally using the given seed. - **/ - generateSessionKeys: AugmentedCall | null | Uint8Array | Bytes | string) => Observable>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - /** 0xd2bc9897eed08f15/3 */ - taggedTransactionQueue: { - /** - * Validate the transaction. - **/ - validateTransaction: AugmentedCall Observable>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - /** 0x37c8bb1350a9a2a8/4 */ - transactionPaymentApi: { - /** - * The transaction fee details - **/ - queryFeeDetails: AugmentedCall Observable>; - /** - * The transaction info - **/ - queryInfo: AugmentedCall Observable>; - /** - * Query the output of the current LengthToFee given some input - **/ - queryLengthToFee: AugmentedCall Observable>; - /** - * Query the output of the current WeightToFee given some input - **/ - queryWeightToFee: AugmentedCall Observable>; - /** - * Generic call - **/ - [key: string]: DecoratedCallBase; - }; - } // AugmentedCalls -} // declare module --- a/js-packages/types/src/augment-api-tx.ts +++ /dev/null @@ -1,1250 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-chain`, do not edit -/* eslint-disable */ - -// import type lookup before we augment - in some environments -// this is required to allow for ambient/previous definitions -import '@polkadot/api-base/types/submittable'; - -import type { ApiTypes, AugmentedSubmittable, SubmittableExtrinsic, SubmittableExtrinsicFunction } from '@polkadot/api-base/types'; -import type { Data } from '@polkadot/types'; -import type { Bytes, Compact, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; -import type { AnyNumber, IMethod, ITuple } from '@polkadot/types-codec/types'; -import type { AccountId32, Call, H160, H256, MultiAddress } from '@polkadot/types/interfaces/runtime'; -import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumLog, EthereumTransactionTransactionV2, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime, OpalRuntimeOriginCaller, OpalRuntimeRuntimeCommonSessionKeys, OrmlVestingVestingSchedule, PalletConfigurationAppPromotionConfiguration, PalletDemocracyConviction, PalletDemocracyMetadataOwner, PalletDemocracyVoteAccountVote, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletIdentityBitFlags, PalletIdentityIdentityInfo, PalletIdentityJudgement, PalletIdentityRegistration, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletStateTrieMigrationProgress, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, StagingXcmV3WeightLimit, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation, StagingXcmVersionedXcm, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsProperty, UpDataStructsPropertyKeyPermission } from '@polkadot/types/lookup'; - -export type __AugmentedSubmittable = AugmentedSubmittable<() => unknown>; -export type __SubmittableExtrinsic = SubmittableExtrinsic; -export type __SubmittableExtrinsicFunction = SubmittableExtrinsicFunction; - -declare module '@polkadot/api-base/types/submittable' { - interface AugmentedSubmittables { - appPromotion: { - /** - * See [`Pallet::force_unstake`]. - **/ - forceUnstake: AugmentedSubmittable<(pendingBlocks: Vec | (u32 | AnyNumber | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * See [`Pallet::payout_stakers`]. - **/ - payoutStakers: AugmentedSubmittable<(stakersNumber: Option | null | Uint8Array | u8 | AnyNumber) => SubmittableExtrinsic, [Option]>; - /** - * See [`Pallet::set_admin_address`]. - **/ - setAdminAddress: AugmentedSubmittable<(admin: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr]>; - /** - * See [`Pallet::sponsor_collection`]. - **/ - sponsorCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::sponsor_contract`]. - **/ - sponsorContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic, [H160]>; - /** - * See [`Pallet::stake`]. - **/ - stake: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u128]>; - /** - * See [`Pallet::stop_sponsoring_collection`]. - **/ - stopSponsoringCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::stop_sponsoring_contract`]. - **/ - stopSponsoringContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic, [H160]>; - /** - * See [`Pallet::unstake_all`]. - **/ - unstakeAll: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::unstake_partial`]. - **/ - unstakePartial: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u128]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - balances: { - /** - * See [`Pallet::force_set_balance`]. - **/ - forceSetBalance: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, newFree: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Compact]>; - /** - * See [`Pallet::force_transfer`]. - **/ - forceTransfer: AugmentedSubmittable<(source: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, MultiAddress, Compact]>; - /** - * See [`Pallet::force_unreserve`]. - **/ - forceUnreserve: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, u128]>; - /** - * See [`Pallet::set_balance_deprecated`]. - **/ - setBalanceDeprecated: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, newFree: Compact | AnyNumber | Uint8Array, oldReserved: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Compact, Compact]>; - /** - * See [`Pallet::transfer`]. - **/ - transfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Compact]>; - /** - * See [`Pallet::transfer_all`]. - **/ - transferAll: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, keepAlive: bool | boolean | Uint8Array) => SubmittableExtrinsic, [MultiAddress, bool]>; - /** - * See [`Pallet::transfer_allow_death`]. - **/ - transferAllowDeath: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Compact]>; - /** - * See [`Pallet::transfer_keep_alive`]. - **/ - transferKeepAlive: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Compact]>; - /** - * See [`Pallet::upgrade_accounts`]. - **/ - upgradeAccounts: AugmentedSubmittable<(who: Vec | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - collatorSelection: { - /** - * See [`Pallet::add_invulnerable`]. - **/ - addInvulnerable: AugmentedSubmittable<(updated: AccountId32 | string | Uint8Array) => SubmittableExtrinsic, [AccountId32]>; - /** - * See [`Pallet::force_release_license`]. - **/ - forceReleaseLicense: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic, [AccountId32]>; - /** - * See [`Pallet::get_license`]. - **/ - getLicense: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::offboard`]. - **/ - offboard: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::onboard`]. - **/ - onboard: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::release_license`]. - **/ - releaseLicense: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::remove_invulnerable`]. - **/ - removeInvulnerable: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic, [AccountId32]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - configuration: { - /** - * See [`Pallet::set_app_promotion_configuration_override`]. - **/ - setAppPromotionConfigurationOverride: AugmentedSubmittable<(configuration: PalletConfigurationAppPromotionConfiguration | { recalculationInterval?: any; pendingInterval?: any; intervalIncome?: any; maxStakersPerCalculation?: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletConfigurationAppPromotionConfiguration]>; - /** - * See [`Pallet::set_collator_selection_desired_collators`]. - **/ - setCollatorSelectionDesiredCollators: AugmentedSubmittable<(max: Option | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic, [Option]>; - /** - * See [`Pallet::set_collator_selection_kick_threshold`]. - **/ - setCollatorSelectionKickThreshold: AugmentedSubmittable<(threshold: Option | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic, [Option]>; - /** - * See [`Pallet::set_collator_selection_license_bond`]. - **/ - setCollatorSelectionLicenseBond: AugmentedSubmittable<(amount: Option | null | Uint8Array | u128 | AnyNumber) => SubmittableExtrinsic, [Option]>; - /** - * See [`Pallet::set_min_gas_price_override`]. - **/ - setMinGasPriceOverride: AugmentedSubmittable<(coeff: Option | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic, [Option]>; - /** - * See [`Pallet::set_weight_to_fee_coefficient_override`]. - **/ - setWeightToFeeCoefficientOverride: AugmentedSubmittable<(coeff: Option | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic, [Option]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - council: { - /** - * See [`Pallet::close`]. - **/ - close: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, index: Compact | AnyNumber | Uint8Array, proposalWeightBound: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [H256, Compact, SpWeightsWeightV2Weight, Compact]>; - /** - * See [`Pallet::disapprove_proposal`]. - **/ - disapproveProposal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; - /** - * See [`Pallet::execute`]. - **/ - execute: AugmentedSubmittable<(proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Call, Compact]>; - /** - * See [`Pallet::propose`]. - **/ - propose: AugmentedSubmittable<(threshold: Compact | AnyNumber | Uint8Array, proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact, Call, Compact]>; - /** - * See [`Pallet::set_members`]. - **/ - setMembers: AugmentedSubmittable<(newMembers: Vec | (AccountId32 | string | Uint8Array)[], prime: Option | null | Uint8Array | AccountId32 | string, oldCount: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Vec, Option, u32]>; - /** - * See [`Pallet::vote`]. - **/ - vote: AugmentedSubmittable<(proposal: H256 | string | Uint8Array, index: Compact | AnyNumber | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic, [H256, Compact, bool]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - councilMembership: { - /** - * See [`Pallet::add_member`]. - **/ - addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::change_key`]. - **/ - changeKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::clear_prime`]. - **/ - clearPrime: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::remove_member`]. - **/ - removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::reset_members`]. - **/ - resetMembers: AugmentedSubmittable<(members: Vec | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * See [`Pallet::set_prime`]. - **/ - setPrime: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::swap_member`]. - **/ - swapMember: AugmentedSubmittable<(remove: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, add: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, MultiAddress]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - cumulusXcm: { - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - democracy: { - /** - * See [`Pallet::blacklist`]. - **/ - blacklist: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, maybeRefIndex: Option | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic, [H256, Option]>; - /** - * See [`Pallet::cancel_proposal`]. - **/ - cancelProposal: AugmentedSubmittable<(propIndex: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; - /** - * See [`Pallet::cancel_referendum`]. - **/ - cancelReferendum: AugmentedSubmittable<(refIndex: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; - /** - * See [`Pallet::clear_public_proposals`]. - **/ - clearPublicProposals: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::delegate`]. - **/ - delegate: AugmentedSubmittable<(to: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, conviction: PalletDemocracyConviction | 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x' | number | Uint8Array, balance: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletDemocracyConviction, u128]>; - /** - * See [`Pallet::emergency_cancel`]. - **/ - emergencyCancel: AugmentedSubmittable<(refIndex: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::external_propose`]. - **/ - externalPropose: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic, [FrameSupportPreimagesBounded]>; - /** - * See [`Pallet::external_propose_default`]. - **/ - externalProposeDefault: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic, [FrameSupportPreimagesBounded]>; - /** - * See [`Pallet::external_propose_majority`]. - **/ - externalProposeMajority: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic, [FrameSupportPreimagesBounded]>; - /** - * See [`Pallet::fast_track`]. - **/ - fastTrack: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, votingPeriod: u32 | AnyNumber | Uint8Array, delay: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [H256, u32, u32]>; - /** - * See [`Pallet::propose`]. - **/ - propose: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array, value: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [FrameSupportPreimagesBounded, Compact]>; - /** - * See [`Pallet::remove_other_vote`]. - **/ - removeOtherVote: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, u32]>; - /** - * See [`Pallet::remove_vote`]. - **/ - removeVote: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::second`]. - **/ - second: AugmentedSubmittable<(proposal: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; - /** - * See [`Pallet::set_metadata`]. - **/ - setMetadata: AugmentedSubmittable<(owner: PalletDemocracyMetadataOwner | { External: any } | { Proposal: any } | { Referendum: any } | string | Uint8Array, maybeHash: Option | null | Uint8Array | H256 | string) => SubmittableExtrinsic, [PalletDemocracyMetadataOwner, Option]>; - /** - * See [`Pallet::undelegate`]. - **/ - undelegate: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::unlock`]. - **/ - unlock: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::veto_external`]. - **/ - vetoExternal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; - /** - * See [`Pallet::vote`]. - **/ - vote: AugmentedSubmittable<(refIndex: Compact | AnyNumber | Uint8Array, vote: PalletDemocracyVoteAccountVote | { Standard: any } | { Split: any } | string | Uint8Array) => SubmittableExtrinsic, [Compact, PalletDemocracyVoteAccountVote]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - dmpQueue: { - /** - * See [`Pallet::service_overweight`]. - **/ - serviceOverweight: AugmentedSubmittable<(index: u64 | AnyNumber | Uint8Array, weightLimit: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [u64, SpWeightsWeightV2Weight]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - ethereum: { - /** - * See [`Pallet::transact`]. - **/ - transact: AugmentedSubmittable<(transaction: EthereumTransactionTransactionV2 | { Legacy: any } | { EIP2930: any } | { EIP1559: any } | string | Uint8Array) => SubmittableExtrinsic, [EthereumTransactionTransactionV2]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - evm: { - /** - * See [`Pallet::call`]. - **/ - call: AugmentedSubmittable<(source: H160 | string | Uint8Array, target: H160 | string | Uint8Array, input: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option | null | Uint8Array | U256 | AnyNumber, nonce: Option | null | Uint8Array | U256 | AnyNumber, accessList: Vec]>> | ([H160 | string | Uint8Array, Vec | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic, [H160, H160, Bytes, U256, u64, U256, Option, Option, Vec]>>]>; - /** - * See [`Pallet::create`]. - **/ - create: AugmentedSubmittable<(source: H160 | string | Uint8Array, init: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option | null | Uint8Array | U256 | AnyNumber, nonce: Option | null | Uint8Array | U256 | AnyNumber, accessList: Vec]>> | ([H160 | string | Uint8Array, Vec | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic, [H160, Bytes, U256, u64, U256, Option, Option, Vec]>>]>; - /** - * See [`Pallet::create2`]. - **/ - create2: AugmentedSubmittable<(source: H160 | string | Uint8Array, init: Bytes | string | Uint8Array, salt: H256 | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option | null | Uint8Array | U256 | AnyNumber, nonce: Option | null | Uint8Array | U256 | AnyNumber, accessList: Vec]>> | ([H160 | string | Uint8Array, Vec | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic, [H160, Bytes, H256, U256, u64, U256, Option, Option, Vec]>>]>; - /** - * See [`Pallet::withdraw`]. - **/ - withdraw: AugmentedSubmittable<(address: H160 | string | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [H160, u128]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - evmContractHelpers: { - /** - * See [`Pallet::migrate_from_self_sponsoring`]. - **/ - migrateFromSelfSponsoring: AugmentedSubmittable<(addresses: Vec | (H160 | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - evmMigration: { - /** - * See [`Pallet::begin`]. - **/ - begin: AugmentedSubmittable<(address: H160 | string | Uint8Array) => SubmittableExtrinsic, [H160]>; - /** - * See [`Pallet::finish`]. - **/ - finish: AugmentedSubmittable<(address: H160 | string | Uint8Array, code: Bytes | string | Uint8Array) => SubmittableExtrinsic, [H160, Bytes]>; - /** - * See [`Pallet::insert_eth_logs`]. - **/ - insertEthLogs: AugmentedSubmittable<(logs: Vec | (EthereumLog | { address?: any; topics?: any; data?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * See [`Pallet::insert_events`]. - **/ - insertEvents: AugmentedSubmittable<(events: Vec | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * See [`Pallet::remove_rmrk_data`]. - **/ - removeRmrkData: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::set_data`]. - **/ - setData: AugmentedSubmittable<(address: H160 | string | Uint8Array, data: Vec> | ([H256 | string | Uint8Array, H256 | string | Uint8Array])[]) => SubmittableExtrinsic, [H160, Vec>]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - fellowshipCollective: { - /** - * See [`Pallet::add_member`]. - **/ - addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::cleanup_poll`]. - **/ - cleanupPoll: AugmentedSubmittable<(pollIndex: u32 | AnyNumber | Uint8Array, max: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32]>; - /** - * See [`Pallet::demote_member`]. - **/ - demoteMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::promote_member`]. - **/ - promoteMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::remove_member`]. - **/ - removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, minRank: u16 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, u16]>; - /** - * See [`Pallet::vote`]. - **/ - vote: AugmentedSubmittable<(poll: u32 | AnyNumber | Uint8Array, aye: bool | boolean | Uint8Array) => SubmittableExtrinsic, [u32, bool]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - fellowshipReferenda: { - /** - * See [`Pallet::cancel`]. - **/ - cancel: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::kill`]. - **/ - kill: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::nudge_referendum`]. - **/ - nudgeReferendum: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::one_fewer_deciding`]. - **/ - oneFewerDeciding: AugmentedSubmittable<(track: u16 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u16]>; - /** - * See [`Pallet::place_decision_deposit`]. - **/ - placeDecisionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::refund_decision_deposit`]. - **/ - refundDecisionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::refund_submission_deposit`]. - **/ - refundSubmissionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::set_metadata`]. - **/ - setMetadata: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array, maybeHash: Option | null | Uint8Array | H256 | string) => SubmittableExtrinsic, [u32, Option]>; - /** - * See [`Pallet::submit`]. - **/ - submit: AugmentedSubmittable<(proposalOrigin: OpalRuntimeOriginCaller | { system: any } | { Void: any } | { Council: any } | { TechnicalCommittee: any } | { PolkadotXcm: any } | { CumulusXcm: any } | { Origins: any } | { Ethereum: any } | string | Uint8Array, proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array, enactmentMoment: FrameSupportScheduleDispatchTime | { At: any } | { After: any } | string | Uint8Array) => SubmittableExtrinsic, [OpalRuntimeOriginCaller, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - foreignAssets: { - /** - * See [`Pallet::register_foreign_asset`]. - **/ - registerForeignAsset: AugmentedSubmittable<(owner: AccountId32 | string | Uint8Array, location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, metadata: PalletForeignAssetsModuleAssetMetadata | { name?: any; symbol?: any; decimals?: any; minimalBalance?: any } | string | Uint8Array) => SubmittableExtrinsic, [AccountId32, StagingXcmVersionedMultiLocation, PalletForeignAssetsModuleAssetMetadata]>; - /** - * See [`Pallet::update_foreign_asset`]. - **/ - updateForeignAsset: AugmentedSubmittable<(foreignAssetId: u32 | AnyNumber | Uint8Array, location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, metadata: PalletForeignAssetsModuleAssetMetadata | { name?: any; symbol?: any; decimals?: any; minimalBalance?: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, StagingXcmVersionedMultiLocation, PalletForeignAssetsModuleAssetMetadata]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - identity: { - /** - * See [`Pallet::add_registrar`]. - **/ - addRegistrar: AugmentedSubmittable<(account: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::add_sub`]. - **/ - addSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, data: Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Data]>; - /** - * See [`Pallet::cancel_request`]. - **/ - cancelRequest: AugmentedSubmittable<(regIndex: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::clear_identity`]. - **/ - clearIdentity: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::force_insert_identities`]. - **/ - forceInsertIdentities: AugmentedSubmittable<(identities: Vec> | ([AccountId32 | string | Uint8Array, PalletIdentityRegistration | { judgements?: any; deposit?: any; info?: any } | string | Uint8Array])[]) => SubmittableExtrinsic, [Vec>]>; - /** - * See [`Pallet::force_remove_identities`]. - **/ - forceRemoveIdentities: AugmentedSubmittable<(identities: Vec | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * See [`Pallet::force_set_subs`]. - **/ - forceSetSubs: AugmentedSubmittable<(subs: Vec>]>]>> | ([AccountId32 | string | Uint8Array, ITuple<[u128, Vec>]> | [u128 | AnyNumber | Uint8Array, Vec> | ([AccountId32 | string | Uint8Array, Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array])[]]])[]) => SubmittableExtrinsic, [Vec>]>]>>]>; - /** - * See [`Pallet::kill_identity`]. - **/ - killIdentity: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::provide_judgement`]. - **/ - provideJudgement: AugmentedSubmittable<(regIndex: Compact | AnyNumber | Uint8Array, target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, judgement: PalletIdentityJudgement | { Unknown: any } | { FeePaid: any } | { Reasonable: any } | { KnownGood: any } | { OutOfDate: any } | { LowQuality: any } | { Erroneous: any } | string | Uint8Array, identity: H256 | string | Uint8Array) => SubmittableExtrinsic, [Compact, MultiAddress, PalletIdentityJudgement, H256]>; - /** - * See [`Pallet::quit_sub`]. - **/ - quitSub: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::remove_sub`]. - **/ - removeSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::rename_sub`]. - **/ - renameSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, data: Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Data]>; - /** - * See [`Pallet::request_judgement`]. - **/ - requestJudgement: AugmentedSubmittable<(regIndex: Compact | AnyNumber | Uint8Array, maxFee: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact, Compact]>; - /** - * See [`Pallet::set_account_id`]. - **/ - setAccountId: AugmentedSubmittable<(index: Compact | AnyNumber | Uint8Array, updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [Compact, MultiAddress]>; - /** - * See [`Pallet::set_fee`]. - **/ - setFee: AugmentedSubmittable<(index: Compact | AnyNumber | Uint8Array, fee: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact, Compact]>; - /** - * See [`Pallet::set_fields`]. - **/ - setFields: AugmentedSubmittable<(index: Compact | AnyNumber | Uint8Array, fields: PalletIdentityBitFlags) => SubmittableExtrinsic, [Compact, PalletIdentityBitFlags]>; - /** - * See [`Pallet::set_identity`]. - **/ - setIdentity: AugmentedSubmittable<(info: PalletIdentityIdentityInfo | { additional?: any; display?: any; legal?: any; web?: any; riot?: any; email?: any; pgpFingerprint?: any; image?: any; twitter?: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletIdentityIdentityInfo]>; - /** - * See [`Pallet::set_subs`]. - **/ - setSubs: AugmentedSubmittable<(subs: Vec> | ([AccountId32 | string | Uint8Array, Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array])[]) => SubmittableExtrinsic, [Vec>]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - inflation: { - /** - * See [`Pallet::start_inflation`]. - **/ - startInflation: AugmentedSubmittable<(inflationStartRelayBlock: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - maintenance: { - /** - * See [`Pallet::disable`]. - **/ - disable: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::enable`]. - **/ - enable: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - parachainInfo: { - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - parachainSystem: { - /** - * See [`Pallet::authorize_upgrade`]. - **/ - authorizeUpgrade: AugmentedSubmittable<(codeHash: H256 | string | Uint8Array, checkVersion: bool | boolean | Uint8Array) => SubmittableExtrinsic, [H256, bool]>; - /** - * See [`Pallet::enact_authorized_upgrade`]. - **/ - enactAuthorizedUpgrade: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; - /** - * See [`Pallet::set_validation_data`]. - **/ - setValidationData: AugmentedSubmittable<(data: CumulusPrimitivesParachainInherentParachainInherentData | { validationData?: any; relayChainState?: any; downwardMessages?: any; horizontalMessages?: any } | string | Uint8Array) => SubmittableExtrinsic, [CumulusPrimitivesParachainInherentParachainInherentData]>; - /** - * See [`Pallet::sudo_send_upward_message`]. - **/ - sudoSendUpwardMessage: AugmentedSubmittable<(message: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - polkadotXcm: { - /** - * See [`Pallet::execute`]. - **/ - execute: AugmentedSubmittable<(message: StagingXcmVersionedXcm | { V2: any } | { V3: any } | string | Uint8Array, maxWeight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedXcm, SpWeightsWeightV2Weight]>; - /** - * See [`Pallet::force_default_xcm_version`]. - **/ - forceDefaultXcmVersion: AugmentedSubmittable<(maybeXcmVersion: Option | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic, [Option]>; - /** - * See [`Pallet::force_subscribe_version_notify`]. - **/ - forceSubscribeVersionNotify: AugmentedSubmittable<(location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation]>; - /** - * See [`Pallet::force_suspension`]. - **/ - forceSuspension: AugmentedSubmittable<(suspended: bool | boolean | Uint8Array) => SubmittableExtrinsic, [bool]>; - /** - * See [`Pallet::force_unsubscribe_version_notify`]. - **/ - forceUnsubscribeVersionNotify: AugmentedSubmittable<(location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation]>; - /** - * See [`Pallet::force_xcm_version`]. - **/ - forceXcmVersion: AugmentedSubmittable<(location: StagingXcmV3MultiLocation | { parents?: any; interior?: any } | string | Uint8Array, version: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [StagingXcmV3MultiLocation, u32]>; - /** - * See [`Pallet::limited_reserve_transfer_assets`]. - **/ - limitedReserveTransferAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array, weightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32, StagingXcmV3WeightLimit]>; - /** - * See [`Pallet::limited_teleport_assets`]. - **/ - limitedTeleportAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array, weightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32, StagingXcmV3WeightLimit]>; - /** - * See [`Pallet::reserve_transfer_assets`]. - **/ - reserveTransferAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32]>; - /** - * See [`Pallet::send`]. - **/ - send: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, message: StagingXcmVersionedXcm | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation, StagingXcmVersionedXcm]>; - /** - * See [`Pallet::teleport_assets`]. - **/ - teleportAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - preimage: { - /** - * See [`Pallet::note_preimage`]. - **/ - notePreimage: AugmentedSubmittable<(bytes: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; - /** - * See [`Pallet::request_preimage`]. - **/ - requestPreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; - /** - * See [`Pallet::unnote_preimage`]. - **/ - unnotePreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; - /** - * See [`Pallet::unrequest_preimage`]. - **/ - unrequestPreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - scheduler: { - /** - * See [`Pallet::cancel`]. - **/ - cancel: AugmentedSubmittable<(when: u32 | AnyNumber | Uint8Array, index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32]>; - /** - * See [`Pallet::cancel_named`]. - **/ - cancelNamed: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array) => SubmittableExtrinsic, [U8aFixed]>; - /** - * See [`Pallet::schedule`]. - **/ - schedule: AugmentedSubmittable<(when: u32 | AnyNumber | Uint8Array, maybePeriodic: Option> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [u32, Option>, u8, Call]>; - /** - * See [`Pallet::schedule_after`]. - **/ - scheduleAfter: AugmentedSubmittable<(after: u32 | AnyNumber | Uint8Array, maybePeriodic: Option> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [u32, Option>, u8, Call]>; - /** - * See [`Pallet::schedule_named`]. - **/ - scheduleNamed: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array, when: u32 | AnyNumber | Uint8Array, maybePeriodic: Option> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [U8aFixed, u32, Option>, u8, Call]>; - /** - * See [`Pallet::schedule_named_after`]. - **/ - scheduleNamedAfter: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array, after: u32 | AnyNumber | Uint8Array, maybePeriodic: Option> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [U8aFixed, u32, Option>, u8, Call]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - session: { - /** - * See [`Pallet::purge_keys`]. - **/ - purgeKeys: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::set_keys`]. - **/ - setKeys: AugmentedSubmittable<(keys: OpalRuntimeRuntimeCommonSessionKeys | { aura?: any } | string | Uint8Array, proof: Bytes | string | Uint8Array) => SubmittableExtrinsic, [OpalRuntimeRuntimeCommonSessionKeys, Bytes]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - stateTrieMigration: { - /** - * See [`Pallet::continue_migrate`]. - **/ - continueMigrate: AugmentedSubmittable<(limits: PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string | Uint8Array, realSizeUpper: u32 | AnyNumber | Uint8Array, witnessTask: PalletStateTrieMigrationMigrationTask | { progressTop?: any; progressChild?: any; size_?: any; topItems?: any; childItems?: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletStateTrieMigrationMigrationLimits, u32, PalletStateTrieMigrationMigrationTask]>; - /** - * See [`Pallet::control_auto_migration`]. - **/ - controlAutoMigration: AugmentedSubmittable<(maybeConfig: Option | null | Uint8Array | PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string) => SubmittableExtrinsic, [Option]>; - /** - * See [`Pallet::force_set_progress`]. - **/ - forceSetProgress: AugmentedSubmittable<(progressTop: PalletStateTrieMigrationProgress | { ToStart: any } | { LastKey: any } | { Complete: any } | string | Uint8Array, progressChild: PalletStateTrieMigrationProgress | { ToStart: any } | { LastKey: any } | { Complete: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletStateTrieMigrationProgress, PalletStateTrieMigrationProgress]>; - /** - * See [`Pallet::migrate_custom_child`]. - **/ - migrateCustomChild: AugmentedSubmittable<(root: Bytes | string | Uint8Array, childKeys: Vec | (Bytes | string | Uint8Array)[], totalSize: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Bytes, Vec, u32]>; - /** - * See [`Pallet::migrate_custom_top`]. - **/ - migrateCustomTop: AugmentedSubmittable<(keys: Vec | (Bytes | string | Uint8Array)[], witnessSize: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Vec, u32]>; - /** - * See [`Pallet::set_signed_max_limits`]. - **/ - setSignedMaxLimits: AugmentedSubmittable<(limits: PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletStateTrieMigrationMigrationLimits]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - structure: { - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - sudo: { - /** - * See [`Pallet::set_key`]. - **/ - setKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::sudo`]. - **/ - sudo: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [Call]>; - /** - * See [`Pallet::sudo_as`]. - **/ - sudoAs: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, Call]>; - /** - * See [`Pallet::sudo_unchecked_weight`]. - **/ - sudoUncheckedWeight: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array, weight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [Call, SpWeightsWeightV2Weight]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - system: { - /** - * See [`Pallet::kill_prefix`]. - **/ - killPrefix: AugmentedSubmittable<(prefix: Bytes | string | Uint8Array, subkeys: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Bytes, u32]>; - /** - * See [`Pallet::kill_storage`]. - **/ - killStorage: AugmentedSubmittable<(keys: Vec | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * See [`Pallet::remark`]. - **/ - remark: AugmentedSubmittable<(remark: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; - /** - * See [`Pallet::remark_with_event`]. - **/ - remarkWithEvent: AugmentedSubmittable<(remark: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; - /** - * See [`Pallet::set_code`]. - **/ - setCode: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; - /** - * See [`Pallet::set_code_without_checks`]. - **/ - setCodeWithoutChecks: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; - /** - * See [`Pallet::set_heap_pages`]. - **/ - setHeapPages: AugmentedSubmittable<(pages: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u64]>; - /** - * See [`Pallet::set_storage`]. - **/ - setStorage: AugmentedSubmittable<(items: Vec> | ([Bytes | string | Uint8Array, Bytes | string | Uint8Array])[]) => SubmittableExtrinsic, [Vec>]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - technicalCommittee: { - /** - * See [`Pallet::close`]. - **/ - close: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, index: Compact | AnyNumber | Uint8Array, proposalWeightBound: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [H256, Compact, SpWeightsWeightV2Weight, Compact]>; - /** - * See [`Pallet::disapprove_proposal`]. - **/ - disapproveProposal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic, [H256]>; - /** - * See [`Pallet::execute`]. - **/ - execute: AugmentedSubmittable<(proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Call, Compact]>; - /** - * See [`Pallet::propose`]. - **/ - propose: AugmentedSubmittable<(threshold: Compact | AnyNumber | Uint8Array, proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact, Call, Compact]>; - /** - * See [`Pallet::set_members`]. - **/ - setMembers: AugmentedSubmittable<(newMembers: Vec | (AccountId32 | string | Uint8Array)[], prime: Option | null | Uint8Array | AccountId32 | string, oldCount: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Vec, Option, u32]>; - /** - * See [`Pallet::vote`]. - **/ - vote: AugmentedSubmittable<(proposal: H256 | string | Uint8Array, index: Compact | AnyNumber | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic, [H256, Compact, bool]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - technicalCommitteeMembership: { - /** - * See [`Pallet::add_member`]. - **/ - addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::change_key`]. - **/ - changeKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::clear_prime`]. - **/ - clearPrime: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::remove_member`]. - **/ - removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::reset_members`]. - **/ - resetMembers: AugmentedSubmittable<(members: Vec | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * See [`Pallet::set_prime`]. - **/ - setPrime: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::swap_member`]. - **/ - swapMember: AugmentedSubmittable<(remove: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, add: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, MultiAddress]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - testUtils: { - /** - * See `Pallet::batch_all`. - **/ - batchAll: AugmentedSubmittable<(calls: Vec | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * See `Pallet::enable`. - **/ - enable: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See `Pallet::inc_test_value`. - **/ - incTestValue: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See `Pallet::just_take_fee`. - **/ - justTakeFee: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See `Pallet::set_test_value`. - **/ - setTestValue: AugmentedSubmittable<(value: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See `Pallet::set_test_value_and_rollback`. - **/ - setTestValueAndRollback: AugmentedSubmittable<(value: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - timestamp: { - /** - * See [`Pallet::set`]. - **/ - set: AugmentedSubmittable<(now: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - tokens: { - /** - * See [`Pallet::force_transfer`]. - **/ - forceTransfer: AugmentedSubmittable<(source: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, MultiAddress, PalletForeignAssetsAssetId, Compact]>; - /** - * See [`Pallet::set_balance`]. - **/ - setBalance: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, newFree: Compact | AnyNumber | Uint8Array, newReserved: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetId, Compact, Compact]>; - /** - * See [`Pallet::transfer`]. - **/ - transfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetId, Compact]>; - /** - * See [`Pallet::transfer_all`]. - **/ - transferAll: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, keepAlive: bool | boolean | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetId, bool]>; - /** - * See [`Pallet::transfer_keep_alive`]. - **/ - transferKeepAlive: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [MultiAddress, PalletForeignAssetsAssetId, Compact]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - treasury: { - /** - * See [`Pallet::approve_proposal`]. - **/ - approveProposal: AugmentedSubmittable<(proposalId: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; - /** - * See [`Pallet::propose_spend`]. - **/ - proposeSpend: AugmentedSubmittable<(value: Compact | AnyNumber | Uint8Array, beneficiary: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [Compact, MultiAddress]>; - /** - * See [`Pallet::reject_proposal`]. - **/ - rejectProposal: AugmentedSubmittable<(proposalId: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; - /** - * See [`Pallet::remove_approval`]. - **/ - removeApproval: AugmentedSubmittable<(proposalId: Compact | AnyNumber | Uint8Array) => SubmittableExtrinsic, [Compact]>; - /** - * See [`Pallet::spend`]. - **/ - spend: AugmentedSubmittable<(amount: Compact | AnyNumber | Uint8Array, beneficiary: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [Compact, MultiAddress]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - unique: { - /** - * See [`Pallet::add_collection_admin`]. - **/ - addCollectionAdmin: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newAdminId: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - /** - * See [`Pallet::add_to_allow_list`]. - **/ - addToAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - /** - * See [`Pallet::approve`]. - **/ - approve: AugmentedSubmittable<(spender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>; - /** - * See [`Pallet::approve_from`]. - **/ - approveFrom: AugmentedSubmittable<(from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, to: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>; - /** - * See [`Pallet::burn_from`]. - **/ - burnFrom: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32, u128]>; - /** - * See [`Pallet::burn_item`]. - **/ - burnItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32, u128]>; - /** - * See [`Pallet::change_collection_owner`]. - **/ - changeCollectionOwner: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newOwner: AccountId32 | string | Uint8Array) => SubmittableExtrinsic, [u32, AccountId32]>; - /** - * See [`Pallet::confirm_sponsorship`]. - **/ - confirmSponsorship: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::create_collection`]. - **/ - createCollection: AugmentedSubmittable<(collectionName: Vec | (u16 | AnyNumber | Uint8Array)[], collectionDescription: Vec | (u16 | AnyNumber | Uint8Array)[], tokenPrefix: Bytes | string | Uint8Array, mode: UpDataStructsCollectionMode | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic, [Vec, Vec, Bytes, UpDataStructsCollectionMode]>; - /** - * See [`Pallet::create_collection_ex`]. - **/ - createCollectionEx: AugmentedSubmittable<(data: UpDataStructsCreateCollectionData | { mode?: any; access?: any; name?: any; description?: any; tokenPrefix?: any; limits?: any; permissions?: any; tokenPropertyPermissions?: any; properties?: any; adminList?: any; pendingSponsor?: any; flags?: any } | string | Uint8Array) => SubmittableExtrinsic, [UpDataStructsCreateCollectionData]>; - /** - * See [`Pallet::create_item`]. - **/ - createItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, data: UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCreateItemData]>; - /** - * See [`Pallet::create_multiple_items`]. - **/ - createMultipleItems: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemsData: Vec | (UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr, Vec]>; - /** - * See [`Pallet::create_multiple_items_ex`]. - **/ - createMultipleItemsEx: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, data: UpDataStructsCreateItemExData | { NFT: any } | { Fungible: any } | { RefungibleMultipleItems: any } | { RefungibleMultipleOwners: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, UpDataStructsCreateItemExData]>; - /** - * See [`Pallet::delete_collection_properties`]. - **/ - deleteCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, Vec]>; - /** - * See [`Pallet::delete_token_properties`]. - **/ - deleteTokenProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, u32, Vec]>; - /** - * See [`Pallet::destroy_collection`]. - **/ - destroyCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::force_repair_collection`]. - **/ - forceRepairCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::force_repair_item`]. - **/ - forceRepairItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32]>; - /** - * See [`Pallet::remove_collection_admin`]. - **/ - removeCollectionAdmin: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, accountId: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - /** - * See [`Pallet::remove_collection_sponsor`]. - **/ - removeCollectionSponsor: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::remove_from_allow_list`]. - **/ - removeFromAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - /** - * See [`Pallet::repartition`]. - **/ - repartition: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32, u128]>; - /** - * See [`Pallet::set_allowance_for_all`]. - **/ - setAllowanceForAll: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, operator: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr, bool]>; - /** - * See [`Pallet::set_collection_limits`]. - **/ - setCollectionLimits: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newLimit: UpDataStructsCollectionLimits | { accountTokenOwnershipLimit?: any; sponsoredDataSize?: any; sponsoredDataRateLimit?: any; tokenLimit?: any; sponsorTransferTimeout?: any; sponsorApproveTimeout?: any; ownerCanTransfer?: any; ownerCanDestroy?: any; transfersEnabled?: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, UpDataStructsCollectionLimits]>; - /** - * See [`Pallet::set_collection_permissions`]. - **/ - setCollectionPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newPermission: UpDataStructsCollectionPermissions | { access?: any; mintMode?: any; nesting?: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, UpDataStructsCollectionPermissions]>; - /** - * See [`Pallet::set_collection_properties`]. - **/ - setCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, properties: Vec | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, Vec]>; - /** - * See [`Pallet::set_collection_sponsor`]. - **/ - setCollectionSponsor: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newSponsor: AccountId32 | string | Uint8Array) => SubmittableExtrinsic, [u32, AccountId32]>; - /** - * See [`Pallet::set_token_properties`]. - **/ - setTokenProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, properties: Vec | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, u32, Vec]>; - /** - * See [`Pallet::set_token_property_permissions`]. - **/ - setTokenPropertyPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyPermissions: Vec | (UpDataStructsPropertyKeyPermission | { key?: any; permission?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, Vec]>; - /** - * See [`Pallet::set_transfers_enabled_flag`]. - **/ - setTransfersEnabledFlag: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, value: bool | boolean | Uint8Array) => SubmittableExtrinsic, [u32, bool]>; - /** - * See [`Pallet::transfer`]. - **/ - transfer: AugmentedSubmittable<(recipient: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>; - /** - * See [`Pallet::transfer_from`]. - **/ - transferFrom: AugmentedSubmittable<(from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, recipient: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - utility: { - /** - * See [`Pallet::as_derivative`]. - **/ - asDerivative: AugmentedSubmittable<(index: u16 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [u16, Call]>; - /** - * See [`Pallet::batch`]. - **/ - batch: AugmentedSubmittable<(calls: Vec | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * See [`Pallet::batch_all`]. - **/ - batchAll: AugmentedSubmittable<(calls: Vec | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * See [`Pallet::dispatch_as`]. - **/ - dispatchAs: AugmentedSubmittable<(asOrigin: OpalRuntimeOriginCaller | { system: any } | { Void: any } | { Council: any } | { TechnicalCommittee: any } | { PolkadotXcm: any } | { CumulusXcm: any } | { Origins: any } | { Ethereum: any } | string | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic, [OpalRuntimeOriginCaller, Call]>; - /** - * See [`Pallet::force_batch`]. - **/ - forceBatch: AugmentedSubmittable<(calls: Vec | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic, [Vec]>; - /** - * See [`Pallet::with_weight`]. - **/ - withWeight: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array, weight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [Call, SpWeightsWeightV2Weight]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - vesting: { - /** - * See [`Pallet::claim`]. - **/ - claim: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::claim_for`]. - **/ - claimFor: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress]>; - /** - * See [`Pallet::update_vesting_schedules`]. - **/ - updateVestingSchedules: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, vestingSchedules: Vec | (OrmlVestingVestingSchedule | { start?: any; period?: any; periodCount?: any; perPeriod?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [MultiAddress, Vec]>; - /** - * See [`Pallet::vested_transfer`]. - **/ - vestedTransfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, schedule: OrmlVestingVestingSchedule | { start?: any; period?: any; periodCount?: any; perPeriod?: any } | string | Uint8Array) => SubmittableExtrinsic, [MultiAddress, OrmlVestingVestingSchedule]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - xcmpQueue: { - /** - * See [`Pallet::resume_xcm_execution`]. - **/ - resumeXcmExecution: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::service_overweight`]. - **/ - serviceOverweight: AugmentedSubmittable<(index: u64 | AnyNumber | Uint8Array, weightLimit: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [u64, SpWeightsWeightV2Weight]>; - /** - * See [`Pallet::suspend_xcm_execution`]. - **/ - suspendXcmExecution: AugmentedSubmittable<() => SubmittableExtrinsic, []>; - /** - * See [`Pallet::update_drop_threshold`]. - **/ - updateDropThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::update_resume_threshold`]. - **/ - updateResumeThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::update_suspend_threshold`]. - **/ - updateSuspendThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; - /** - * See [`Pallet::update_threshold_weight`]. - **/ - updateThresholdWeight: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [SpWeightsWeightV2Weight]>; - /** - * See [`Pallet::update_weight_restrict_decay`]. - **/ - updateWeightRestrictDecay: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [SpWeightsWeightV2Weight]>; - /** - * See [`Pallet::update_xcmp_max_individual_weight`]. - **/ - updateXcmpMaxIndividualWeight: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic, [SpWeightsWeightV2Weight]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - xTokens: { - /** - * See [`Pallet::transfer`]. - **/ - transfer: AugmentedSubmittable<(currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletForeignAssetsAssetId, u128, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; - /** - * See [`Pallet::transfer_multiasset`]. - **/ - transferMultiasset: AugmentedSubmittable<(asset: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; - /** - * See [`Pallet::transfer_multiassets`]. - **/ - transferMultiassets: AugmentedSubmittable<(assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeItem: u32 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiAssets, u32, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; - /** - * See [`Pallet::transfer_multiasset_with_fee`]. - **/ - transferMultiassetWithFee: AugmentedSubmittable<(asset: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, fee: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; - /** - * See [`Pallet::transfer_multicurrencies`]. - **/ - transferMulticurrencies: AugmentedSubmittable<(currencies: Vec> | ([PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, u128 | AnyNumber | Uint8Array])[], feeItem: u32 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [Vec>, u32, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; - /** - * See [`Pallet::transfer_with_fee`]. - **/ - transferWithFee: AugmentedSubmittable<(currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array, fee: u128 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletForeignAssetsAssetId, u128, u128, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>; - /** - * Generic tx - **/ - [key: string]: SubmittableExtrinsicFunction; - }; - } // AugmentedSubmittables -} // declare module --- a/js-packages/types/src/augment-api.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-chain`, do not edit -/* eslint-disable */ - -import './augment-api-consts.js'; -import './augment-api-errors.js'; -import './augment-api-events.js'; -import './augment-api-query.js'; -import './augment-api-tx.js'; -import './augment-api-rpc.js'; -import './augment-api-runtime.js'; --- a/js-packages/types/src/augment-types.ts +++ /dev/null @@ -1,1232 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -// import type lookup before we augment - in some environments -// this is required to allow for ambient/previous definitions -import '@polkadot/types/types/registry'; - -import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemCodeUpgradeAuthorization, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity, CumulusPalletParachainSystemUnincludedSegmentAncestor, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate, CumulusPalletParachainSystemUnincludedSegmentSegmentTracker, CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OpalRuntimeRuntimeCommonSessionKeys, OpalRuntimeRuntimeHoldReason, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesIdAmount, PalletBalancesReasons, PalletBalancesReserveData, PalletCollatorSelectionCall, PalletCollatorSelectionError, PalletCollatorSelectionEvent, PalletCollatorSelectionHoldReason, PalletCollectiveCall, PalletCollectiveError, PalletCollectiveEvent, PalletCollectiveRawOrigin, PalletCollectiveVotes, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletConfigurationEvent, PalletDemocracyCall, PalletDemocracyConviction, PalletDemocracyDelegations, PalletDemocracyError, PalletDemocracyEvent, PalletDemocracyMetadataOwner, PalletDemocracyReferendumInfo, PalletDemocracyReferendumStatus, PalletDemocracyTally, PalletDemocracyVoteAccountVote, PalletDemocracyVotePriorLock, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCodeMetadata, PalletEvmCoderSubstrateError, PalletEvmContractHelpersCall, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletGovOriginsOrigin, PalletIdentityBitFlags, PalletIdentityCall, PalletIdentityError, PalletIdentityEvent, PalletIdentityIdentityField, PalletIdentityIdentityInfo, PalletIdentityJudgement, PalletIdentityRegistrarInfo, PalletIdentityRegistration, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletMembershipCall, PalletMembershipError, PalletMembershipEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletPreimageCall, PalletPreimageError, PalletPreimageEvent, PalletPreimageRequestStatus, PalletRankedCollectiveCall, PalletRankedCollectiveError, PalletRankedCollectiveEvent, PalletRankedCollectiveMemberRecord, PalletRankedCollectiveTally, PalletRankedCollectiveVoteRecord, PalletReferendaCall, PalletReferendaCurve, PalletReferendaDecidingStatus, PalletReferendaDeposit, PalletReferendaError, PalletReferendaEvent, PalletReferendaReferendumInfo, PalletReferendaReferendumStatus, PalletReferendaTrackInfo, PalletRefungibleError, PalletSchedulerCall, PalletSchedulerError, PalletSchedulerEvent, PalletSchedulerScheduled, PalletSessionCall, PalletSessionError, PalletSessionEvent, PalletStateTrieMigrationCall, PalletStateTrieMigrationError, PalletStateTrieMigrationEvent, PalletStateTrieMigrationMigrationCompute, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletStateTrieMigrationProgress, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUtilityCall, PalletUtilityError, PalletUtilityEvent, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PalletXcmQueryStatus, PalletXcmRemoteLockedFungibleRecord, PalletXcmVersionMigrationStage, ParachainInfoCall, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat, PolkadotPrimitivesV5AbridgedHostConfiguration, PolkadotPrimitivesV5AbridgedHrmpChannel, PolkadotPrimitivesV5PersistedValidationData, PolkadotPrimitivesV5UpgradeGoAhead, PolkadotPrimitivesV5UpgradeRestriction, PolkadotPrimitivesVstagingAsyncBackingParams, SpArithmeticArithmeticError, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Public, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionValidityInvalidTransaction, SpRuntimeTransactionValidityTransactionValidityError, SpRuntimeTransactionValidityUnknownTransaction, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, StagingXcmDoubleEncoded, StagingXcmV2BodyId, StagingXcmV2BodyPart, StagingXcmV2Instruction, StagingXcmV2Junction, StagingXcmV2MultiAsset, StagingXcmV2MultiLocation, StagingXcmV2MultiassetAssetId, StagingXcmV2MultiassetAssetInstance, StagingXcmV2MultiassetFungibility, StagingXcmV2MultiassetMultiAssetFilter, StagingXcmV2MultiassetMultiAssets, StagingXcmV2MultiassetWildFungibility, StagingXcmV2MultiassetWildMultiAsset, StagingXcmV2MultilocationJunctions, StagingXcmV2NetworkId, StagingXcmV2OriginKind, StagingXcmV2Response, StagingXcmV2TraitsError, StagingXcmV2WeightLimit, StagingXcmV2Xcm, StagingXcmV3Instruction, StagingXcmV3Junction, StagingXcmV3JunctionBodyId, StagingXcmV3JunctionBodyPart, StagingXcmV3JunctionNetworkId, StagingXcmV3Junctions, StagingXcmV3MaybeErrorCode, StagingXcmV3MultiAsset, StagingXcmV3MultiLocation, StagingXcmV3MultiassetAssetId, StagingXcmV3MultiassetAssetInstance, StagingXcmV3MultiassetFungibility, StagingXcmV3MultiassetMultiAssetFilter, StagingXcmV3MultiassetMultiAssets, StagingXcmV3MultiassetWildFungibility, StagingXcmV3MultiassetWildMultiAsset, StagingXcmV3PalletInfo, StagingXcmV3QueryResponseInfo, StagingXcmV3Response, StagingXcmV3TraitsError, StagingXcmV3TraitsOutcome, StagingXcmV3WeightLimit, StagingXcmV3Xcm, StagingXcmVersionedAssetId, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation, StagingXcmVersionedResponse, StagingXcmVersionedXcm, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, - UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo, UpPovEstimateRpcTrieKeyValue } from '@polkadot/types/lookup'; -import type { Data, StorageKey } from '@polkadot/types'; -import type { BitVec, Bool, Bytes, F32, F64, I128, I16, I256, I32, I64, I8, ISize, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, f32, f64, i128, i16, i256, i32, i64, i8, isize, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec'; -import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets'; -import type { BlockAttestations, IncludedBlocks, MoreAttestations } from '@polkadot/types/interfaces/attestations'; -import type { RawAuraPreDigest } from '@polkadot/types/interfaces/aura'; -import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author'; -import type { UncleEntryItem } from '@polkadot/types/interfaces/authorship'; -import type { AllowedSlots, BabeAuthorityWeight, BabeBlockWeight, BabeEpochConfiguration, BabeEquivocationProof, BabeGenesisConfiguration, BabeGenesisConfigurationV1, BabeWeight, Epoch, EpochAuthorship, MaybeRandomness, MaybeVrf, NextConfigDescriptor, NextConfigDescriptorV1, OpaqueKeyOwnershipProof, Randomness, RawBabePreDigest, RawBabePreDigestCompat, RawBabePreDigestPrimary, RawBabePreDigestPrimaryTo159, RawBabePreDigestSecondaryPlain, RawBabePreDigestSecondaryTo159, RawBabePreDigestSecondaryVRF, RawBabePreDigestTo159, SlotNumber, VrfData, VrfOutput, VrfProof } from '@polkadot/types/interfaces/babe'; -import type { AccountData, BalanceLock, BalanceLockTo212, BalanceStatus, Reasons, ReserveData, ReserveIdentifier, VestingSchedule, WithdrawReasons } from '@polkadot/types/interfaces/balances'; -import type { BeefyAuthoritySet, BeefyCommitment, BeefyEquivocationProof, BeefyId, BeefyNextAuthoritySet, BeefyPayload, BeefyPayloadId, BeefySignedCommitment, BeefyVoteMessage, MmrRootHash, ValidatorSet, ValidatorSetId } from '@polkadot/types/interfaces/beefy'; -import type { BenchmarkBatch, BenchmarkConfig, BenchmarkList, BenchmarkMetadata, BenchmarkParameter, BenchmarkResult } from '@polkadot/types/interfaces/benchmark'; -import type { CheckInherentsResult, InherentData, InherentIdentifier } from '@polkadot/types/interfaces/blockbuilder'; -import type { BridgeMessageId, BridgedBlockHash, BridgedBlockNumber, BridgedHeader, CallOrigin, ChainId, DeliveredMessages, DispatchFeePayment, InboundLaneData, InboundRelayer, InitializationData, LaneId, MessageData, MessageKey, MessageNonce, MessagesDeliveryProofOf, MessagesProofOf, OperatingMode, OutboundLaneData, OutboundMessageFee, OutboundPayload, Parameter, RelayerId, UnrewardedRelayer, UnrewardedRelayersState } from '@polkadot/types/interfaces/bridges'; -import type { BlockHash } from '@polkadot/types/interfaces/chain'; -import type { PrefixedStorageKey } from '@polkadot/types/interfaces/childstate'; -import type { StatementKind } from '@polkadot/types/interfaces/claims'; -import type { CollectiveOrigin, MemberCount, ProposalIndex, Votes, VotesTo230 } from '@polkadot/types/interfaces/collective'; -import type { AuthorityId, RawVRFOutput } from '@polkadot/types/interfaces/consensus'; -import type { AliveContractInfo, CodeHash, CodeSource, CodeUploadRequest, CodeUploadResult, CodeUploadResultValue, ContractCallFlags, ContractCallRequest, ContractExecResult, ContractExecResultOk, ContractExecResultResult, ContractExecResultSuccessTo255, ContractExecResultSuccessTo260, ContractExecResultTo255, ContractExecResultTo260, ContractExecResultTo267, ContractExecResultU64, ContractInfo, ContractInstantiateResult, ContractInstantiateResultTo267, ContractInstantiateResultTo299, ContractInstantiateResultU64, ContractReturnFlags, ContractStorageKey, DeletedContract, ExecReturnValue, Gas, HostFnWeights, HostFnWeightsTo264, InstantiateRequest, InstantiateRequestV1, InstantiateRequestV2, InstantiateReturnValue, InstantiateReturnValueOk, InstantiateReturnValueTo267, InstructionWeights, Limits, LimitsTo264, PrefabWasmModule, RentProjection, Schedule, ScheduleTo212, ScheduleTo258, ScheduleTo264, SeedOf, StorageDeposit, TombstoneContractInfo, TrieId } from '@polkadot/types/interfaces/contracts'; -import type { ContractConstructorSpecLatest, ContractConstructorSpecV0, ContractConstructorSpecV1, ContractConstructorSpecV2, ContractConstructorSpecV3, ContractConstructorSpecV4, ContractContractSpecV0, ContractContractSpecV1, ContractContractSpecV2, ContractContractSpecV3, ContractContractSpecV4, ContractCryptoHasher, ContractDiscriminant, ContractDisplayName, ContractEnvironmentV4, ContractEventParamSpecLatest, ContractEventParamSpecV0, ContractEventParamSpecV2, ContractEventSpecLatest, ContractEventSpecV0, ContractEventSpecV1, ContractEventSpecV2, ContractLayoutArray, ContractLayoutCell, ContractLayoutEnum, ContractLayoutHash, ContractLayoutHashingStrategy, ContractLayoutKey, ContractLayoutStruct, ContractLayoutStructField, ContractMessageParamSpecLatest, ContractMessageParamSpecV0, ContractMessageParamSpecV2, ContractMessageSpecLatest, ContractMessageSpecV0, ContractMessageSpecV1, ContractMessageSpecV2, ContractMessageSpecV3, ContractMetadata, ContractMetadataLatest, ContractMetadataV0, ContractMetadataV1, ContractMetadataV2, ContractMetadataV3, ContractMetadataV4, ContractProject, ContractProjectContract, ContractProjectInfo, ContractProjectSource, ContractProjectV0, ContractSelector, ContractStorageLayout, ContractTypeSpec } from '@polkadot/types/interfaces/contractsAbi'; -import type { FundIndex, FundInfo, LastContribution, TrieIndex } from '@polkadot/types/interfaces/crowdloan'; -import type { CollationInfo, CollationInfoV1, ConfigData, MessageId, OverweightIndex, PageCounter, PageIndexData } from '@polkadot/types/interfaces/cumulus'; -import type { AccountVote, AccountVoteSplit, AccountVoteStandard, Conviction, Delegations, PreimageStatus, PreimageStatusAvailable, PriorLock, PropIndex, Proposal, ProxyState, ReferendumIndex, ReferendumInfo, ReferendumInfoFinished, ReferendumInfoTo239, ReferendumStatus, Tally, Voting, VotingDelegating, VotingDirect, VotingDirectVote } from '@polkadot/types/interfaces/democracy'; -import type { BlockStats } from '@polkadot/types/interfaces/dev'; -import type { ApprovalFlag, DefunctVoter, Renouncing, SetIndex, Vote, VoteIndex, VoteThreshold, VoterInfo } from '@polkadot/types/interfaces/elections'; -import type { CreatedBlock, ImportedAux } from '@polkadot/types/interfaces/engine'; -import type { BlockV0, BlockV1, BlockV2, EIP1559Transaction, EIP2930Transaction, EthAccessList, EthAccessListItem, EthAccount, EthAddress, EthBlock, EthBloom, EthCallRequest, EthFeeHistory, EthFilter, EthFilterAddress, EthFilterChanges, EthFilterTopic, EthFilterTopicEntry, EthFilterTopicInner, EthHeader, EthLog, EthReceipt, EthReceiptV0, EthReceiptV3, EthRichBlock, EthRichHeader, EthStorageProof, EthSubKind, EthSubParams, EthSubResult, EthSyncInfo, EthSyncStatus, EthTransaction, EthTransactionAction, EthTransactionCondition, EthTransactionRequest, EthTransactionSignature, EthTransactionStatus, EthWork, EthereumAccountId, EthereumAddress, EthereumLookupSource, EthereumSignature, LegacyTransaction, TransactionV0, TransactionV1, TransactionV2 } from '@polkadot/types/interfaces/eth'; -import type { EvmAccount, EvmCallInfo, EvmCallInfoV2, EvmCreateInfo, EvmCreateInfoV2, EvmLog, EvmVicinity, EvmWeightInfo, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed } from '@polkadot/types/interfaces/evm'; -import type { AnySignature, EcdsaSignature, Ed25519Signature, Era, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicSignature, ExtrinsicSignatureV4, ExtrinsicUnknown, ExtrinsicV4, ImmortalEra, MortalEra, MultiSignature, Signature, SignerPayload, Sr25519Signature } from '@polkadot/types/interfaces/extrinsics'; -import type { FungiblesAccessError } from '@polkadot/types/interfaces/fungibles'; -import type { AssetOptions, Owner, PermissionLatest, PermissionVersions, PermissionsV1 } from '@polkadot/types/interfaces/genericAsset'; -import type { ActiveGilt, ActiveGiltsTotal, ActiveIndex, GiltBid } from '@polkadot/types/interfaces/gilt'; -import type { AuthorityIndex, AuthorityList, AuthoritySet, AuthoritySetChange, AuthoritySetChanges, AuthorityWeight, DelayKind, DelayKindBest, EncodedFinalityProofs, ForkTreePendingChange, ForkTreePendingChangeNode, GrandpaCommit, GrandpaEquivocation, GrandpaEquivocationProof, GrandpaEquivocationValue, GrandpaJustification, GrandpaPrecommit, GrandpaPrevote, GrandpaSignedPrecommit, JustificationNotification, KeyOwnerProof, NextAuthority, PendingChange, PendingPause, PendingResume, Precommits, Prevotes, ReportedRoundStates, RoundState, SetId, StoredPendingChange, StoredState } from '@polkadot/types/interfaces/grandpa'; -import type { IdentityFields, IdentityInfo, IdentityInfoAdditional, IdentityInfoTo198, IdentityJudgement, RegistrarIndex, RegistrarInfo, Registration, RegistrationJudgement, RegistrationTo198 } from '@polkadot/types/interfaces/identity'; -import type { AuthIndex, AuthoritySignature, Heartbeat, HeartbeatTo244, OpaqueMultiaddr, OpaqueNetworkState, OpaquePeerId } from '@polkadot/types/interfaces/imOnline'; -import type { CallIndex, LotteryConfig } from '@polkadot/types/interfaces/lottery'; -import type { CustomMetadata15, CustomValueMetadata15, ErrorMetadataLatest, ErrorMetadataV10, ErrorMetadataV11, ErrorMetadataV12, ErrorMetadataV13, ErrorMetadataV14, ErrorMetadataV9, EventMetadataLatest, EventMetadataV10, EventMetadataV11, EventMetadataV12, EventMetadataV13, EventMetadataV14, EventMetadataV9, ExtrinsicMetadataLatest, ExtrinsicMetadataV11, ExtrinsicMetadataV12, ExtrinsicMetadataV13, ExtrinsicMetadataV14, ExtrinsicMetadataV15, FunctionArgumentMetadataLatest, FunctionArgumentMetadataV10, FunctionArgumentMetadataV11, FunctionArgumentMetadataV12, FunctionArgumentMetadataV13, FunctionArgumentMetadataV14, FunctionArgumentMetadataV9, FunctionMetadataLatest, FunctionMetadataV10, FunctionMetadataV11, FunctionMetadataV12, FunctionMetadataV13, FunctionMetadataV14, FunctionMetadataV9, MetadataAll, MetadataLatest, MetadataV10, MetadataV11, MetadataV12, MetadataV13, MetadataV14, MetadataV15, MetadataV9, ModuleConstantMetadataV10, ModuleConstantMetadataV11, ModuleConstantMetadataV12, ModuleConstantMetadataV13, ModuleConstantMetadataV9, ModuleMetadataV10, ModuleMetadataV11, ModuleMetadataV12, ModuleMetadataV13, ModuleMetadataV9, OpaqueMetadata, OuterEnums15, PalletCallMetadataLatest, PalletCallMetadataV14, PalletConstantMetadataLatest, PalletConstantMetadataV14, PalletErrorMetadataLatest, PalletErrorMetadataV14, PalletEventMetadataLatest, PalletEventMetadataV14, PalletMetadataLatest, PalletMetadataV14, PalletMetadataV15, PalletStorageMetadataLatest, PalletStorageMetadataV14, PortableType, PortableTypeV14, RuntimeApiMetadataLatest, RuntimeApiMetadataV15, RuntimeApiMethodMetadataV15, RuntimeApiMethodParamMetadataV15, SignedExtensionMetadataLatest, SignedExtensionMetadataV14, StorageEntryMetadataLatest, StorageEntryMetadataV10, StorageEntryMetadataV11, StorageEntryMetadataV12, StorageEntryMetadataV13, StorageEntryMetadataV14, StorageEntryMetadataV9, StorageEntryModifierLatest, StorageEntryModifierV10, StorageEntryModifierV11, StorageEntryModifierV12, StorageEntryModifierV13, StorageEntryModifierV14, StorageEntryModifierV9, StorageEntryTypeLatest, StorageEntryTypeV10, StorageEntryTypeV11, StorageEntryTypeV12, StorageEntryTypeV13, StorageEntryTypeV14, StorageEntryTypeV9, StorageHasher, StorageHasherV10, StorageHasherV11, StorageHasherV12, StorageHasherV13, StorageHasherV14, StorageHasherV9, StorageMetadataV10, StorageMetadataV11, StorageMetadataV12, StorageMetadataV13, StorageMetadataV9 } from '@polkadot/types/interfaces/metadata'; -import type { MmrBatchProof, MmrEncodableOpaqueLeaf, MmrError, MmrHash, MmrLeafBatchProof, MmrLeafIndex, MmrLeafProof, MmrNodeIndex, MmrProof } from '@polkadot/types/interfaces/mmr'; -import type { NftCollectionId, NftItemId } from '@polkadot/types/interfaces/nfts'; -import type { NpApiError, NpPoolId } from '@polkadot/types/interfaces/nompools'; -import type { StorageKind } from '@polkadot/types/interfaces/offchain'; -import type { DeferredOffenceOf, Kind, OffenceDetails, Offender, OpaqueTimeSlot, ReportIdOf, Reporter } from '@polkadot/types/interfaces/offences'; -import type { AbridgedCandidateReceipt, AbridgedHostConfiguration, AbridgedHrmpChannel, AssignmentId, AssignmentKind, AttestedCandidate, AuctionIndex, AuthorityDiscoveryId, AvailabilityBitfield, AvailabilityBitfieldRecord, BackedCandidate, Bidder, BufferedSessionChange, CandidateCommitments, CandidateDescriptor, CandidateEvent, CandidateHash, CandidateInfo, CandidatePendingAvailability, CandidateReceipt, CollatorId, CollatorSignature, CommittedCandidateReceipt, CoreAssignment, CoreIndex, CoreOccupied, CoreState, DisputeLocation, DisputeProof, DisputeResult, DisputeState, DisputeStatement, DisputeStatementSet, DisputesTimeSlot, DoubleVoteReport, DownwardMessage, ExecutorParam, ExecutorParams, ExecutorParamsHash, ExplicitDisputeStatement, GlobalValidationData, GlobalValidationSchedule, GroupIndex, GroupRotationInfo, HeadData, HostConfiguration, HrmpChannel, HrmpChannelId, HrmpOpenChannelRequest, InboundDownwardMessage, InboundHrmpMessage, InboundHrmpMessages, IncomingParachain, IncomingParachainDeploy, IncomingParachainFixed, InvalidDisputeStatementKind, LeasePeriod, LeasePeriodOf, LocalValidationData, MessageIngestionType, MessageQueueChain, MessagingStateSnapshot, MessagingStateSnapshotEgressEntry, MultiDisputeStatementSet, NewBidder, OccupiedCore, OccupiedCoreAssumption, OldV1SessionInfo, OutboundHrmpMessage, ParaGenesisArgs, ParaId, ParaInfo, ParaLifecycle, ParaPastCodeMeta, ParaScheduling, ParaValidatorIndex, ParachainDispatchOrigin, ParachainInherentData, ParachainProposal, ParachainsInherentData, ParathreadClaim, ParathreadClaimQueue, ParathreadEntry, PendingSlashes, PersistedValidationData, PvfCheckStatement, PvfExecTimeoutKind, PvfPrepTimeoutKind, QueuedParathread, RegisteredParachainInfo, RelayBlockNumber, RelayChainBlockNumber, RelayChainHash, RelayHash, Remark, ReplacementTimes, Retriable, ScheduledCore, Scheduling, ScrapedOnChainVotes, ServiceQuality, SessionInfo, SessionInfoValidatorGroup, SignedAvailabilityBitfield, SignedAvailabilityBitfields, SigningContext, SlashingOffenceKind, SlotRange, SlotRange10, Statement, SubId, SystemInherentData, TransientValidationData, UpgradeGoAhead, UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode, ValidationCodeHash, ValidationData, ValidationDataType, ValidationFunctionParams, ValidatorSignature, ValidityAttestation, VecInboundHrmpMessage, WinnersData, WinnersData10, WinnersDataTuple, WinnersDataTuple10, WinningData, WinningData10, WinningDataEntry } from '@polkadot/types/interfaces/parachains'; -import type { FeeDetails, InclusionFee, RuntimeDispatchInfo, RuntimeDispatchInfoV1, RuntimeDispatchInfoV2 } from '@polkadot/types/interfaces/payment'; -import type { Approvals } from '@polkadot/types/interfaces/poll'; -import type { ProxyAnnouncement, ProxyDefinition, ProxyType } from '@polkadot/types/interfaces/proxy'; -import type { AccountStatus, AccountValidity } from '@polkadot/types/interfaces/purchase'; -import type { ActiveRecovery, RecoveryConfig } from '@polkadot/types/interfaces/recovery'; -import type { RpcMethods } from '@polkadot/types/interfaces/rpc'; -import type { AccountId, AccountId20, AccountId32, AccountId33, AccountIdOf, AccountIndex, Address, AssetId, Balance, BalanceOf, Block, BlockNumber, BlockNumberFor, BlockNumberOf, Call, CallHash, CallHashOf, ChangesTrieConfiguration, ChangesTrieSignal, CodecHash, Consensus, ConsensusEngineId, CrateVersion, Digest, DigestItem, EncodedJustification, ExtrinsicsWeight, Fixed128, Fixed64, FixedI128, FixedI64, FixedU128, FixedU64, H1024, H128, H160, H2048, H256, H32, H512, H64, Hash, Header, HeaderPartial, I32F32, Index, IndicesLookupSource, Justification, Justifications, KeyTypeId, KeyValue, LockIdentifier, LookupSource, LookupTarget, ModuleId, Moment, MultiAddress, MultiSigner, OpaqueCall, Origin, OriginCaller, PalletId, PalletVersion, PalletsOrigin, Pays, PerU16, Perbill, Percent, Permill, Perquintill, Phantom, PhantomData, PreRuntime, Releases, RuntimeCall, RuntimeDbWeight, RuntimeEvent, Seal, SealV0, SignedBlock, SignedBlockWithJustification, SignedBlockWithJustifications, Slot, SlotDuration, StorageData, StorageInfo, StorageProof, TransactionInfo, TransactionLongevity, TransactionPriority, TransactionStorageProof, TransactionTag, U32F32, ValidatorId, ValidatorIdOf, Weight, WeightMultiplier, WeightV0, WeightV1, WeightV2 } from '@polkadot/types/interfaces/runtime'; -import type { Si0Field, Si0LookupTypeId, Si0Path, Si0Type, Si0TypeDef, Si0TypeDefArray, Si0TypeDefBitSequence, Si0TypeDefCompact, Si0TypeDefComposite, Si0TypeDefPhantom, Si0TypeDefPrimitive, Si0TypeDefSequence, Si0TypeDefTuple, Si0TypeDefVariant, Si0TypeParameter, Si0Variant, Si1Field, Si1LookupTypeId, Si1Path, Si1Type, Si1TypeDef, Si1TypeDefArray, Si1TypeDefBitSequence, Si1TypeDefCompact, Si1TypeDefComposite, Si1TypeDefPrimitive, Si1TypeDefSequence, Si1TypeDefTuple, Si1TypeDefVariant, Si1TypeParameter, Si1Variant, SiField, SiLookupTypeId, SiPath, SiType, SiTypeDef, SiTypeDefArray, SiTypeDefBitSequence, SiTypeDefCompact, SiTypeDefComposite, SiTypeDefPrimitive, SiTypeDefSequence, SiTypeDefTuple, SiTypeDefVariant, SiTypeParameter, SiVariant } from '@polkadot/types/interfaces/scaleInfo'; -import type { Period, Priority, SchedulePeriod, SchedulePriority, Scheduled, ScheduledTo254, TaskAddress } from '@polkadot/types/interfaces/scheduler'; -import type { BeefyKey, FullIdentification, IdentificationTuple, Keys, MembershipProof, SessionIndex, SessionKeys1, SessionKeys10, SessionKeys10B, SessionKeys2, SessionKeys3, SessionKeys4, SessionKeys5, SessionKeys6, SessionKeys6B, SessionKeys7, SessionKeys7B, SessionKeys8, SessionKeys8B, SessionKeys9, SessionKeys9B, ValidatorCount } from '@polkadot/types/interfaces/session'; -import type { Bid, BidKind, SocietyJudgement, SocietyVote, StrikeCount, VouchingStatus } from '@polkadot/types/interfaces/society'; -import type { ActiveEraInfo, CompactAssignments, CompactAssignmentsTo257, CompactAssignmentsTo265, CompactAssignmentsWith16, CompactAssignmentsWith24, CompactScore, CompactScoreCompact, ElectionCompute, ElectionPhase, ElectionResult, ElectionScore, ElectionSize, ElectionStatus, EraIndex, EraPoints, EraRewardPoints, EraRewards, Exposure, ExtendedBalance, Forcing, IndividualExposure, KeyType, MomentOf, Nominations, NominatorIndex, NominatorIndexCompact, OffchainAccuracy, OffchainAccuracyCompact, PhragmenScore, Points, RawSolution, RawSolutionTo265, RawSolutionWith16, RawSolutionWith24, ReadySolution, RewardDestination, RewardPoint, RoundSnapshot, SeatHolder, SignedSubmission, SignedSubmissionOf, SignedSubmissionTo276, SlashJournalEntry, SlashingSpans, SlashingSpansTo204, SolutionOrSnapshotSize, SolutionSupport, SolutionSupports, SpanIndex, SpanRecord, StakingLedger, StakingLedgerTo223, StakingLedgerTo240, SubmissionIndicesOf, Supports, UnappliedSlash, UnappliedSlashOther, UnlockChunk, ValidatorIndex, ValidatorIndexCompact, ValidatorPrefs, ValidatorPrefsTo145, ValidatorPrefsTo196, ValidatorPrefsWithBlocked, ValidatorPrefsWithCommission, VoteWeight, Voter } from '@polkadot/types/interfaces/staking'; -import type { ApiId, BlockTrace, BlockTraceEvent, BlockTraceEventData, BlockTraceSpan, KeyValueOption, MigrationStatusResult, ReadProof, RuntimeVersion, RuntimeVersionApi, RuntimeVersionPartial, RuntimeVersionPre3, RuntimeVersionPre4, SpecVersion, StorageChangeSet, TraceBlockResponse, TraceError } from '@polkadot/types/interfaces/state'; -import type { WeightToFeeCoefficient } from '@polkadot/types/interfaces/support'; -import type { AccountInfo, AccountInfoWithDualRefCount, AccountInfoWithProviders, AccountInfoWithRefCount, AccountInfoWithRefCountU8, AccountInfoWithTripleRefCount, ApplyExtrinsicResult, ApplyExtrinsicResultPre6, ArithmeticError, BlockLength, BlockWeights, ChainProperties, ChainType, ConsumedWeight, DigestOf, DispatchClass, DispatchError, DispatchErrorModule, DispatchErrorModulePre6, DispatchErrorModuleU8, DispatchErrorModuleU8a, DispatchErrorPre6, DispatchErrorPre6First, DispatchErrorTo198, DispatchInfo, DispatchInfoTo190, DispatchInfoTo244, DispatchOutcome, DispatchOutcomePre6, DispatchResult, DispatchResultOf, DispatchResultTo198, Event, EventId, EventIndex, EventRecord, Health, InvalidTransaction, Key, LastRuntimeUpgradeInfo, NetworkState, NetworkStatePeerset, NetworkStatePeersetInfo, NodeRole, NotConnectedPeer, Peer, PeerEndpoint, PeerEndpointAddr, PeerInfo, PeerPing, PerDispatchClassU32, PerDispatchClassWeight, PerDispatchClassWeightsPerClass, Phase, RawOrigin, RefCount, RefCountTo259, SyncState, SystemOrigin, TokenError, TransactionValidityError, TransactionalError, UnknownTransaction, WeightPerClass } from '@polkadot/types/interfaces/system'; -import type { Bounty, BountyIndex, BountyStatus, BountyStatusActive, BountyStatusCuratorProposed, BountyStatusPendingPayout, OpenTip, OpenTipFinderTo225, OpenTipTip, OpenTipTo225, TreasuryProposal } from '@polkadot/types/interfaces/treasury'; -import type { Multiplier } from '@polkadot/types/interfaces/txpayment'; -import type { TransactionSource, TransactionValidity, ValidTransaction } from '@polkadot/types/interfaces/txqueue'; -import type { ClassDetails, ClassId, ClassMetadata, DepositBalance, DepositBalanceOf, DestroyWitness, InstanceDetails, InstanceId, InstanceMetadata } from '@polkadot/types/interfaces/uniques'; -import type { Multisig, Timepoint } from '@polkadot/types/interfaces/utility'; -import type { VestingInfo } from '@polkadot/types/interfaces/vesting'; -import type { AssetInstance, AssetInstanceV0, AssetInstanceV1, AssetInstanceV2, BodyId, BodyPart, DoubleEncodedCall, Fungibility, FungibilityV0, FungibilityV1, FungibilityV2, InboundStatus, InstructionV2, InteriorMultiLocation, Junction, JunctionV0, JunctionV1, JunctionV2, Junctions, JunctionsV1, JunctionsV2, MultiAsset, MultiAssetFilter, MultiAssetFilterV1, MultiAssetFilterV2, MultiAssetV0, MultiAssetV1, MultiAssetV2, MultiAssets, MultiAssetsV1, MultiAssetsV2, MultiLocation, MultiLocationV0, MultiLocationV1, MultiLocationV2, NetworkId, OriginKindV0, OriginKindV1, OriginKindV2, OutboundStatus, Outcome, QueryId, QueryStatus, QueueConfigData, Response, ResponseV0, ResponseV1, ResponseV2, ResponseV2Error, ResponseV2Result, VersionMigrationStage, VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation, VersionedResponse, VersionedXcm, WeightLimitV2, WildFungibility, WildFungibilityV0, WildFungibilityV1, WildFungibilityV2, WildMultiAsset, WildMultiAssetV1, WildMultiAssetV2, Xcm, XcmAssetId, XcmError, XcmErrorV0, XcmErrorV1, XcmErrorV2, XcmOrder, XcmOrderV0, XcmOrderV1, XcmOrderV2, XcmOrigin, XcmOriginKind, XcmV0, XcmV1, XcmV2, XcmVersion, XcmpMessageFormat } from '@polkadot/types/interfaces/xcm'; - -declare module '@polkadot/types/types/registry' { - interface InterfaceTypes { - AbridgedCandidateReceipt: AbridgedCandidateReceipt; - AbridgedHostConfiguration: AbridgedHostConfiguration; - AbridgedHrmpChannel: AbridgedHrmpChannel; - AccountData: AccountData; - AccountId: AccountId; - AccountId20: AccountId20; - AccountId32: AccountId32; - AccountId33: AccountId33; - AccountIdOf: AccountIdOf; - AccountIndex: AccountIndex; - AccountInfo: AccountInfo; - AccountInfoWithDualRefCount: AccountInfoWithDualRefCount; - AccountInfoWithProviders: AccountInfoWithProviders; - AccountInfoWithRefCount: AccountInfoWithRefCount; - AccountInfoWithRefCountU8: AccountInfoWithRefCountU8; - AccountInfoWithTripleRefCount: AccountInfoWithTripleRefCount; - AccountStatus: AccountStatus; - AccountValidity: AccountValidity; - AccountVote: AccountVote; - AccountVoteSplit: AccountVoteSplit; - AccountVoteStandard: AccountVoteStandard; - ActiveEraInfo: ActiveEraInfo; - ActiveGilt: ActiveGilt; - ActiveGiltsTotal: ActiveGiltsTotal; - ActiveIndex: ActiveIndex; - ActiveRecovery: ActiveRecovery; - Address: Address; - AliveContractInfo: AliveContractInfo; - AllowedSlots: AllowedSlots; - AnySignature: AnySignature; - ApiId: ApiId; - ApplyExtrinsicResult: ApplyExtrinsicResult; - ApplyExtrinsicResultPre6: ApplyExtrinsicResultPre6; - ApprovalFlag: ApprovalFlag; - Approvals: Approvals; - ArithmeticError: ArithmeticError; - AssetApproval: AssetApproval; - AssetApprovalKey: AssetApprovalKey; - AssetBalance: AssetBalance; - AssetDestroyWitness: AssetDestroyWitness; - AssetDetails: AssetDetails; - AssetId: AssetId; - AssetInstance: AssetInstance; - AssetInstanceV0: AssetInstanceV0; - AssetInstanceV1: AssetInstanceV1; - AssetInstanceV2: AssetInstanceV2; - AssetMetadata: AssetMetadata; - AssetOptions: AssetOptions; - AssignmentId: AssignmentId; - AssignmentKind: AssignmentKind; - AttestedCandidate: AttestedCandidate; - AuctionIndex: AuctionIndex; - AuthIndex: AuthIndex; - AuthorityDiscoveryId: AuthorityDiscoveryId; - AuthorityId: AuthorityId; - AuthorityIndex: AuthorityIndex; - AuthorityList: AuthorityList; - AuthoritySet: AuthoritySet; - AuthoritySetChange: AuthoritySetChange; - AuthoritySetChanges: AuthoritySetChanges; - AuthoritySignature: AuthoritySignature; - AuthorityWeight: AuthorityWeight; - AvailabilityBitfield: AvailabilityBitfield; - AvailabilityBitfieldRecord: AvailabilityBitfieldRecord; - BabeAuthorityWeight: BabeAuthorityWeight; - BabeBlockWeight: BabeBlockWeight; - BabeEpochConfiguration: BabeEpochConfiguration; - BabeEquivocationProof: BabeEquivocationProof; - BabeGenesisConfiguration: BabeGenesisConfiguration; - BabeGenesisConfigurationV1: BabeGenesisConfigurationV1; - BabeWeight: BabeWeight; - BackedCandidate: BackedCandidate; - Balance: Balance; - BalanceLock: BalanceLock; - BalanceLockTo212: BalanceLockTo212; - BalanceOf: BalanceOf; - BalanceStatus: BalanceStatus; - BeefyAuthoritySet: BeefyAuthoritySet; - BeefyCommitment: BeefyCommitment; - BeefyEquivocationProof: BeefyEquivocationProof; - BeefyId: BeefyId; - BeefyKey: BeefyKey; - BeefyNextAuthoritySet: BeefyNextAuthoritySet; - BeefyPayload: BeefyPayload; - BeefyPayloadId: BeefyPayloadId; - BeefySignedCommitment: BeefySignedCommitment; - BeefyVoteMessage: BeefyVoteMessage; - BenchmarkBatch: BenchmarkBatch; - BenchmarkConfig: BenchmarkConfig; - BenchmarkList: BenchmarkList; - BenchmarkMetadata: BenchmarkMetadata; - BenchmarkParameter: BenchmarkParameter; - BenchmarkResult: BenchmarkResult; - Bid: Bid; - Bidder: Bidder; - BidKind: BidKind; - BitVec: BitVec; - Block: Block; - BlockAttestations: BlockAttestations; - BlockHash: BlockHash; - BlockLength: BlockLength; - BlockNumber: BlockNumber; - BlockNumberFor: BlockNumberFor; - BlockNumberOf: BlockNumberOf; - BlockStats: BlockStats; - BlockTrace: BlockTrace; - BlockTraceEvent: BlockTraceEvent; - BlockTraceEventData: BlockTraceEventData; - BlockTraceSpan: BlockTraceSpan; - BlockV0: BlockV0; - BlockV1: BlockV1; - BlockV2: BlockV2; - BlockWeights: BlockWeights; - BodyId: BodyId; - BodyPart: BodyPart; - bool: bool; - Bool: Bool; - Bounty: Bounty; - BountyIndex: BountyIndex; - BountyStatus: BountyStatus; - BountyStatusActive: BountyStatusActive; - BountyStatusCuratorProposed: BountyStatusCuratorProposed; - BountyStatusPendingPayout: BountyStatusPendingPayout; - BridgedBlockHash: BridgedBlockHash; - BridgedBlockNumber: BridgedBlockNumber; - BridgedHeader: BridgedHeader; - BridgeMessageId: BridgeMessageId; - BufferedSessionChange: BufferedSessionChange; - Bytes: Bytes; - Call: Call; - CallHash: CallHash; - CallHashOf: CallHashOf; - CallIndex: CallIndex; - CallOrigin: CallOrigin; - CandidateCommitments: CandidateCommitments; - CandidateDescriptor: CandidateDescriptor; - CandidateEvent: CandidateEvent; - CandidateHash: CandidateHash; - CandidateInfo: CandidateInfo; - CandidatePendingAvailability: CandidatePendingAvailability; - CandidateReceipt: CandidateReceipt; - ChainId: ChainId; - ChainProperties: ChainProperties; - ChainType: ChainType; - ChangesTrieConfiguration: ChangesTrieConfiguration; - ChangesTrieSignal: ChangesTrieSignal; - CheckInherentsResult: CheckInherentsResult; - ClassDetails: ClassDetails; - ClassId: ClassId; - ClassMetadata: ClassMetadata; - CodecHash: CodecHash; - CodeHash: CodeHash; - CodeSource: CodeSource; - CodeUploadRequest: CodeUploadRequest; - CodeUploadResult: CodeUploadResult; - CodeUploadResultValue: CodeUploadResultValue; - CollationInfo: CollationInfo; - CollationInfoV1: CollationInfoV1; - CollatorId: CollatorId; - CollatorSignature: CollatorSignature; - CollectiveOrigin: CollectiveOrigin; - CommittedCandidateReceipt: CommittedCandidateReceipt; - CompactAssignments: CompactAssignments; - CompactAssignmentsTo257: CompactAssignmentsTo257; - CompactAssignmentsTo265: CompactAssignmentsTo265; - CompactAssignmentsWith16: CompactAssignmentsWith16; - CompactAssignmentsWith24: CompactAssignmentsWith24; - CompactScore: CompactScore; - CompactScoreCompact: CompactScoreCompact; - ConfigData: ConfigData; - Consensus: Consensus; - ConsensusEngineId: ConsensusEngineId; - ConsumedWeight: ConsumedWeight; - ContractCallFlags: ContractCallFlags; - ContractCallRequest: ContractCallRequest; - ContractConstructorSpecLatest: ContractConstructorSpecLatest; - ContractConstructorSpecV0: ContractConstructorSpecV0; - ContractConstructorSpecV1: ContractConstructorSpecV1; - ContractConstructorSpecV2: ContractConstructorSpecV2; - ContractConstructorSpecV3: ContractConstructorSpecV3; - ContractConstructorSpecV4: ContractConstructorSpecV4; - ContractContractSpecV0: ContractContractSpecV0; - ContractContractSpecV1: ContractContractSpecV1; - ContractContractSpecV2: ContractContractSpecV2; - ContractContractSpecV3: ContractContractSpecV3; - ContractContractSpecV4: ContractContractSpecV4; - ContractCryptoHasher: ContractCryptoHasher; - ContractDiscriminant: ContractDiscriminant; - ContractDisplayName: ContractDisplayName; - ContractEnvironmentV4: ContractEnvironmentV4; - ContractEventParamSpecLatest: ContractEventParamSpecLatest; - ContractEventParamSpecV0: ContractEventParamSpecV0; - ContractEventParamSpecV2: ContractEventParamSpecV2; - ContractEventSpecLatest: ContractEventSpecLatest; - ContractEventSpecV0: ContractEventSpecV0; - ContractEventSpecV1: ContractEventSpecV1; - ContractEventSpecV2: ContractEventSpecV2; - ContractExecResult: ContractExecResult; - ContractExecResultOk: ContractExecResultOk; - ContractExecResultResult: ContractExecResultResult; - ContractExecResultSuccessTo255: ContractExecResultSuccessTo255; - ContractExecResultSuccessTo260: ContractExecResultSuccessTo260; - ContractExecResultTo255: ContractExecResultTo255; - ContractExecResultTo260: ContractExecResultTo260; - ContractExecResultTo267: ContractExecResultTo267; - ContractExecResultU64: ContractExecResultU64; - ContractInfo: ContractInfo; - ContractInstantiateResult: ContractInstantiateResult; - ContractInstantiateResultTo267: ContractInstantiateResultTo267; - ContractInstantiateResultTo299: ContractInstantiateResultTo299; - ContractInstantiateResultU64: ContractInstantiateResultU64; - ContractLayoutArray: ContractLayoutArray; - ContractLayoutCell: ContractLayoutCell; - ContractLayoutEnum: ContractLayoutEnum; - ContractLayoutHash: ContractLayoutHash; - ContractLayoutHashingStrategy: ContractLayoutHashingStrategy; - ContractLayoutKey: ContractLayoutKey; - ContractLayoutStruct: ContractLayoutStruct; - ContractLayoutStructField: ContractLayoutStructField; - ContractMessageParamSpecLatest: ContractMessageParamSpecLatest; - ContractMessageParamSpecV0: ContractMessageParamSpecV0; - ContractMessageParamSpecV2: ContractMessageParamSpecV2; - ContractMessageSpecLatest: ContractMessageSpecLatest; - ContractMessageSpecV0: ContractMessageSpecV0; - ContractMessageSpecV1: ContractMessageSpecV1; - ContractMessageSpecV2: ContractMessageSpecV2; - ContractMessageSpecV3: ContractMessageSpecV3; - ContractMetadata: ContractMetadata; - ContractMetadataLatest: ContractMetadataLatest; - ContractMetadataV0: ContractMetadataV0; - ContractMetadataV1: ContractMetadataV1; - ContractMetadataV2: ContractMetadataV2; - ContractMetadataV3: ContractMetadataV3; - ContractMetadataV4: ContractMetadataV4; - ContractProject: ContractProject; - ContractProjectContract: ContractProjectContract; - ContractProjectInfo: ContractProjectInfo; - ContractProjectSource: ContractProjectSource; - ContractProjectV0: ContractProjectV0; - ContractReturnFlags: ContractReturnFlags; - ContractSelector: ContractSelector; - ContractStorageKey: ContractStorageKey; - ContractStorageLayout: ContractStorageLayout; - ContractTypeSpec: ContractTypeSpec; - Conviction: Conviction; - CoreAssignment: CoreAssignment; - CoreIndex: CoreIndex; - CoreOccupied: CoreOccupied; - CoreState: CoreState; - CrateVersion: CrateVersion; - CreatedBlock: CreatedBlock; - CustomMetadata15: CustomMetadata15; - CustomValueMetadata15: CustomValueMetadata15; - Data: Data; - DeferredOffenceOf: DeferredOffenceOf; - DefunctVoter: DefunctVoter; - DelayKind: DelayKind; - DelayKindBest: DelayKindBest; - Delegations: Delegations; - DeletedContract: DeletedContract; - DeliveredMessages: DeliveredMessages; - DepositBalance: DepositBalance; - DepositBalanceOf: DepositBalanceOf; - DestroyWitness: DestroyWitness; - Digest: Digest; - DigestItem: DigestItem; - DigestOf: DigestOf; - DispatchClass: DispatchClass; - DispatchError: DispatchError; - DispatchErrorModule: DispatchErrorModule; - DispatchErrorModulePre6: DispatchErrorModulePre6; - DispatchErrorModuleU8: DispatchErrorModuleU8; - DispatchErrorModuleU8a: DispatchErrorModuleU8a; - DispatchErrorPre6: DispatchErrorPre6; - DispatchErrorPre6First: DispatchErrorPre6First; - DispatchErrorTo198: DispatchErrorTo198; - DispatchFeePayment: DispatchFeePayment; - DispatchInfo: DispatchInfo; - DispatchInfoTo190: DispatchInfoTo190; - DispatchInfoTo244: DispatchInfoTo244; - DispatchOutcome: DispatchOutcome; - DispatchOutcomePre6: DispatchOutcomePre6; - DispatchResult: DispatchResult; - DispatchResultOf: DispatchResultOf; - DispatchResultTo198: DispatchResultTo198; - DisputeLocation: DisputeLocation; - DisputeProof: DisputeProof; - DisputeResult: DisputeResult; - DisputeState: DisputeState; - DisputeStatement: DisputeStatement; - DisputeStatementSet: DisputeStatementSet; - DisputesTimeSlot: DisputesTimeSlot; - DoubleEncodedCall: DoubleEncodedCall; - DoubleVoteReport: DoubleVoteReport; - DownwardMessage: DownwardMessage; - EcdsaSignature: EcdsaSignature; - Ed25519Signature: Ed25519Signature; - EIP1559Transaction: EIP1559Transaction; - EIP2930Transaction: EIP2930Transaction; - ElectionCompute: ElectionCompute; - ElectionPhase: ElectionPhase; - ElectionResult: ElectionResult; - ElectionScore: ElectionScore; - ElectionSize: ElectionSize; - ElectionStatus: ElectionStatus; - EncodedFinalityProofs: EncodedFinalityProofs; - EncodedJustification: EncodedJustification; - Epoch: Epoch; - EpochAuthorship: EpochAuthorship; - Era: Era; - EraIndex: EraIndex; - EraPoints: EraPoints; - EraRewardPoints: EraRewardPoints; - EraRewards: EraRewards; - ErrorMetadataLatest: ErrorMetadataLatest; - ErrorMetadataV10: ErrorMetadataV10; - ErrorMetadataV11: ErrorMetadataV11; - ErrorMetadataV12: ErrorMetadataV12; - ErrorMetadataV13: ErrorMetadataV13; - ErrorMetadataV14: ErrorMetadataV14; - ErrorMetadataV9: ErrorMetadataV9; - EthAccessList: EthAccessList; - EthAccessListItem: EthAccessListItem; - EthAccount: EthAccount; - EthAddress: EthAddress; - EthBlock: EthBlock; - EthBloom: EthBloom; - EthCallRequest: EthCallRequest; - EthereumAccountId: EthereumAccountId; - EthereumAddress: EthereumAddress; - EthereumLookupSource: EthereumLookupSource; - EthereumSignature: EthereumSignature; - EthFeeHistory: EthFeeHistory; - EthFilter: EthFilter; - EthFilterAddress: EthFilterAddress; - EthFilterChanges: EthFilterChanges; - EthFilterTopic: EthFilterTopic; - EthFilterTopicEntry: EthFilterTopicEntry; - EthFilterTopicInner: EthFilterTopicInner; - EthHeader: EthHeader; - EthLog: EthLog; - EthReceipt: EthReceipt; - EthReceiptV0: EthReceiptV0; - EthReceiptV3: EthReceiptV3; - EthRichBlock: EthRichBlock; - EthRichHeader: EthRichHeader; - EthStorageProof: EthStorageProof; - EthSubKind: EthSubKind; - EthSubParams: EthSubParams; - EthSubResult: EthSubResult; - EthSyncInfo: EthSyncInfo; - EthSyncStatus: EthSyncStatus; - EthTransaction: EthTransaction; - EthTransactionAction: EthTransactionAction; - EthTransactionCondition: EthTransactionCondition; - EthTransactionRequest: EthTransactionRequest; - EthTransactionSignature: EthTransactionSignature; - EthTransactionStatus: EthTransactionStatus; - EthWork: EthWork; - Event: Event; - EventId: EventId; - EventIndex: EventIndex; - EventMetadataLatest: EventMetadataLatest; - EventMetadataV10: EventMetadataV10; - EventMetadataV11: EventMetadataV11; - EventMetadataV12: EventMetadataV12; - EventMetadataV13: EventMetadataV13; - EventMetadataV14: EventMetadataV14; - EventMetadataV9: EventMetadataV9; - EventRecord: EventRecord; - EvmAccount: EvmAccount; - EvmCallInfo: EvmCallInfo; - EvmCallInfoV2: EvmCallInfoV2; - EvmCreateInfo: EvmCreateInfo; - EvmCreateInfoV2: EvmCreateInfoV2; - EvmLog: EvmLog; - EvmVicinity: EvmVicinity; - EvmWeightInfo: EvmWeightInfo; - ExecReturnValue: ExecReturnValue; - ExecutorParam: ExecutorParam; - ExecutorParams: ExecutorParams; - ExecutorParamsHash: ExecutorParamsHash; - ExitError: ExitError; - ExitFatal: ExitFatal; - ExitReason: ExitReason; - ExitRevert: ExitRevert; - ExitSucceed: ExitSucceed; - ExplicitDisputeStatement: ExplicitDisputeStatement; - Exposure: Exposure; - ExtendedBalance: ExtendedBalance; - Extrinsic: Extrinsic; - ExtrinsicEra: ExtrinsicEra; - ExtrinsicMetadataLatest: ExtrinsicMetadataLatest; - ExtrinsicMetadataV11: ExtrinsicMetadataV11; - ExtrinsicMetadataV12: ExtrinsicMetadataV12; - ExtrinsicMetadataV13: ExtrinsicMetadataV13; - ExtrinsicMetadataV14: ExtrinsicMetadataV14; - ExtrinsicMetadataV15: ExtrinsicMetadataV15; - ExtrinsicOrHash: ExtrinsicOrHash; - ExtrinsicPayload: ExtrinsicPayload; - ExtrinsicPayloadUnknown: ExtrinsicPayloadUnknown; - ExtrinsicPayloadV4: ExtrinsicPayloadV4; - ExtrinsicSignature: ExtrinsicSignature; - ExtrinsicSignatureV4: ExtrinsicSignatureV4; - ExtrinsicStatus: ExtrinsicStatus; - ExtrinsicsWeight: ExtrinsicsWeight; - ExtrinsicUnknown: ExtrinsicUnknown; - ExtrinsicV4: ExtrinsicV4; - f32: f32; - F32: F32; - f64: f64; - F64: F64; - FeeDetails: FeeDetails; - Fixed128: Fixed128; - Fixed64: Fixed64; - FixedI128: FixedI128; - FixedI64: FixedI64; - FixedU128: FixedU128; - FixedU64: FixedU64; - Forcing: Forcing; - ForkTreePendingChange: ForkTreePendingChange; - ForkTreePendingChangeNode: ForkTreePendingChangeNode; - FullIdentification: FullIdentification; - FunctionArgumentMetadataLatest: FunctionArgumentMetadataLatest; - FunctionArgumentMetadataV10: FunctionArgumentMetadataV10; - FunctionArgumentMetadataV11: FunctionArgumentMetadataV11; - FunctionArgumentMetadataV12: FunctionArgumentMetadataV12; - FunctionArgumentMetadataV13: FunctionArgumentMetadataV13; - FunctionArgumentMetadataV14: FunctionArgumentMetadataV14; - FunctionArgumentMetadataV9: FunctionArgumentMetadataV9; - FunctionMetadataLatest: FunctionMetadataLatest; - FunctionMetadataV10: FunctionMetadataV10; - FunctionMetadataV11: FunctionMetadataV11; - FunctionMetadataV12: FunctionMetadataV12; - FunctionMetadataV13: FunctionMetadataV13; - FunctionMetadataV14: FunctionMetadataV14; - FunctionMetadataV9: FunctionMetadataV9; - FundIndex: FundIndex; - FundInfo: FundInfo; - Fungibility: Fungibility; - FungibilityV0: FungibilityV0; - FungibilityV1: FungibilityV1; - FungibilityV2: FungibilityV2; - FungiblesAccessError: FungiblesAccessError; - Gas: Gas; - GiltBid: GiltBid; - GlobalValidationData: GlobalValidationData; - GlobalValidationSchedule: GlobalValidationSchedule; - GrandpaCommit: GrandpaCommit; - GrandpaEquivocation: GrandpaEquivocation; - GrandpaEquivocationProof: GrandpaEquivocationProof; - GrandpaEquivocationValue: GrandpaEquivocationValue; - GrandpaJustification: GrandpaJustification; - GrandpaPrecommit: GrandpaPrecommit; - GrandpaPrevote: GrandpaPrevote; - GrandpaSignedPrecommit: GrandpaSignedPrecommit; - GroupIndex: GroupIndex; - GroupRotationInfo: GroupRotationInfo; - H1024: H1024; - H128: H128; - H160: H160; - H2048: H2048; - H256: H256; - H32: H32; - H512: H512; - H64: H64; - Hash: Hash; - HeadData: HeadData; - Header: Header; - HeaderPartial: HeaderPartial; - Health: Health; - Heartbeat: Heartbeat; - HeartbeatTo244: HeartbeatTo244; - HostConfiguration: HostConfiguration; - HostFnWeights: HostFnWeights; - HostFnWeightsTo264: HostFnWeightsTo264; - HrmpChannel: HrmpChannel; - HrmpChannelId: HrmpChannelId; - HrmpOpenChannelRequest: HrmpOpenChannelRequest; - i128: i128; - I128: I128; - i16: i16; - I16: I16; - i256: i256; - I256: I256; - i32: i32; - I32: I32; - I32F32: I32F32; - i64: i64; - I64: I64; - i8: i8; - I8: I8; - IdentificationTuple: IdentificationTuple; - IdentityFields: IdentityFields; - IdentityInfo: IdentityInfo; - IdentityInfoAdditional: IdentityInfoAdditional; - IdentityInfoTo198: IdentityInfoTo198; - IdentityJudgement: IdentityJudgement; - ImmortalEra: ImmortalEra; - ImportedAux: ImportedAux; - InboundDownwardMessage: InboundDownwardMessage; - InboundHrmpMessage: InboundHrmpMessage; - InboundHrmpMessages: InboundHrmpMessages; - InboundLaneData: InboundLaneData; - InboundRelayer: InboundRelayer; - InboundStatus: InboundStatus; - IncludedBlocks: IncludedBlocks; - InclusionFee: InclusionFee; - IncomingParachain: IncomingParachain; - IncomingParachainDeploy: IncomingParachainDeploy; - IncomingParachainFixed: IncomingParachainFixed; - Index: Index; - IndicesLookupSource: IndicesLookupSource; - IndividualExposure: IndividualExposure; - InherentData: InherentData; - InherentIdentifier: InherentIdentifier; - InitializationData: InitializationData; - InstanceDetails: InstanceDetails; - InstanceId: InstanceId; - InstanceMetadata: InstanceMetadata; - InstantiateRequest: InstantiateRequest; - InstantiateRequestV1: InstantiateRequestV1; - InstantiateRequestV2: InstantiateRequestV2; - InstantiateReturnValue: InstantiateReturnValue; - InstantiateReturnValueOk: InstantiateReturnValueOk; - InstantiateReturnValueTo267: InstantiateReturnValueTo267; - InstructionV2: InstructionV2; - InstructionWeights: InstructionWeights; - InteriorMultiLocation: InteriorMultiLocation; - InvalidDisputeStatementKind: InvalidDisputeStatementKind; - InvalidTransaction: InvalidTransaction; - isize: isize; - ISize: ISize; - Json: Json; - Junction: Junction; - Junctions: Junctions; - JunctionsV1: JunctionsV1; - JunctionsV2: JunctionsV2; - JunctionV0: JunctionV0; - JunctionV1: JunctionV1; - JunctionV2: JunctionV2; - Justification: Justification; - JustificationNotification: JustificationNotification; - Justifications: Justifications; - Key: Key; - KeyOwnerProof: KeyOwnerProof; - Keys: Keys; - KeyType: KeyType; - KeyTypeId: KeyTypeId; - KeyValue: KeyValue; - KeyValueOption: KeyValueOption; - Kind: Kind; - LaneId: LaneId; - LastContribution: LastContribution; - LastRuntimeUpgradeInfo: LastRuntimeUpgradeInfo; - LeasePeriod: LeasePeriod; - LeasePeriodOf: LeasePeriodOf; - LegacyTransaction: LegacyTransaction; - Limits: Limits; - LimitsTo264: LimitsTo264; - LocalValidationData: LocalValidationData; - LockIdentifier: LockIdentifier; - LookupSource: LookupSource; - LookupTarget: LookupTarget; - LotteryConfig: LotteryConfig; - MaybeRandomness: MaybeRandomness; - MaybeVrf: MaybeVrf; - MemberCount: MemberCount; - MembershipProof: MembershipProof; - MessageData: MessageData; - MessageId: MessageId; - MessageIngestionType: MessageIngestionType; - MessageKey: MessageKey; - MessageNonce: MessageNonce; - MessageQueueChain: MessageQueueChain; - MessagesDeliveryProofOf: MessagesDeliveryProofOf; - MessagesProofOf: MessagesProofOf; - MessagingStateSnapshot: MessagingStateSnapshot; - MessagingStateSnapshotEgressEntry: MessagingStateSnapshotEgressEntry; - MetadataAll: MetadataAll; - MetadataLatest: MetadataLatest; - MetadataV10: MetadataV10; - MetadataV11: MetadataV11; - MetadataV12: MetadataV12; - MetadataV13: MetadataV13; - MetadataV14: MetadataV14; - MetadataV15: MetadataV15; - MetadataV9: MetadataV9; - MigrationStatusResult: MigrationStatusResult; - MmrBatchProof: MmrBatchProof; - MmrEncodableOpaqueLeaf: MmrEncodableOpaqueLeaf; - MmrError: MmrError; - MmrHash: MmrHash; - MmrLeafBatchProof: MmrLeafBatchProof; - MmrLeafIndex: MmrLeafIndex; - MmrLeafProof: MmrLeafProof; - MmrNodeIndex: MmrNodeIndex; - MmrProof: MmrProof; - MmrRootHash: MmrRootHash; - ModuleConstantMetadataV10: ModuleConstantMetadataV10; - ModuleConstantMetadataV11: ModuleConstantMetadataV11; - ModuleConstantMetadataV12: ModuleConstantMetadataV12; - ModuleConstantMetadataV13: ModuleConstantMetadataV13; - ModuleConstantMetadataV9: ModuleConstantMetadataV9; - ModuleId: ModuleId; - ModuleMetadataV10: ModuleMetadataV10; - ModuleMetadataV11: ModuleMetadataV11; - ModuleMetadataV12: ModuleMetadataV12; - ModuleMetadataV13: ModuleMetadataV13; - ModuleMetadataV9: ModuleMetadataV9; - Moment: Moment; - MomentOf: MomentOf; - MoreAttestations: MoreAttestations; - MortalEra: MortalEra; - MultiAddress: MultiAddress; - MultiAsset: MultiAsset; - MultiAssetFilter: MultiAssetFilter; - MultiAssetFilterV1: MultiAssetFilterV1; - MultiAssetFilterV2: MultiAssetFilterV2; - MultiAssets: MultiAssets; - MultiAssetsV1: MultiAssetsV1; - MultiAssetsV2: MultiAssetsV2; - MultiAssetV0: MultiAssetV0; - MultiAssetV1: MultiAssetV1; - MultiAssetV2: MultiAssetV2; - MultiDisputeStatementSet: MultiDisputeStatementSet; - MultiLocation: MultiLocation; - MultiLocationV0: MultiLocationV0; - MultiLocationV1: MultiLocationV1; - MultiLocationV2: MultiLocationV2; - Multiplier: Multiplier; - Multisig: Multisig; - MultiSignature: MultiSignature; - MultiSigner: MultiSigner; - NetworkId: NetworkId; - NetworkState: NetworkState; - NetworkStatePeerset: NetworkStatePeerset; - NetworkStatePeersetInfo: NetworkStatePeersetInfo; - NewBidder: NewBidder; - NextAuthority: NextAuthority; - NextConfigDescriptor: NextConfigDescriptor; - NextConfigDescriptorV1: NextConfigDescriptorV1; - NftCollectionId: NftCollectionId; - NftItemId: NftItemId; - NodeRole: NodeRole; - Nominations: Nominations; - NominatorIndex: NominatorIndex; - NominatorIndexCompact: NominatorIndexCompact; - NotConnectedPeer: NotConnectedPeer; - NpApiError: NpApiError; - NpPoolId: NpPoolId; - Null: Null; - OccupiedCore: OccupiedCore; - OccupiedCoreAssumption: OccupiedCoreAssumption; - OffchainAccuracy: OffchainAccuracy; - OffchainAccuracyCompact: OffchainAccuracyCompact; - OffenceDetails: OffenceDetails; - Offender: Offender; - OldV1SessionInfo: OldV1SessionInfo; - OpaqueCall: OpaqueCall; - OpaqueKeyOwnershipProof: OpaqueKeyOwnershipProof; - OpaqueMetadata: OpaqueMetadata; - OpaqueMultiaddr: OpaqueMultiaddr; - OpaqueNetworkState: OpaqueNetworkState; - OpaquePeerId: OpaquePeerId; - OpaqueTimeSlot: OpaqueTimeSlot; - OpenTip: OpenTip; - OpenTipFinderTo225: OpenTipFinderTo225; - OpenTipTip: OpenTipTip; - OpenTipTo225: OpenTipTo225; - OperatingMode: OperatingMode; - OptionBool: OptionBool; - Origin: Origin; - OriginCaller: OriginCaller; - OriginKindV0: OriginKindV0; - OriginKindV1: OriginKindV1; - OriginKindV2: OriginKindV2; - OutboundHrmpMessage: OutboundHrmpMessage; - OutboundLaneData: OutboundLaneData; - OutboundMessageFee: OutboundMessageFee; - OutboundPayload: OutboundPayload; - OutboundStatus: OutboundStatus; - Outcome: Outcome; - OuterEnums15: OuterEnums15; - OverweightIndex: OverweightIndex; - Owner: Owner; - PageCounter: PageCounter; - PageIndexData: PageIndexData; - PalletCallMetadataLatest: PalletCallMetadataLatest; - PalletCallMetadataV14: PalletCallMetadataV14; - PalletConstantMetadataLatest: PalletConstantMetadataLatest; - PalletConstantMetadataV14: PalletConstantMetadataV14; - PalletErrorMetadataLatest: PalletErrorMetadataLatest; - PalletErrorMetadataV14: PalletErrorMetadataV14; - PalletEventMetadataLatest: PalletEventMetadataLatest; - PalletEventMetadataV14: PalletEventMetadataV14; - PalletId: PalletId; - PalletMetadataLatest: PalletMetadataLatest; - PalletMetadataV14: PalletMetadataV14; - PalletMetadataV15: PalletMetadataV15; - PalletsOrigin: PalletsOrigin; - PalletStorageMetadataLatest: PalletStorageMetadataLatest; - PalletStorageMetadataV14: PalletStorageMetadataV14; - PalletVersion: PalletVersion; - ParachainDispatchOrigin: ParachainDispatchOrigin; - ParachainInherentData: ParachainInherentData; - ParachainProposal: ParachainProposal; - ParachainsInherentData: ParachainsInherentData; - ParaGenesisArgs: ParaGenesisArgs; - ParaId: ParaId; - ParaInfo: ParaInfo; - ParaLifecycle: ParaLifecycle; - Parameter: Parameter; - ParaPastCodeMeta: ParaPastCodeMeta; - ParaScheduling: ParaScheduling; - ParathreadClaim: ParathreadClaim; - ParathreadClaimQueue: ParathreadClaimQueue; - ParathreadEntry: ParathreadEntry; - ParaValidatorIndex: ParaValidatorIndex; - Pays: Pays; - Peer: Peer; - PeerEndpoint: PeerEndpoint; - PeerEndpointAddr: PeerEndpointAddr; - PeerInfo: PeerInfo; - PeerPing: PeerPing; - PendingChange: PendingChange; - PendingPause: PendingPause; - PendingResume: PendingResume; - PendingSlashes: PendingSlashes; - Perbill: Perbill; - Percent: Percent; - PerDispatchClassU32: PerDispatchClassU32; - PerDispatchClassWeight: PerDispatchClassWeight; - PerDispatchClassWeightsPerClass: PerDispatchClassWeightsPerClass; - Period: Period; - Permill: Permill; - PermissionLatest: PermissionLatest; - PermissionsV1: PermissionsV1; - PermissionVersions: PermissionVersions; - Perquintill: Perquintill; - PersistedValidationData: PersistedValidationData; - PerU16: PerU16; - Phantom: Phantom; - PhantomData: PhantomData; - Phase: Phase; - PhragmenScore: PhragmenScore; - Points: Points; - PortableType: PortableType; - PortableTypeV14: PortableTypeV14; - Precommits: Precommits; - PrefabWasmModule: PrefabWasmModule; - PrefixedStorageKey: PrefixedStorageKey; - PreimageStatus: PreimageStatus; - PreimageStatusAvailable: PreimageStatusAvailable; - PreRuntime: PreRuntime; - Prevotes: Prevotes; - Priority: Priority; - PriorLock: PriorLock; - PropIndex: PropIndex; - Proposal: Proposal; - ProposalIndex: ProposalIndex; - ProxyAnnouncement: ProxyAnnouncement; - ProxyDefinition: ProxyDefinition; - ProxyState: ProxyState; - ProxyType: ProxyType; - PvfCheckStatement: PvfCheckStatement; - PvfExecTimeoutKind: PvfExecTimeoutKind; - PvfPrepTimeoutKind: PvfPrepTimeoutKind; - QueryId: QueryId; - QueryStatus: QueryStatus; - QueueConfigData: QueueConfigData; - QueuedParathread: QueuedParathread; - Randomness: Randomness; - Raw: Raw; - RawAuraPreDigest: RawAuraPreDigest; - RawBabePreDigest: RawBabePreDigest; - RawBabePreDigestCompat: RawBabePreDigestCompat; - RawBabePreDigestPrimary: RawBabePreDigestPrimary; - RawBabePreDigestPrimaryTo159: RawBabePreDigestPrimaryTo159; - RawBabePreDigestSecondaryPlain: RawBabePreDigestSecondaryPlain; - RawBabePreDigestSecondaryTo159: RawBabePreDigestSecondaryTo159; - RawBabePreDigestSecondaryVRF: RawBabePreDigestSecondaryVRF; - RawBabePreDigestTo159: RawBabePreDigestTo159; - RawOrigin: RawOrigin; - RawSolution: RawSolution; - RawSolutionTo265: RawSolutionTo265; - RawSolutionWith16: RawSolutionWith16; - RawSolutionWith24: RawSolutionWith24; - RawVRFOutput: RawVRFOutput; - ReadProof: ReadProof; - ReadySolution: ReadySolution; - Reasons: Reasons; - RecoveryConfig: RecoveryConfig; - RefCount: RefCount; - RefCountTo259: RefCountTo259; - ReferendumIndex: ReferendumIndex; - ReferendumInfo: ReferendumInfo; - ReferendumInfoFinished: ReferendumInfoFinished; - ReferendumInfoTo239: ReferendumInfoTo239; - ReferendumStatus: ReferendumStatus; - RegisteredParachainInfo: RegisteredParachainInfo; - RegistrarIndex: RegistrarIndex; - RegistrarInfo: RegistrarInfo; - Registration: Registration; - RegistrationJudgement: RegistrationJudgement; - RegistrationTo198: RegistrationTo198; - RelayBlockNumber: RelayBlockNumber; - RelayChainBlockNumber: RelayChainBlockNumber; - RelayChainHash: RelayChainHash; - RelayerId: RelayerId; - RelayHash: RelayHash; - Releases: Releases; - Remark: Remark; - Renouncing: Renouncing; - RentProjection: RentProjection; - ReplacementTimes: ReplacementTimes; - ReportedRoundStates: ReportedRoundStates; - Reporter: Reporter; - ReportIdOf: ReportIdOf; - ReserveData: ReserveData; - ReserveIdentifier: ReserveIdentifier; - Response: Response; - ResponseV0: ResponseV0; - ResponseV1: ResponseV1; - ResponseV2: ResponseV2; - ResponseV2Error: ResponseV2Error; - ResponseV2Result: ResponseV2Result; - Retriable: Retriable; - RewardDestination: RewardDestination; - RewardPoint: RewardPoint; - RoundSnapshot: RoundSnapshot; - RoundState: RoundState; - RpcMethods: RpcMethods; - RuntimeApiMetadataLatest: RuntimeApiMetadataLatest; - RuntimeApiMetadataV15: RuntimeApiMetadataV15; - RuntimeApiMethodMetadataV15: RuntimeApiMethodMetadataV15; - RuntimeApiMethodParamMetadataV15: RuntimeApiMethodParamMetadataV15; - RuntimeCall: RuntimeCall; - RuntimeDbWeight: RuntimeDbWeight; - RuntimeDispatchInfo: RuntimeDispatchInfo; - RuntimeDispatchInfoV1: RuntimeDispatchInfoV1; - RuntimeDispatchInfoV2: RuntimeDispatchInfoV2; - RuntimeEvent: RuntimeEvent; - RuntimeVersion: RuntimeVersion; - RuntimeVersionApi: RuntimeVersionApi; - RuntimeVersionPartial: RuntimeVersionPartial; - RuntimeVersionPre3: RuntimeVersionPre3; - RuntimeVersionPre4: RuntimeVersionPre4; - Schedule: Schedule; - Scheduled: Scheduled; - ScheduledCore: ScheduledCore; - ScheduledTo254: ScheduledTo254; - SchedulePeriod: SchedulePeriod; - SchedulePriority: SchedulePriority; - ScheduleTo212: ScheduleTo212; - ScheduleTo258: ScheduleTo258; - ScheduleTo264: ScheduleTo264; - Scheduling: Scheduling; - ScrapedOnChainVotes: ScrapedOnChainVotes; - Seal: Seal; - SealV0: SealV0; - SeatHolder: SeatHolder; - SeedOf: SeedOf; - ServiceQuality: ServiceQuality; - SessionIndex: SessionIndex; - SessionInfo: SessionInfo; - SessionInfoValidatorGroup: SessionInfoValidatorGroup; - SessionKeys1: SessionKeys1; - SessionKeys10: SessionKeys10; - SessionKeys10B: SessionKeys10B; - SessionKeys2: SessionKeys2; - SessionKeys3: SessionKeys3; - SessionKeys4: SessionKeys4; - SessionKeys5: SessionKeys5; - SessionKeys6: SessionKeys6; - SessionKeys6B: SessionKeys6B; - SessionKeys7: SessionKeys7; - SessionKeys7B: SessionKeys7B; - SessionKeys8: SessionKeys8; - SessionKeys8B: SessionKeys8B; - SessionKeys9: SessionKeys9; - SessionKeys9B: SessionKeys9B; - SetId: SetId; - SetIndex: SetIndex; - Si0Field: Si0Field; - Si0LookupTypeId: Si0LookupTypeId; - Si0Path: Si0Path; - Si0Type: Si0Type; - Si0TypeDef: Si0TypeDef; - Si0TypeDefArray: Si0TypeDefArray; - Si0TypeDefBitSequence: Si0TypeDefBitSequence; - Si0TypeDefCompact: Si0TypeDefCompact; - Si0TypeDefComposite: Si0TypeDefComposite; - Si0TypeDefPhantom: Si0TypeDefPhantom; - Si0TypeDefPrimitive: Si0TypeDefPrimitive; - Si0TypeDefSequence: Si0TypeDefSequence; - Si0TypeDefTuple: Si0TypeDefTuple; - Si0TypeDefVariant: Si0TypeDefVariant; - Si0TypeParameter: Si0TypeParameter; - Si0Variant: Si0Variant; - Si1Field: Si1Field; - Si1LookupTypeId: Si1LookupTypeId; - Si1Path: Si1Path; - Si1Type: Si1Type; - Si1TypeDef: Si1TypeDef; - Si1TypeDefArray: Si1TypeDefArray; - Si1TypeDefBitSequence: Si1TypeDefBitSequence; - Si1TypeDefCompact: Si1TypeDefCompact; - Si1TypeDefComposite: Si1TypeDefComposite; - Si1TypeDefPrimitive: Si1TypeDefPrimitive; - Si1TypeDefSequence: Si1TypeDefSequence; - Si1TypeDefTuple: Si1TypeDefTuple; - Si1TypeDefVariant: Si1TypeDefVariant; - Si1TypeParameter: Si1TypeParameter; - Si1Variant: Si1Variant; - SiField: SiField; - Signature: Signature; - SignedAvailabilityBitfield: SignedAvailabilityBitfield; - SignedAvailabilityBitfields: SignedAvailabilityBitfields; - SignedBlock: SignedBlock; - SignedBlockWithJustification: SignedBlockWithJustification; - SignedBlockWithJustifications: SignedBlockWithJustifications; - SignedExtensionMetadataLatest: SignedExtensionMetadataLatest; - SignedExtensionMetadataV14: SignedExtensionMetadataV14; - SignedSubmission: SignedSubmission; - SignedSubmissionOf: SignedSubmissionOf; - SignedSubmissionTo276: SignedSubmissionTo276; - SignerPayload: SignerPayload; - SigningContext: SigningContext; - SiLookupTypeId: SiLookupTypeId; - SiPath: SiPath; - SiType: SiType; - SiTypeDef: SiTypeDef; - SiTypeDefArray: SiTypeDefArray; - SiTypeDefBitSequence: SiTypeDefBitSequence; - SiTypeDefCompact: SiTypeDefCompact; - SiTypeDefComposite: SiTypeDefComposite; - SiTypeDefPrimitive: SiTypeDefPrimitive; - SiTypeDefSequence: SiTypeDefSequence; - SiTypeDefTuple: SiTypeDefTuple; - SiTypeDefVariant: SiTypeDefVariant; - SiTypeParameter: SiTypeParameter; - SiVariant: SiVariant; - SlashingOffenceKind: SlashingOffenceKind; - SlashingSpans: SlashingSpans; - SlashingSpansTo204: SlashingSpansTo204; - SlashJournalEntry: SlashJournalEntry; - Slot: Slot; - SlotDuration: SlotDuration; - SlotNumber: SlotNumber; - SlotRange: SlotRange; - SlotRange10: SlotRange10; - SocietyJudgement: SocietyJudgement; - SocietyVote: SocietyVote; - SolutionOrSnapshotSize: SolutionOrSnapshotSize; - SolutionSupport: SolutionSupport; - SolutionSupports: SolutionSupports; - SpanIndex: SpanIndex; - SpanRecord: SpanRecord; - SpecVersion: SpecVersion; - Sr25519Signature: Sr25519Signature; - StakingLedger: StakingLedger; - StakingLedgerTo223: StakingLedgerTo223; - StakingLedgerTo240: StakingLedgerTo240; - Statement: Statement; - StatementKind: StatementKind; - StorageChangeSet: StorageChangeSet; - StorageData: StorageData; - StorageDeposit: StorageDeposit; - StorageEntryMetadataLatest: StorageEntryMetadataLatest; - StorageEntryMetadataV10: StorageEntryMetadataV10; - StorageEntryMetadataV11: StorageEntryMetadataV11; - StorageEntryMetadataV12: StorageEntryMetadataV12; - StorageEntryMetadataV13: StorageEntryMetadataV13; - StorageEntryMetadataV14: StorageEntryMetadataV14; - StorageEntryMetadataV9: StorageEntryMetadataV9; - StorageEntryModifierLatest: StorageEntryModifierLatest; - StorageEntryModifierV10: StorageEntryModifierV10; - StorageEntryModifierV11: StorageEntryModifierV11; - StorageEntryModifierV12: StorageEntryModifierV12; - StorageEntryModifierV13: StorageEntryModifierV13; - StorageEntryModifierV14: StorageEntryModifierV14; - StorageEntryModifierV9: StorageEntryModifierV9; - StorageEntryTypeLatest: StorageEntryTypeLatest; - StorageEntryTypeV10: StorageEntryTypeV10; - StorageEntryTypeV11: StorageEntryTypeV11; - StorageEntryTypeV12: StorageEntryTypeV12; - StorageEntryTypeV13: StorageEntryTypeV13; - StorageEntryTypeV14: StorageEntryTypeV14; - StorageEntryTypeV9: StorageEntryTypeV9; - StorageHasher: StorageHasher; - StorageHasherV10: StorageHasherV10; - StorageHasherV11: StorageHasherV11; - StorageHasherV12: StorageHasherV12; - StorageHasherV13: StorageHasherV13; - StorageHasherV14: StorageHasherV14; - StorageHasherV9: StorageHasherV9; - StorageInfo: StorageInfo; - StorageKey: StorageKey; - StorageKind: StorageKind; - StorageMetadataV10: StorageMetadataV10; - StorageMetadataV11: StorageMetadataV11; - StorageMetadataV12: StorageMetadataV12; - StorageMetadataV13: StorageMetadataV13; - StorageMetadataV9: StorageMetadataV9; - StorageProof: StorageProof; - StoredPendingChange: StoredPendingChange; - StoredState: StoredState; - StrikeCount: StrikeCount; - SubId: SubId; - SubmissionIndicesOf: SubmissionIndicesOf; - Supports: Supports; - SyncState: SyncState; - SystemInherentData: SystemInherentData; - SystemOrigin: SystemOrigin; - Tally: Tally; - TaskAddress: TaskAddress; - TAssetBalance: TAssetBalance; - TAssetDepositBalance: TAssetDepositBalance; - Text: Text; - Timepoint: Timepoint; - TokenError: TokenError; - TombstoneContractInfo: TombstoneContractInfo; - TraceBlockResponse: TraceBlockResponse; - TraceError: TraceError; - TransactionalError: TransactionalError; - TransactionInfo: TransactionInfo; - TransactionLongevity: TransactionLongevity; - TransactionPriority: TransactionPriority; - TransactionSource: TransactionSource; - TransactionStorageProof: TransactionStorageProof; - TransactionTag: TransactionTag; - TransactionV0: TransactionV0; - TransactionV1: TransactionV1; - TransactionV2: TransactionV2; - TransactionValidity: TransactionValidity; - TransactionValidityError: TransactionValidityError; - TransientValidationData: TransientValidationData; - TreasuryProposal: TreasuryProposal; - TrieId: TrieId; - TrieIndex: TrieIndex; - Type: Type; - u128: u128; - U128: U128; - u16: u16; - U16: U16; - u256: u256; - U256: U256; - u32: u32; - U32: U32; - U32F32: U32F32; - u64: u64; - U64: U64; - u8: u8; - U8: U8; - UnappliedSlash: UnappliedSlash; - UnappliedSlashOther: UnappliedSlashOther; - UncleEntryItem: UncleEntryItem; - UnknownTransaction: UnknownTransaction; - UnlockChunk: UnlockChunk; - UnrewardedRelayer: UnrewardedRelayer; - UnrewardedRelayersState: UnrewardedRelayersState; - UpgradeGoAhead: UpgradeGoAhead; - UpgradeRestriction: UpgradeRestriction; - UpwardMessage: UpwardMessage; - usize: usize; - USize: USize; - ValidationCode: ValidationCode; - ValidationCodeHash: ValidationCodeHash; - ValidationData: ValidationData; - ValidationDataType: ValidationDataType; - ValidationFunctionParams: ValidationFunctionParams; - ValidatorCount: ValidatorCount; - ValidatorId: ValidatorId; - ValidatorIdOf: ValidatorIdOf; - ValidatorIndex: ValidatorIndex; - ValidatorIndexCompact: ValidatorIndexCompact; - ValidatorPrefs: ValidatorPrefs; - ValidatorPrefsTo145: ValidatorPrefsTo145; - ValidatorPrefsTo196: ValidatorPrefsTo196; - ValidatorPrefsWithBlocked: ValidatorPrefsWithBlocked; - ValidatorPrefsWithCommission: ValidatorPrefsWithCommission; - ValidatorSet: ValidatorSet; - ValidatorSetId: ValidatorSetId; - ValidatorSignature: ValidatorSignature; - ValidDisputeStatementKind: ValidDisputeStatementKind; - ValidityAttestation: ValidityAttestation; - ValidTransaction: ValidTransaction; - VecInboundHrmpMessage: VecInboundHrmpMessage; - VersionedMultiAsset: VersionedMultiAsset; - VersionedMultiAssets: VersionedMultiAssets; - VersionedMultiLocation: VersionedMultiLocation; - VersionedResponse: VersionedResponse; - VersionedXcm: VersionedXcm; - VersionMigrationStage: VersionMigrationStage; - VestingInfo: VestingInfo; - VestingSchedule: VestingSchedule; - Vote: Vote; - VoteIndex: VoteIndex; - Voter: Voter; - VoterInfo: VoterInfo; - Votes: Votes; - VotesTo230: VotesTo230; - VoteThreshold: VoteThreshold; - VoteWeight: VoteWeight; - Voting: Voting; - VotingDelegating: VotingDelegating; - VotingDirect: VotingDirect; - VotingDirectVote: VotingDirectVote; - VouchingStatus: VouchingStatus; - VrfData: VrfData; - VrfOutput: VrfOutput; - VrfProof: VrfProof; - Weight: Weight; - WeightLimitV2: WeightLimitV2; - WeightMultiplier: WeightMultiplier; - WeightPerClass: WeightPerClass; - WeightToFeeCoefficient: WeightToFeeCoefficient; - WeightV0: WeightV0; - WeightV1: WeightV1; - WeightV2: WeightV2; - WildFungibility: WildFungibility; - WildFungibilityV0: WildFungibilityV0; - WildFungibilityV1: WildFungibilityV1; - WildFungibilityV2: WildFungibilityV2; - WildMultiAsset: WildMultiAsset; - WildMultiAssetV1: WildMultiAssetV1; - WildMultiAssetV2: WildMultiAssetV2; - WinnersData: WinnersData; - WinnersData10: WinnersData10; - WinnersDataTuple: WinnersDataTuple; - WinnersDataTuple10: WinnersDataTuple10; - WinningData: WinningData; - WinningData10: WinningData10; - WinningDataEntry: WinningDataEntry; - WithdrawReasons: WithdrawReasons; - Xcm: Xcm; - XcmAssetId: XcmAssetId; - XcmError: XcmError; - XcmErrorV0: XcmErrorV0; - XcmErrorV1: XcmErrorV1; - XcmErrorV2: XcmErrorV2; - XcmOrder: XcmOrder; - XcmOrderV0: XcmOrderV0; - XcmOrderV1: XcmOrderV1; - XcmOrderV2: XcmOrderV2; - XcmOrigin: XcmOrigin; - XcmOriginKind: XcmOriginKind; - XcmpMessageFormat: XcmpMessageFormat; - XcmV0: XcmV0; - XcmV1: XcmV1; - XcmV2: XcmV2; - XcmVersion: XcmVersion; - } // InterfaceTypes -} // declare module --- a/js-packages/types/src/default/definitions.ts +++ /dev/null @@ -1,6 +0,0 @@ -import types from '../lookup.js'; - -export default { - types, - rpc: {}, -}; --- a/js-packages/types/src/default/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -export * from './types.js'; --- a/js-packages/types/src/default/types.ts +++ /dev/null @@ -1,5537 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -import type { Data } from '@polkadot/types'; -import type { BTreeMap, BTreeSet, Bytes, Compact, Enum, Null, Option, Result, Struct, Text, U256, U8aFixed, Vec, bool, i64, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; -import type { ITuple } from '@polkadot/types-codec/types'; -import type { Vote } from '@polkadot/types/interfaces/elections'; -import type { AccountId32, Call, H160, H256, MultiAddress, Perbill } from '@polkadot/types/interfaces/runtime'; -import type { Event } from '@polkadot/types/interfaces/system'; - -/** @name CumulusPalletDmpQueueCall */ -export interface CumulusPalletDmpQueueCall extends Enum { - readonly isServiceOverweight: boolean; - readonly asServiceOverweight: { - readonly index: u64; - readonly weightLimit: SpWeightsWeightV2Weight; - } & Struct; - readonly type: 'ServiceOverweight'; -} - -/** @name CumulusPalletDmpQueueConfigData */ -export interface CumulusPalletDmpQueueConfigData extends Struct { - readonly maxIndividual: SpWeightsWeightV2Weight; -} - -/** @name CumulusPalletDmpQueueError */ -export interface CumulusPalletDmpQueueError extends Enum { - readonly isUnknown: boolean; - readonly isOverLimit: boolean; - readonly type: 'Unknown' | 'OverLimit'; -} - -/** @name CumulusPalletDmpQueueEvent */ -export interface CumulusPalletDmpQueueEvent extends Enum { - readonly isInvalidFormat: boolean; - readonly asInvalidFormat: { - readonly messageHash: U8aFixed; - } & Struct; - readonly isUnsupportedVersion: boolean; - readonly asUnsupportedVersion: { - readonly messageHash: U8aFixed; - } & Struct; - readonly isExecutedDownward: boolean; - readonly asExecutedDownward: { - readonly messageHash: U8aFixed; - readonly messageId: U8aFixed; - readonly outcome: StagingXcmV3TraitsOutcome; - } & Struct; - readonly isWeightExhausted: boolean; - readonly asWeightExhausted: { - readonly messageHash: U8aFixed; - readonly messageId: U8aFixed; - readonly remainingWeight: SpWeightsWeightV2Weight; - readonly requiredWeight: SpWeightsWeightV2Weight; - } & Struct; - readonly isOverweightEnqueued: boolean; - readonly asOverweightEnqueued: { - readonly messageHash: U8aFixed; - readonly messageId: U8aFixed; - readonly overweightIndex: u64; - readonly requiredWeight: SpWeightsWeightV2Weight; - } & Struct; - readonly isOverweightServiced: boolean; - readonly asOverweightServiced: { - readonly overweightIndex: u64; - readonly weightUsed: SpWeightsWeightV2Weight; - } & Struct; - readonly isMaxMessagesExhausted: boolean; - readonly asMaxMessagesExhausted: { - readonly messageHash: U8aFixed; - } & Struct; - readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced' | 'MaxMessagesExhausted'; -} - -/** @name CumulusPalletDmpQueuePageIndexData */ -export interface CumulusPalletDmpQueuePageIndexData extends Struct { - readonly beginUsed: u32; - readonly endUsed: u32; - readonly overweightCount: u64; -} - -/** @name CumulusPalletParachainSystemCall */ -export interface CumulusPalletParachainSystemCall extends Enum { - readonly isSetValidationData: boolean; - readonly asSetValidationData: { - readonly data: CumulusPrimitivesParachainInherentParachainInherentData; - } & Struct; - readonly isSudoSendUpwardMessage: boolean; - readonly asSudoSendUpwardMessage: { - readonly message: Bytes; - } & Struct; - readonly isAuthorizeUpgrade: boolean; - readonly asAuthorizeUpgrade: { - readonly codeHash: H256; - readonly checkVersion: bool; - } & Struct; - readonly isEnactAuthorizedUpgrade: boolean; - readonly asEnactAuthorizedUpgrade: { - readonly code: Bytes; - } & Struct; - readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade'; -} - -/** @name CumulusPalletParachainSystemCodeUpgradeAuthorization */ -export interface CumulusPalletParachainSystemCodeUpgradeAuthorization extends Struct { - readonly codeHash: H256; - readonly checkVersion: bool; -} - -/** @name CumulusPalletParachainSystemError */ -export interface CumulusPalletParachainSystemError extends Enum { - readonly isOverlappingUpgrades: boolean; - readonly isProhibitedByPolkadot: boolean; - readonly isTooBig: boolean; - readonly isValidationDataNotAvailable: boolean; - readonly isHostConfigurationNotAvailable: boolean; - readonly isNotScheduled: boolean; - readonly isNothingAuthorized: boolean; - readonly isUnauthorized: boolean; - readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized'; -} - -/** @name CumulusPalletParachainSystemEvent */ -export interface CumulusPalletParachainSystemEvent extends Enum { - readonly isValidationFunctionStored: boolean; - readonly isValidationFunctionApplied: boolean; - readonly asValidationFunctionApplied: { - readonly relayChainBlockNum: u32; - } & Struct; - readonly isValidationFunctionDiscarded: boolean; - readonly isUpgradeAuthorized: boolean; - readonly asUpgradeAuthorized: { - readonly codeHash: H256; - } & Struct; - readonly isDownwardMessagesReceived: boolean; - readonly asDownwardMessagesReceived: { - readonly count: u32; - } & Struct; - readonly isDownwardMessagesProcessed: boolean; - readonly asDownwardMessagesProcessed: { - readonly weightUsed: SpWeightsWeightV2Weight; - readonly dmqHead: H256; - } & Struct; - readonly isUpwardMessageSent: boolean; - readonly asUpwardMessageSent: { - readonly messageHash: Option; - } & Struct; - readonly type: 'ValidationFunctionStored' | 'ValidationFunctionApplied' | 'ValidationFunctionDiscarded' | 'UpgradeAuthorized' | 'DownwardMessagesReceived' | 'DownwardMessagesProcessed' | 'UpwardMessageSent'; -} - -/** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot */ -export interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct { - readonly dmqMqcHead: H256; - readonly relayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity; - readonly ingressChannels: Vec>; - readonly egressChannels: Vec>; -} - -/** @name CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity */ -export interface CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity extends Struct { - readonly remainingCount: u32; - readonly remainingSize: u32; -} - -/** @name CumulusPalletParachainSystemUnincludedSegmentAncestor */ -export interface CumulusPalletParachainSystemUnincludedSegmentAncestor extends Struct { - readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth; - readonly paraHeadHash: Option; - readonly consumedGoAheadSignal: Option; -} - -/** @name CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate */ -export interface CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate extends Struct { - readonly msgCount: u32; - readonly totalBytes: u32; -} - -/** @name CumulusPalletParachainSystemUnincludedSegmentSegmentTracker */ -export interface CumulusPalletParachainSystemUnincludedSegmentSegmentTracker extends Struct { - readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth; - readonly hrmpWatermark: Option; - readonly consumedGoAheadSignal: Option; -} - -/** @name CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth */ -export interface CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth extends Struct { - readonly umpMsgCount: u32; - readonly umpTotalBytes: u32; - readonly hrmpOutgoing: BTreeMap; -} - -/** @name CumulusPalletXcmCall */ -export interface CumulusPalletXcmCall extends Null {} - -/** @name CumulusPalletXcmError */ -export interface CumulusPalletXcmError extends Null {} - -/** @name CumulusPalletXcmEvent */ -export interface CumulusPalletXcmEvent extends Enum { - readonly isInvalidFormat: boolean; - readonly asInvalidFormat: U8aFixed; - readonly isUnsupportedVersion: boolean; - readonly asUnsupportedVersion: U8aFixed; - readonly isExecutedDownward: boolean; - readonly asExecutedDownward: ITuple<[U8aFixed, StagingXcmV3TraitsOutcome]>; - readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward'; -} - -/** @name CumulusPalletXcmOrigin */ -export interface CumulusPalletXcmOrigin extends Enum { - readonly isRelay: boolean; - readonly isSiblingParachain: boolean; - readonly asSiblingParachain: u32; - readonly type: 'Relay' | 'SiblingParachain'; -} - -/** @name CumulusPalletXcmpQueueCall */ -export interface CumulusPalletXcmpQueueCall extends Enum { - readonly isServiceOverweight: boolean; - readonly asServiceOverweight: { - readonly index: u64; - readonly weightLimit: SpWeightsWeightV2Weight; - } & Struct; - readonly isSuspendXcmExecution: boolean; - readonly isResumeXcmExecution: boolean; - readonly isUpdateSuspendThreshold: boolean; - readonly asUpdateSuspendThreshold: { - readonly new_: u32; - } & Struct; - readonly isUpdateDropThreshold: boolean; - readonly asUpdateDropThreshold: { - readonly new_: u32; - } & Struct; - readonly isUpdateResumeThreshold: boolean; - readonly asUpdateResumeThreshold: { - readonly new_: u32; - } & Struct; - readonly isUpdateThresholdWeight: boolean; - readonly asUpdateThresholdWeight: { - readonly new_: SpWeightsWeightV2Weight; - } & Struct; - readonly isUpdateWeightRestrictDecay: boolean; - readonly asUpdateWeightRestrictDecay: { - readonly new_: SpWeightsWeightV2Weight; - } & Struct; - readonly isUpdateXcmpMaxIndividualWeight: boolean; - readonly asUpdateXcmpMaxIndividualWeight: { - readonly new_: SpWeightsWeightV2Weight; - } & Struct; - readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight'; -} - -/** @name CumulusPalletXcmpQueueError */ -export interface CumulusPalletXcmpQueueError extends Enum { - readonly isFailedToSend: boolean; - readonly isBadXcmOrigin: boolean; - readonly isBadXcm: boolean; - readonly isBadOverweightIndex: boolean; - readonly isWeightOverLimit: boolean; - readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit'; -} - -/** @name CumulusPalletXcmpQueueEvent */ -export interface CumulusPalletXcmpQueueEvent extends Enum { - readonly isSuccess: boolean; - readonly asSuccess: { - readonly messageHash: U8aFixed; - readonly messageId: U8aFixed; - readonly weight: SpWeightsWeightV2Weight; - } & Struct; - readonly isFail: boolean; - readonly asFail: { - readonly messageHash: U8aFixed; - readonly messageId: U8aFixed; - readonly error: StagingXcmV3TraitsError; - readonly weight: SpWeightsWeightV2Weight; - } & Struct; - readonly isBadVersion: boolean; - readonly asBadVersion: { - readonly messageHash: U8aFixed; - } & Struct; - readonly isBadFormat: boolean; - readonly asBadFormat: { - readonly messageHash: U8aFixed; - } & Struct; - readonly isXcmpMessageSent: boolean; - readonly asXcmpMessageSent: { - readonly messageHash: U8aFixed; - } & Struct; - readonly isOverweightEnqueued: boolean; - readonly asOverweightEnqueued: { - readonly sender: u32; - readonly sentAt: u32; - readonly index: u64; - readonly required: SpWeightsWeightV2Weight; - } & Struct; - readonly isOverweightServiced: boolean; - readonly asOverweightServiced: { - readonly index: u64; - readonly used: SpWeightsWeightV2Weight; - } & Struct; - readonly type: 'Success' | 'Fail' | 'BadVersion' | 'BadFormat' | 'XcmpMessageSent' | 'OverweightEnqueued' | 'OverweightServiced'; -} - -/** @name CumulusPalletXcmpQueueInboundChannelDetails */ -export interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct { - readonly sender: u32; - readonly state: CumulusPalletXcmpQueueInboundState; - readonly messageMetadata: Vec>; -} - -/** @name CumulusPalletXcmpQueueInboundState */ -export interface CumulusPalletXcmpQueueInboundState extends Enum { - readonly isOk: boolean; - readonly isSuspended: boolean; - readonly type: 'Ok' | 'Suspended'; -} - -/** @name CumulusPalletXcmpQueueOutboundChannelDetails */ -export interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct { - readonly recipient: u32; - readonly state: CumulusPalletXcmpQueueOutboundState; - readonly signalsExist: bool; - readonly firstIndex: u16; - readonly lastIndex: u16; -} - -/** @name CumulusPalletXcmpQueueOutboundState */ -export interface CumulusPalletXcmpQueueOutboundState extends Enum { - readonly isOk: boolean; - readonly isSuspended: boolean; - readonly type: 'Ok' | 'Suspended'; -} - -/** @name CumulusPalletXcmpQueueQueueConfigData */ -export interface CumulusPalletXcmpQueueQueueConfigData extends Struct { - readonly suspendThreshold: u32; - readonly dropThreshold: u32; - readonly resumeThreshold: u32; - readonly thresholdWeight: SpWeightsWeightV2Weight; - readonly weightRestrictDecay: SpWeightsWeightV2Weight; - readonly xcmpMaxIndividualWeight: SpWeightsWeightV2Weight; -} - -/** @name CumulusPrimitivesParachainInherentParachainInherentData */ -export interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct { - readonly validationData: PolkadotPrimitivesV5PersistedValidationData; - readonly relayChainState: SpTrieStorageProof; - readonly downwardMessages: Vec; - readonly horizontalMessages: BTreeMap>; -} - -/** @name EthbloomBloom */ -export interface EthbloomBloom extends U8aFixed {} - -/** @name EthereumBlock */ -export interface EthereumBlock extends Struct { - readonly header: EthereumHeader; - readonly transactions: Vec; - readonly ommers: Vec; -} - -/** @name EthereumHeader */ -export interface EthereumHeader extends Struct { - readonly parentHash: H256; - readonly ommersHash: H256; - readonly beneficiary: H160; - readonly stateRoot: H256; - readonly transactionsRoot: H256; - readonly receiptsRoot: H256; - readonly logsBloom: EthbloomBloom; - readonly difficulty: U256; - readonly number: U256; - readonly gasLimit: U256; - readonly gasUsed: U256; - readonly timestamp: u64; - readonly extraData: Bytes; - readonly mixHash: H256; - readonly nonce: EthereumTypesHashH64; -} - -/** @name EthereumLog */ -export interface EthereumLog extends Struct { - readonly address: H160; - readonly topics: Vec; - readonly data: Bytes; -} - -/** @name EthereumReceiptEip658ReceiptData */ -export interface EthereumReceiptEip658ReceiptData extends Struct { - readonly statusCode: u8; - readonly usedGas: U256; - readonly logsBloom: EthbloomBloom; - readonly logs: Vec; -} - -/** @name EthereumReceiptReceiptV3 */ -export interface EthereumReceiptReceiptV3 extends Enum { - readonly isLegacy: boolean; - readonly asLegacy: EthereumReceiptEip658ReceiptData; - readonly isEip2930: boolean; - readonly asEip2930: EthereumReceiptEip658ReceiptData; - readonly isEip1559: boolean; - readonly asEip1559: EthereumReceiptEip658ReceiptData; - readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; -} - -/** @name EthereumTransactionAccessListItem */ -export interface EthereumTransactionAccessListItem extends Struct { - readonly address: H160; - readonly storageKeys: Vec; -} - -/** @name EthereumTransactionEip1559Transaction */ -export interface EthereumTransactionEip1559Transaction extends Struct { - readonly chainId: u64; - readonly nonce: U256; - readonly maxPriorityFeePerGas: U256; - readonly maxFeePerGas: U256; - readonly gasLimit: U256; - readonly action: EthereumTransactionTransactionAction; - readonly value: U256; - readonly input: Bytes; - readonly accessList: Vec; - readonly oddYParity: bool; - readonly r: H256; - readonly s: H256; -} - -/** @name EthereumTransactionEip2930Transaction */ -export interface EthereumTransactionEip2930Transaction extends Struct { - readonly chainId: u64; - readonly nonce: U256; - readonly gasPrice: U256; - readonly gasLimit: U256; - readonly action: EthereumTransactionTransactionAction; - readonly value: U256; - readonly input: Bytes; - readonly accessList: Vec; - readonly oddYParity: bool; - readonly r: H256; - readonly s: H256; -} - -/** @name EthereumTransactionLegacyTransaction */ -export interface EthereumTransactionLegacyTransaction extends Struct { - readonly nonce: U256; - readonly gasPrice: U256; - readonly gasLimit: U256; - readonly action: EthereumTransactionTransactionAction; - readonly value: U256; - readonly input: Bytes; - readonly signature: EthereumTransactionTransactionSignature; -} - -/** @name EthereumTransactionTransactionAction */ -export interface EthereumTransactionTransactionAction extends Enum { - readonly isCall: boolean; - readonly asCall: H160; - readonly isCreate: boolean; - readonly type: 'Call' | 'Create'; -} - -/** @name EthereumTransactionTransactionSignature */ -export interface EthereumTransactionTransactionSignature extends Struct { - readonly v: u64; - readonly r: H256; - readonly s: H256; -} - -/** @name EthereumTransactionTransactionV2 */ -export interface EthereumTransactionTransactionV2 extends Enum { - readonly isLegacy: boolean; - readonly asLegacy: EthereumTransactionLegacyTransaction; - readonly isEip2930: boolean; - readonly asEip2930: EthereumTransactionEip2930Transaction; - readonly isEip1559: boolean; - readonly asEip1559: EthereumTransactionEip1559Transaction; - readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; -} - -/** @name EthereumTypesHashH64 */ -export interface EthereumTypesHashH64 extends U8aFixed {} - -/** @name EvmCoreErrorExitError */ -export interface EvmCoreErrorExitError extends Enum { - readonly isStackUnderflow: boolean; - readonly isStackOverflow: boolean; - readonly isInvalidJump: boolean; - readonly isInvalidRange: boolean; - readonly isDesignatedInvalid: boolean; - readonly isCallTooDeep: boolean; - readonly isCreateCollision: boolean; - readonly isCreateContractLimit: boolean; - readonly isOutOfOffset: boolean; - readonly isOutOfGas: boolean; - readonly isOutOfFund: boolean; - readonly isPcUnderflow: boolean; - readonly isCreateEmpty: boolean; - readonly isOther: boolean; - readonly asOther: Text; - readonly isMaxNonce: boolean; - readonly isInvalidCode: boolean; - readonly asInvalidCode: u8; - readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'MaxNonce' | 'InvalidCode'; -} - -/** @name EvmCoreErrorExitFatal */ -export interface EvmCoreErrorExitFatal extends Enum { - readonly isNotSupported: boolean; - readonly isUnhandledInterrupt: boolean; - readonly isCallErrorAsFatal: boolean; - readonly asCallErrorAsFatal: EvmCoreErrorExitError; - readonly isOther: boolean; - readonly asOther: Text; - readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other'; -} - -/** @name EvmCoreErrorExitReason */ -export interface EvmCoreErrorExitReason extends Enum { - readonly isSucceed: boolean; - readonly asSucceed: EvmCoreErrorExitSucceed; - readonly isError: boolean; - readonly asError: EvmCoreErrorExitError; - readonly isRevert: boolean; - readonly asRevert: EvmCoreErrorExitRevert; - readonly isFatal: boolean; - readonly asFatal: EvmCoreErrorExitFatal; - readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal'; -} - -/** @name EvmCoreErrorExitRevert */ -export interface EvmCoreErrorExitRevert extends Enum { - readonly isReverted: boolean; - readonly type: 'Reverted'; -} - -/** @name EvmCoreErrorExitSucceed */ -export interface EvmCoreErrorExitSucceed extends Enum { - readonly isStopped: boolean; - readonly isReturned: boolean; - readonly isSuicided: boolean; - readonly type: 'Stopped' | 'Returned' | 'Suicided'; -} - -/** @name FpRpcTransactionStatus */ -export interface FpRpcTransactionStatus extends Struct { - readonly transactionHash: H256; - readonly transactionIndex: u32; - readonly from: H160; - readonly to: Option; - readonly contractAddress: Option; - readonly logs: Vec; - readonly logsBloom: EthbloomBloom; -} - -/** @name FrameSupportDispatchDispatchClass */ -export interface FrameSupportDispatchDispatchClass extends Enum { - readonly isNormal: boolean; - readonly isOperational: boolean; - readonly isMandatory: boolean; - readonly type: 'Normal' | 'Operational' | 'Mandatory'; -} - -/** @name FrameSupportDispatchDispatchInfo */ -export interface FrameSupportDispatchDispatchInfo extends Struct { - readonly weight: SpWeightsWeightV2Weight; - readonly class: FrameSupportDispatchDispatchClass; - readonly paysFee: FrameSupportDispatchPays; -} - -/** @name FrameSupportDispatchPays */ -export interface FrameSupportDispatchPays extends Enum { - readonly isYes: boolean; - readonly isNo: boolean; - readonly type: 'Yes' | 'No'; -} - -/** @name FrameSupportDispatchPerDispatchClassU32 */ -export interface FrameSupportDispatchPerDispatchClassU32 extends Struct { - readonly normal: u32; - readonly operational: u32; - readonly mandatory: u32; -} - -/** @name FrameSupportDispatchPerDispatchClassWeight */ -export interface FrameSupportDispatchPerDispatchClassWeight extends Struct { - readonly normal: SpWeightsWeightV2Weight; - readonly operational: SpWeightsWeightV2Weight; - readonly mandatory: SpWeightsWeightV2Weight; -} - -/** @name FrameSupportDispatchPerDispatchClassWeightsPerClass */ -export interface FrameSupportDispatchPerDispatchClassWeightsPerClass extends Struct { - readonly normal: FrameSystemLimitsWeightsPerClass; - readonly operational: FrameSystemLimitsWeightsPerClass; - readonly mandatory: FrameSystemLimitsWeightsPerClass; -} - -/** @name FrameSupportDispatchRawOrigin */ -export interface FrameSupportDispatchRawOrigin extends Enum { - readonly isRoot: boolean; - readonly isSigned: boolean; - readonly asSigned: AccountId32; - readonly isNone: boolean; - readonly type: 'Root' | 'Signed' | 'None'; -} - -/** @name FrameSupportPalletId */ -export interface FrameSupportPalletId extends U8aFixed {} - -/** @name FrameSupportPreimagesBounded */ -export interface FrameSupportPreimagesBounded extends Enum { - readonly isLegacy: boolean; - readonly asLegacy: { - readonly hash_: H256; - } & Struct; - readonly isInline: boolean; - readonly asInline: Bytes; - readonly isLookup: boolean; - readonly asLookup: { - readonly hash_: H256; - readonly len: u32; - } & Struct; - readonly type: 'Legacy' | 'Inline' | 'Lookup'; -} - -/** @name FrameSupportScheduleDispatchTime */ -export interface FrameSupportScheduleDispatchTime extends Enum { - readonly isAt: boolean; - readonly asAt: u32; - readonly isAfter: boolean; - readonly asAfter: u32; - readonly type: 'At' | 'After'; -} - -/** @name FrameSupportTokensMiscBalanceStatus */ -export interface FrameSupportTokensMiscBalanceStatus extends Enum { - readonly isFree: boolean; - readonly isReserved: boolean; - readonly type: 'Free' | 'Reserved'; -} - -/** @name FrameSystemAccountInfo */ -export interface FrameSystemAccountInfo extends Struct { - readonly nonce: u32; - readonly consumers: u32; - readonly providers: u32; - readonly sufficients: u32; - readonly data: PalletBalancesAccountData; -} - -/** @name FrameSystemCall */ -export interface FrameSystemCall extends Enum { - readonly isRemark: boolean; - readonly asRemark: { - readonly remark: Bytes; - } & Struct; - readonly isSetHeapPages: boolean; - readonly asSetHeapPages: { - readonly pages: u64; - } & Struct; - readonly isSetCode: boolean; - readonly asSetCode: { - readonly code: Bytes; - } & Struct; - readonly isSetCodeWithoutChecks: boolean; - readonly asSetCodeWithoutChecks: { - readonly code: Bytes; - } & Struct; - readonly isSetStorage: boolean; - readonly asSetStorage: { - readonly items: Vec>; - } & Struct; - readonly isKillStorage: boolean; - readonly asKillStorage: { - readonly keys_: Vec; - } & Struct; - readonly isKillPrefix: boolean; - readonly asKillPrefix: { - readonly prefix: Bytes; - readonly subkeys: u32; - } & Struct; - readonly isRemarkWithEvent: boolean; - readonly asRemarkWithEvent: { - readonly remark: Bytes; - } & Struct; - readonly type: 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent'; -} - -/** @name FrameSystemError */ -export interface FrameSystemError extends Enum { - readonly isInvalidSpecName: boolean; - readonly isSpecVersionNeedsToIncrease: boolean; - readonly isFailedToExtractRuntimeVersion: boolean; - readonly isNonDefaultComposite: boolean; - readonly isNonZeroRefCount: boolean; - readonly isCallFiltered: boolean; - readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered'; -} - -/** @name FrameSystemEvent */ -export interface FrameSystemEvent extends Enum { - readonly isExtrinsicSuccess: boolean; - readonly asExtrinsicSuccess: { - readonly dispatchInfo: FrameSupportDispatchDispatchInfo; - } & Struct; - readonly isExtrinsicFailed: boolean; - readonly asExtrinsicFailed: { - readonly dispatchError: SpRuntimeDispatchError; - readonly dispatchInfo: FrameSupportDispatchDispatchInfo; - } & Struct; - readonly isCodeUpdated: boolean; - readonly isNewAccount: boolean; - readonly asNewAccount: { - readonly account: AccountId32; - } & Struct; - readonly isKilledAccount: boolean; - readonly asKilledAccount: { - readonly account: AccountId32; - } & Struct; - readonly isRemarked: boolean; - readonly asRemarked: { - readonly sender: AccountId32; - readonly hash_: H256; - } & Struct; - readonly type: 'ExtrinsicSuccess' | 'ExtrinsicFailed' | 'CodeUpdated' | 'NewAccount' | 'KilledAccount' | 'Remarked'; -} - -/** @name FrameSystemEventRecord */ -export interface FrameSystemEventRecord extends Struct { - readonly phase: FrameSystemPhase; - readonly event: Event; - readonly topics: Vec; -} - -/** @name FrameSystemExtensionsCheckGenesis */ -export interface FrameSystemExtensionsCheckGenesis extends Null {} - -/** @name FrameSystemExtensionsCheckNonce */ -export interface FrameSystemExtensionsCheckNonce extends Compact {} - -/** @name FrameSystemExtensionsCheckSpecVersion */ -export interface FrameSystemExtensionsCheckSpecVersion extends Null {} - -/** @name FrameSystemExtensionsCheckTxVersion */ -export interface FrameSystemExtensionsCheckTxVersion extends Null {} - -/** @name FrameSystemExtensionsCheckWeight */ -export interface FrameSystemExtensionsCheckWeight extends Null {} - -/** @name FrameSystemLastRuntimeUpgradeInfo */ -export interface FrameSystemLastRuntimeUpgradeInfo extends Struct { - readonly specVersion: Compact; - readonly specName: Text; -} - -/** @name FrameSystemLimitsBlockLength */ -export interface FrameSystemLimitsBlockLength extends Struct { - readonly max: FrameSupportDispatchPerDispatchClassU32; -} - -/** @name FrameSystemLimitsBlockWeights */ -export interface FrameSystemLimitsBlockWeights extends Struct { - readonly baseBlock: SpWeightsWeightV2Weight; - readonly maxBlock: SpWeightsWeightV2Weight; - readonly perClass: FrameSupportDispatchPerDispatchClassWeightsPerClass; -} - -/** @name FrameSystemLimitsWeightsPerClass */ -export interface FrameSystemLimitsWeightsPerClass extends Struct { - readonly baseExtrinsic: SpWeightsWeightV2Weight; - readonly maxExtrinsic: Option; - readonly maxTotal: Option; - readonly reserved: Option; -} - -/** @name FrameSystemPhase */ -export interface FrameSystemPhase extends Enum { - readonly isApplyExtrinsic: boolean; - readonly asApplyExtrinsic: u32; - readonly isFinalization: boolean; - readonly isInitialization: boolean; - readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization'; -} - -/** @name OpalRuntimeOriginCaller */ -export interface OpalRuntimeOriginCaller extends Enum { - readonly isSystem: boolean; - readonly asSystem: FrameSupportDispatchRawOrigin; - readonly isVoid: boolean; - readonly asVoid: SpCoreVoid; - readonly isCouncil: boolean; - readonly asCouncil: PalletCollectiveRawOrigin; - readonly isTechnicalCommittee: boolean; - readonly asTechnicalCommittee: PalletCollectiveRawOrigin; - readonly isPolkadotXcm: boolean; - readonly asPolkadotXcm: PalletXcmOrigin; - readonly isCumulusXcm: boolean; - readonly asCumulusXcm: CumulusPalletXcmOrigin; - readonly isOrigins: boolean; - readonly asOrigins: PalletGovOriginsOrigin; - readonly isEthereum: boolean; - readonly asEthereum: PalletEthereumRawOrigin; - readonly type: 'System' | 'Void' | 'Council' | 'TechnicalCommittee' | 'PolkadotXcm' | 'CumulusXcm' | 'Origins' | 'Ethereum'; -} - -/** @name OpalRuntimeRuntime */ -export interface OpalRuntimeRuntime extends Null {} - -/** @name OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls */ -export interface OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls extends Null {} - -/** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance */ -export interface OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance extends Null {} - -/** @name OpalRuntimeRuntimeCommonSessionKeys */ -export interface OpalRuntimeRuntimeCommonSessionKeys extends Struct { - readonly aura: SpConsensusAuraSr25519AppSr25519Public; -} - -/** @name OpalRuntimeRuntimeHoldReason */ -export interface OpalRuntimeRuntimeHoldReason extends Enum { - readonly isCollatorSelection: boolean; - readonly asCollatorSelection: PalletCollatorSelectionHoldReason; - readonly type: 'CollatorSelection'; -} - -/** @name OrmlTokensAccountData */ -export interface OrmlTokensAccountData extends Struct { - readonly free: u128; - readonly reserved: u128; - readonly frozen: u128; -} - -/** @name OrmlTokensBalanceLock */ -export interface OrmlTokensBalanceLock extends Struct { - readonly id: U8aFixed; - readonly amount: u128; -} - -/** @name OrmlTokensModuleCall */ -export interface OrmlTokensModuleCall extends Enum { - readonly isTransfer: boolean; - readonly asTransfer: { - readonly dest: MultiAddress; - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: Compact; - } & Struct; - readonly isTransferAll: boolean; - readonly asTransferAll: { - readonly dest: MultiAddress; - readonly currencyId: PalletForeignAssetsAssetId; - readonly keepAlive: bool; - } & Struct; - readonly isTransferKeepAlive: boolean; - readonly asTransferKeepAlive: { - readonly dest: MultiAddress; - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: Compact; - } & Struct; - readonly isForceTransfer: boolean; - readonly asForceTransfer: { - readonly source: MultiAddress; - readonly dest: MultiAddress; - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: Compact; - } & Struct; - readonly isSetBalance: boolean; - readonly asSetBalance: { - readonly who: MultiAddress; - readonly currencyId: PalletForeignAssetsAssetId; - readonly newFree: Compact; - readonly newReserved: Compact; - } & Struct; - readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance'; -} - -/** @name OrmlTokensModuleError */ -export interface OrmlTokensModuleError extends Enum { - readonly isBalanceTooLow: boolean; - readonly isAmountIntoBalanceFailed: boolean; - readonly isLiquidityRestrictions: boolean; - readonly isMaxLocksExceeded: boolean; - readonly isKeepAlive: boolean; - readonly isExistentialDeposit: boolean; - readonly isDeadAccount: boolean; - readonly isTooManyReserves: boolean; - readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves'; -} - -/** @name OrmlTokensModuleEvent */ -export interface OrmlTokensModuleEvent extends Enum { - readonly isEndowed: boolean; - readonly asEndowed: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isDustLost: boolean; - readonly asDustLost: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isTransfer: boolean; - readonly asTransfer: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly from: AccountId32; - readonly to: AccountId32; - readonly amount: u128; - } & Struct; - readonly isReserved: boolean; - readonly asReserved: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isUnreserved: boolean; - readonly asUnreserved: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isReserveRepatriated: boolean; - readonly asReserveRepatriated: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly from: AccountId32; - readonly to: AccountId32; - readonly amount: u128; - readonly status: FrameSupportTokensMiscBalanceStatus; - } & Struct; - readonly isBalanceSet: boolean; - readonly asBalanceSet: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly free: u128; - readonly reserved: u128; - } & Struct; - readonly isTotalIssuanceSet: boolean; - readonly asTotalIssuanceSet: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: u128; - } & Struct; - readonly isWithdrawn: boolean; - readonly asWithdrawn: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isSlashed: boolean; - readonly asSlashed: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly freeAmount: u128; - readonly reservedAmount: u128; - } & Struct; - readonly isDeposited: boolean; - readonly asDeposited: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isLockSet: boolean; - readonly asLockSet: { - readonly lockId: U8aFixed; - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isLockRemoved: boolean; - readonly asLockRemoved: { - readonly lockId: U8aFixed; - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - } & Struct; - readonly isLocked: boolean; - readonly asLocked: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isUnlocked: boolean; - readonly asUnlocked: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isIssued: boolean; - readonly asIssued: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: u128; - } & Struct; - readonly isRescinded: boolean; - readonly asRescinded: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: u128; - } & Struct; - readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'BalanceSet' | 'TotalIssuanceSet' | 'Withdrawn' | 'Slashed' | 'Deposited' | 'LockSet' | 'LockRemoved' | 'Locked' | 'Unlocked' | 'Issued' | 'Rescinded'; -} - -/** @name OrmlTokensReserveData */ -export interface OrmlTokensReserveData extends Struct { - readonly id: Null; - readonly amount: u128; -} - -/** @name OrmlVestingModuleCall */ -export interface OrmlVestingModuleCall extends Enum { - readonly isClaim: boolean; - readonly isVestedTransfer: boolean; - readonly asVestedTransfer: { - readonly dest: MultiAddress; - readonly schedule: OrmlVestingVestingSchedule; - } & Struct; - readonly isUpdateVestingSchedules: boolean; - readonly asUpdateVestingSchedules: { - readonly who: MultiAddress; - readonly vestingSchedules: Vec; - } & Struct; - readonly isClaimFor: boolean; - readonly asClaimFor: { - readonly dest: MultiAddress; - } & Struct; - readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor'; -} - -/** @name OrmlVestingModuleError */ -export interface OrmlVestingModuleError extends Enum { - readonly isZeroVestingPeriod: boolean; - readonly isZeroVestingPeriodCount: boolean; - readonly isInsufficientBalanceToLock: boolean; - readonly isTooManyVestingSchedules: boolean; - readonly isAmountLow: boolean; - readonly isMaxVestingSchedulesExceeded: boolean; - readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded'; -} - -/** @name OrmlVestingModuleEvent */ -export interface OrmlVestingModuleEvent extends Enum { - readonly isVestingScheduleAdded: boolean; - readonly asVestingScheduleAdded: { - readonly from: AccountId32; - readonly to: AccountId32; - readonly vestingSchedule: OrmlVestingVestingSchedule; - } & Struct; - readonly isClaimed: boolean; - readonly asClaimed: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isVestingSchedulesUpdated: boolean; - readonly asVestingSchedulesUpdated: { - readonly who: AccountId32; - } & Struct; - readonly type: 'VestingScheduleAdded' | 'Claimed' | 'VestingSchedulesUpdated'; -} - -/** @name OrmlVestingVestingSchedule */ -export interface OrmlVestingVestingSchedule extends Struct { - readonly start: u32; - readonly period: u32; - readonly periodCount: u32; - readonly perPeriod: Compact; -} - -/** @name OrmlXtokensModuleCall */ -export interface OrmlXtokensModuleCall extends Enum { - readonly isTransfer: boolean; - readonly asTransfer: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: u128; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isTransferMultiasset: boolean; - readonly asTransferMultiasset: { - readonly asset: StagingXcmVersionedMultiAsset; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isTransferWithFee: boolean; - readonly asTransferWithFee: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: u128; - readonly fee: u128; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isTransferMultiassetWithFee: boolean; - readonly asTransferMultiassetWithFee: { - readonly asset: StagingXcmVersionedMultiAsset; - readonly fee: StagingXcmVersionedMultiAsset; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isTransferMulticurrencies: boolean; - readonly asTransferMulticurrencies: { - readonly currencies: Vec>; - readonly feeItem: u32; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isTransferMultiassets: boolean; - readonly asTransferMultiassets: { - readonly assets: StagingXcmVersionedMultiAssets; - readonly feeItem: u32; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets'; -} - -/** @name OrmlXtokensModuleError */ -export interface OrmlXtokensModuleError extends Enum { - readonly isAssetHasNoReserve: boolean; - readonly isNotCrossChainTransfer: boolean; - readonly isInvalidDest: boolean; - readonly isNotCrossChainTransferableCurrency: boolean; - readonly isUnweighableMessage: boolean; - readonly isXcmExecutionFailed: boolean; - readonly isCannotReanchor: boolean; - readonly isInvalidAncestry: boolean; - readonly isInvalidAsset: boolean; - readonly isDestinationNotInvertible: boolean; - readonly isBadVersion: boolean; - readonly isDistinctReserveForAssetAndFee: boolean; - readonly isZeroFee: boolean; - readonly isZeroAmount: boolean; - readonly isTooManyAssetsBeingSent: boolean; - readonly isAssetIndexNonExistent: boolean; - readonly isFeeNotEnough: boolean; - readonly isNotSupportedMultiLocation: boolean; - readonly isMinXcmFeeNotDefined: boolean; - readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined'; -} - -/** @name OrmlXtokensModuleEvent */ -export interface OrmlXtokensModuleEvent extends Enum { - readonly isTransferredMultiAssets: boolean; - readonly asTransferredMultiAssets: { - readonly sender: AccountId32; - readonly assets: StagingXcmV3MultiassetMultiAssets; - readonly fee: StagingXcmV3MultiAsset; - readonly dest: StagingXcmV3MultiLocation; - } & Struct; - readonly type: 'TransferredMultiAssets'; -} - -/** @name PalletAppPromotionCall */ -export interface PalletAppPromotionCall extends Enum { - readonly isSetAdminAddress: boolean; - readonly asSetAdminAddress: { - readonly admin: PalletEvmAccountBasicCrossAccountIdRepr; - } & Struct; - readonly isStake: boolean; - readonly asStake: { - readonly amount: u128; - } & Struct; - readonly isUnstakeAll: boolean; - readonly isSponsorCollection: boolean; - readonly asSponsorCollection: { - readonly collectionId: u32; - } & Struct; - readonly isStopSponsoringCollection: boolean; - readonly asStopSponsoringCollection: { - readonly collectionId: u32; - } & Struct; - readonly isSponsorContract: boolean; - readonly asSponsorContract: { - readonly contractId: H160; - } & Struct; - readonly isStopSponsoringContract: boolean; - readonly asStopSponsoringContract: { - readonly contractId: H160; - } & Struct; - readonly isPayoutStakers: boolean; - readonly asPayoutStakers: { - readonly stakersNumber: Option; - } & Struct; - readonly isUnstakePartial: boolean; - readonly asUnstakePartial: { - readonly amount: u128; - } & Struct; - readonly isForceUnstake: boolean; - readonly asForceUnstake: { - readonly pendingBlocks: Vec; - } & Struct; - readonly type: 'SetAdminAddress' | 'Stake' | 'UnstakeAll' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers' | 'UnstakePartial' | 'ForceUnstake'; -} - -/** @name PalletAppPromotionError */ -export interface PalletAppPromotionError extends Enum { - readonly isAdminNotSet: boolean; - readonly isNoPermission: boolean; - readonly isNotSufficientFunds: boolean; - readonly isPendingForBlockOverflow: boolean; - readonly isSponsorNotSet: boolean; - readonly isInsufficientStakedBalance: boolean; - readonly isInconsistencyState: boolean; - readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'InsufficientStakedBalance' | 'InconsistencyState'; -} - -/** @name PalletAppPromotionEvent */ -export interface PalletAppPromotionEvent extends Enum { - readonly isStakingRecalculation: boolean; - readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>; - readonly isStake: boolean; - readonly asStake: ITuple<[AccountId32, u128]>; - readonly isUnstake: boolean; - readonly asUnstake: ITuple<[AccountId32, u128]>; - readonly isSetAdmin: boolean; - readonly asSetAdmin: AccountId32; - readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin'; -} - -/** @name PalletBalancesAccountData */ -export interface PalletBalancesAccountData extends Struct { - readonly free: u128; - readonly reserved: u128; - readonly frozen: u128; - readonly flags: u128; -} - -/** @name PalletBalancesBalanceLock */ -export interface PalletBalancesBalanceLock extends Struct { - readonly id: U8aFixed; - readonly amount: u128; - readonly reasons: PalletBalancesReasons; -} - -/** @name PalletBalancesCall */ -export interface PalletBalancesCall extends Enum { - readonly isTransferAllowDeath: boolean; - readonly asTransferAllowDeath: { - readonly dest: MultiAddress; - readonly value: Compact; - } & Struct; - readonly isSetBalanceDeprecated: boolean; - readonly asSetBalanceDeprecated: { - readonly who: MultiAddress; - readonly newFree: Compact; - readonly oldReserved: Compact; - } & Struct; - readonly isForceTransfer: boolean; - readonly asForceTransfer: { - readonly source: MultiAddress; - readonly dest: MultiAddress; - readonly value: Compact; - } & Struct; - readonly isTransferKeepAlive: boolean; - readonly asTransferKeepAlive: { - readonly dest: MultiAddress; - readonly value: Compact; - } & Struct; - readonly isTransferAll: boolean; - readonly asTransferAll: { - readonly dest: MultiAddress; - readonly keepAlive: bool; - } & Struct; - readonly isForceUnreserve: boolean; - readonly asForceUnreserve: { - readonly who: MultiAddress; - readonly amount: u128; - } & Struct; - readonly isUpgradeAccounts: boolean; - readonly asUpgradeAccounts: { - readonly who: Vec; - } & Struct; - readonly isTransfer: boolean; - readonly asTransfer: { - readonly dest: MultiAddress; - readonly value: Compact; - } & Struct; - readonly isForceSetBalance: boolean; - readonly asForceSetBalance: { - readonly who: MultiAddress; - readonly newFree: Compact; - } & Struct; - readonly type: 'TransferAllowDeath' | 'SetBalanceDeprecated' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve' | 'UpgradeAccounts' | 'Transfer' | 'ForceSetBalance'; -} - -/** @name PalletBalancesError */ -export interface PalletBalancesError extends Enum { - readonly isVestingBalance: boolean; - readonly isLiquidityRestrictions: boolean; - readonly isInsufficientBalance: boolean; - readonly isExistentialDeposit: boolean; - readonly isExpendability: boolean; - readonly isExistingVestingSchedule: boolean; - readonly isDeadAccount: boolean; - readonly isTooManyReserves: boolean; - readonly isTooManyHolds: boolean; - readonly isTooManyFreezes: boolean; - readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'Expendability' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves' | 'TooManyHolds' | 'TooManyFreezes'; -} - -/** @name PalletBalancesEvent */ -export interface PalletBalancesEvent extends Enum { - readonly isEndowed: boolean; - readonly asEndowed: { - readonly account: AccountId32; - readonly freeBalance: u128; - } & Struct; - readonly isDustLost: boolean; - readonly asDustLost: { - readonly account: AccountId32; - readonly amount: u128; - } & Struct; - readonly isTransfer: boolean; - readonly asTransfer: { - readonly from: AccountId32; - readonly to: AccountId32; - readonly amount: u128; - } & Struct; - readonly isBalanceSet: boolean; - readonly asBalanceSet: { - readonly who: AccountId32; - readonly free: u128; - } & Struct; - readonly isReserved: boolean; - readonly asReserved: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isUnreserved: boolean; - readonly asUnreserved: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isReserveRepatriated: boolean; - readonly asReserveRepatriated: { - readonly from: AccountId32; - readonly to: AccountId32; - readonly amount: u128; - readonly destinationStatus: FrameSupportTokensMiscBalanceStatus; - } & Struct; - readonly isDeposit: boolean; - readonly asDeposit: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isWithdraw: boolean; - readonly asWithdraw: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isSlashed: boolean; - readonly asSlashed: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isMinted: boolean; - readonly asMinted: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isBurned: boolean; - readonly asBurned: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isSuspended: boolean; - readonly asSuspended: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isRestored: boolean; - readonly asRestored: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isUpgraded: boolean; - readonly asUpgraded: { - readonly who: AccountId32; - } & Struct; - readonly isIssued: boolean; - readonly asIssued: { - readonly amount: u128; - } & Struct; - readonly isRescinded: boolean; - readonly asRescinded: { - readonly amount: u128; - } & Struct; - readonly isLocked: boolean; - readonly asLocked: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isUnlocked: boolean; - readonly asUnlocked: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isFrozen: boolean; - readonly asFrozen: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isThawed: boolean; - readonly asThawed: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'BalanceSet' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'Deposit' | 'Withdraw' | 'Slashed' | 'Minted' | 'Burned' | 'Suspended' | 'Restored' | 'Upgraded' | 'Issued' | 'Rescinded' | 'Locked' | 'Unlocked' | 'Frozen' | 'Thawed'; -} - -/** @name PalletBalancesIdAmount */ -export interface PalletBalancesIdAmount extends Struct { - readonly id: U8aFixed; - readonly amount: u128; -} - -/** @name PalletBalancesReasons */ -export interface PalletBalancesReasons extends Enum { - readonly isFee: boolean; - readonly isMisc: boolean; - readonly isAll: boolean; - readonly type: 'Fee' | 'Misc' | 'All'; -} - -/** @name PalletBalancesReserveData */ -export interface PalletBalancesReserveData extends Struct { - readonly id: U8aFixed; - readonly amount: u128; -} - -/** @name PalletCollatorSelectionCall */ -export interface PalletCollatorSelectionCall extends Enum { - readonly isAddInvulnerable: boolean; - readonly asAddInvulnerable: { - readonly new_: AccountId32; - } & Struct; - readonly isRemoveInvulnerable: boolean; - readonly asRemoveInvulnerable: { - readonly who: AccountId32; - } & Struct; - readonly isGetLicense: boolean; - readonly isOnboard: boolean; - readonly isOffboard: boolean; - readonly isReleaseLicense: boolean; - readonly isForceReleaseLicense: boolean; - readonly asForceReleaseLicense: { - readonly who: AccountId32; - } & Struct; - readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceReleaseLicense'; -} - -/** @name PalletCollatorSelectionError */ -export interface PalletCollatorSelectionError extends Enum { - readonly isTooManyCandidates: boolean; - readonly isUnknown: boolean; - readonly isPermission: boolean; - readonly isAlreadyHoldingLicense: boolean; - readonly isNoLicense: boolean; - readonly isAlreadyCandidate: boolean; - readonly isNotCandidate: boolean; - readonly isTooManyInvulnerables: boolean; - readonly isTooFewInvulnerables: boolean; - readonly isAlreadyInvulnerable: boolean; - readonly isNotInvulnerable: boolean; - readonly isNoAssociatedValidatorId: boolean; - readonly isValidatorNotRegistered: boolean; - readonly type: 'TooManyCandidates' | 'Unknown' | 'Permission' | 'AlreadyHoldingLicense' | 'NoLicense' | 'AlreadyCandidate' | 'NotCandidate' | 'TooManyInvulnerables' | 'TooFewInvulnerables' | 'AlreadyInvulnerable' | 'NotInvulnerable' | 'NoAssociatedValidatorId' | 'ValidatorNotRegistered'; -} - -/** @name PalletCollatorSelectionEvent */ -export interface PalletCollatorSelectionEvent extends Enum { - readonly isInvulnerableAdded: boolean; - readonly asInvulnerableAdded: { - readonly invulnerable: AccountId32; - } & Struct; - readonly isInvulnerableRemoved: boolean; - readonly asInvulnerableRemoved: { - readonly invulnerable: AccountId32; - } & Struct; - readonly isLicenseObtained: boolean; - readonly asLicenseObtained: { - readonly accountId: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isLicenseReleased: boolean; - readonly asLicenseReleased: { - readonly accountId: AccountId32; - readonly depositReturned: u128; - } & Struct; - readonly isCandidateAdded: boolean; - readonly asCandidateAdded: { - readonly accountId: AccountId32; - } & Struct; - readonly isCandidateRemoved: boolean; - readonly asCandidateRemoved: { - readonly accountId: AccountId32; - } & Struct; - readonly type: 'InvulnerableAdded' | 'InvulnerableRemoved' | 'LicenseObtained' | 'LicenseReleased' | 'CandidateAdded' | 'CandidateRemoved'; -} - -/** @name PalletCollatorSelectionHoldReason */ -export interface PalletCollatorSelectionHoldReason extends Enum { - readonly isLicenseBond: boolean; - readonly type: 'LicenseBond'; -} - -/** @name PalletCollectiveCall */ -export interface PalletCollectiveCall extends Enum { - readonly isSetMembers: boolean; - readonly asSetMembers: { - readonly newMembers: Vec; - readonly prime: Option; - readonly oldCount: u32; - } & Struct; - readonly isExecute: boolean; - readonly asExecute: { - readonly proposal: Call; - readonly lengthBound: Compact; - } & Struct; - readonly isPropose: boolean; - readonly asPropose: { - readonly threshold: Compact; - readonly proposal: Call; - readonly lengthBound: Compact; - } & Struct; - readonly isVote: boolean; - readonly asVote: { - readonly proposal: H256; - readonly index: Compact; - readonly approve: bool; - } & Struct; - readonly isDisapproveProposal: boolean; - readonly asDisapproveProposal: { - readonly proposalHash: H256; - } & Struct; - readonly isClose: boolean; - readonly asClose: { - readonly proposalHash: H256; - readonly index: Compact; - readonly proposalWeightBound: SpWeightsWeightV2Weight; - readonly lengthBound: Compact; - } & Struct; - readonly type: 'SetMembers' | 'Execute' | 'Propose' | 'Vote' | 'DisapproveProposal' | 'Close'; -} - -/** @name PalletCollectiveError */ -export interface PalletCollectiveError extends Enum { - readonly isNotMember: boolean; - readonly isDuplicateProposal: boolean; - readonly isProposalMissing: boolean; - readonly isWrongIndex: boolean; - readonly isDuplicateVote: boolean; - readonly isAlreadyInitialized: boolean; - readonly isTooEarly: boolean; - readonly isTooManyProposals: boolean; - readonly isWrongProposalWeight: boolean; - readonly isWrongProposalLength: boolean; - readonly isPrimeAccountNotMember: boolean; - readonly type: 'NotMember' | 'DuplicateProposal' | 'ProposalMissing' | 'WrongIndex' | 'DuplicateVote' | 'AlreadyInitialized' | 'TooEarly' | 'TooManyProposals' | 'WrongProposalWeight' | 'WrongProposalLength' | 'PrimeAccountNotMember'; -} - -/** @name PalletCollectiveEvent */ -export interface PalletCollectiveEvent extends Enum { - readonly isProposed: boolean; - readonly asProposed: { - readonly account: AccountId32; - readonly proposalIndex: u32; - readonly proposalHash: H256; - readonly threshold: u32; - } & Struct; - readonly isVoted: boolean; - readonly asVoted: { - readonly account: AccountId32; - readonly proposalHash: H256; - readonly voted: bool; - readonly yes: u32; - readonly no: u32; - } & Struct; - readonly isApproved: boolean; - readonly asApproved: { - readonly proposalHash: H256; - } & Struct; - readonly isDisapproved: boolean; - readonly asDisapproved: { - readonly proposalHash: H256; - } & Struct; - readonly isExecuted: boolean; - readonly asExecuted: { - readonly proposalHash: H256; - readonly result: Result; - } & Struct; - readonly isMemberExecuted: boolean; - readonly asMemberExecuted: { - readonly proposalHash: H256; - readonly result: Result; - } & Struct; - readonly isClosed: boolean; - readonly asClosed: { - readonly proposalHash: H256; - readonly yes: u32; - readonly no: u32; - } & Struct; - readonly type: 'Proposed' | 'Voted' | 'Approved' | 'Disapproved' | 'Executed' | 'MemberExecuted' | 'Closed'; -} - -/** @name PalletCollectiveRawOrigin */ -export interface PalletCollectiveRawOrigin extends Enum { - readonly isMembers: boolean; - readonly asMembers: ITuple<[u32, u32]>; - readonly isMember: boolean; - readonly asMember: AccountId32; - readonly isPhantom: boolean; - readonly type: 'Members' | 'Member' | 'Phantom'; -} - -/** @name PalletCollectiveVotes */ -export interface PalletCollectiveVotes extends Struct { - readonly index: u32; - readonly threshold: u32; - readonly ayes: Vec; - readonly nays: Vec; - readonly end: u32; -} - -/** @name PalletCommonError */ -export interface PalletCommonError extends Enum { - readonly isCollectionNotFound: boolean; - readonly isMustBeTokenOwner: boolean; - readonly isNoPermission: boolean; - readonly isCantDestroyNotEmptyCollection: boolean; - readonly isPublicMintingNotAllowed: boolean; - readonly isAddressNotInAllowlist: boolean; - readonly isCollectionNameLimitExceeded: boolean; - readonly isCollectionDescriptionLimitExceeded: boolean; - readonly isCollectionTokenPrefixLimitExceeded: boolean; - readonly isTotalCollectionsLimitExceeded: boolean; - readonly isCollectionAdminCountExceeded: boolean; - readonly isCollectionLimitBoundsExceeded: boolean; - readonly isOwnerPermissionsCantBeReverted: boolean; - readonly isTransferNotAllowed: boolean; - readonly isAccountTokenLimitExceeded: boolean; - readonly isCollectionTokenLimitExceeded: boolean; - readonly isMetadataFlagFrozen: boolean; - readonly isTokenNotFound: boolean; - readonly isTokenValueTooLow: boolean; - readonly isApprovedValueTooLow: boolean; - readonly isCantApproveMoreThanOwned: boolean; - readonly isAddressIsNotEthMirror: boolean; - readonly isAddressIsZero: boolean; - readonly isUnsupportedOperation: boolean; - readonly isNotSufficientFounds: boolean; - readonly isUserIsNotAllowedToNest: boolean; - readonly isSourceCollectionIsNotAllowedToNest: boolean; - readonly isCollectionFieldSizeExceeded: boolean; - readonly isNoSpaceForProperty: boolean; - readonly isPropertyLimitReached: boolean; - readonly isPropertyKeyIsTooLong: boolean; - readonly isInvalidCharacterInPropertyKey: boolean; - readonly isEmptyPropertyKey: boolean; - readonly isCollectionIsExternal: boolean; - readonly isCollectionIsInternal: boolean; - readonly isConfirmSponsorshipFail: boolean; - readonly isUserIsNotCollectionAdmin: boolean; - readonly isFungibleItemsHaveNoId: boolean; - readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsNotEthMirror' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal' | 'ConfirmSponsorshipFail' | 'UserIsNotCollectionAdmin' | 'FungibleItemsHaveNoId'; -} - -/** @name PalletCommonEvent */ -export interface PalletCommonEvent extends Enum { - readonly isCollectionCreated: boolean; - readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>; - readonly isCollectionDestroyed: boolean; - readonly asCollectionDestroyed: u32; - readonly isItemCreated: boolean; - readonly asItemCreated: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; - readonly isItemDestroyed: boolean; - readonly asItemDestroyed: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; - readonly isTransfer: boolean; - readonly asTransfer: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; - readonly isApproved: boolean; - readonly asApproved: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; - readonly isApprovedForAll: boolean; - readonly asApprovedForAll: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>; - readonly isCollectionPropertySet: boolean; - readonly asCollectionPropertySet: ITuple<[u32, Bytes]>; - readonly isCollectionPropertyDeleted: boolean; - readonly asCollectionPropertyDeleted: ITuple<[u32, Bytes]>; - readonly isTokenPropertySet: boolean; - readonly asTokenPropertySet: ITuple<[u32, u32, Bytes]>; - readonly isTokenPropertyDeleted: boolean; - readonly asTokenPropertyDeleted: ITuple<[u32, u32, Bytes]>; - readonly isPropertyPermissionSet: boolean; - readonly asPropertyPermissionSet: ITuple<[u32, Bytes]>; - readonly isAllowListAddressAdded: boolean; - readonly asAllowListAddressAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - readonly isAllowListAddressRemoved: boolean; - readonly asAllowListAddressRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - readonly isCollectionAdminAdded: boolean; - readonly asCollectionAdminAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - readonly isCollectionAdminRemoved: boolean; - readonly asCollectionAdminRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - readonly isCollectionLimitSet: boolean; - readonly asCollectionLimitSet: u32; - readonly isCollectionOwnerChanged: boolean; - readonly asCollectionOwnerChanged: ITuple<[u32, AccountId32]>; - readonly isCollectionPermissionSet: boolean; - readonly asCollectionPermissionSet: u32; - readonly isCollectionSponsorSet: boolean; - readonly asCollectionSponsorSet: ITuple<[u32, AccountId32]>; - readonly isSponsorshipConfirmed: boolean; - readonly asSponsorshipConfirmed: ITuple<[u32, AccountId32]>; - readonly isCollectionSponsorRemoved: boolean; - readonly asCollectionSponsorRemoved: u32; - readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet' | 'AllowListAddressAdded' | 'AllowListAddressRemoved' | 'CollectionAdminAdded' | 'CollectionAdminRemoved' | 'CollectionLimitSet' | 'CollectionOwnerChanged' | 'CollectionPermissionSet' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionSponsorRemoved'; -} - -/** @name PalletConfigurationAppPromotionConfiguration */ -export interface PalletConfigurationAppPromotionConfiguration extends Struct { - readonly recalculationInterval: Option; - readonly pendingInterval: Option; - readonly intervalIncome: Option; - readonly maxStakersPerCalculation: Option; -} - -/** @name PalletConfigurationCall */ -export interface PalletConfigurationCall extends Enum { - readonly isSetWeightToFeeCoefficientOverride: boolean; - readonly asSetWeightToFeeCoefficientOverride: { - readonly coeff: Option; - } & Struct; - readonly isSetMinGasPriceOverride: boolean; - readonly asSetMinGasPriceOverride: { - readonly coeff: Option; - } & Struct; - readonly isSetAppPromotionConfigurationOverride: boolean; - readonly asSetAppPromotionConfigurationOverride: { - readonly configuration: PalletConfigurationAppPromotionConfiguration; - } & Struct; - readonly isSetCollatorSelectionDesiredCollators: boolean; - readonly asSetCollatorSelectionDesiredCollators: { - readonly max: Option; - } & Struct; - readonly isSetCollatorSelectionLicenseBond: boolean; - readonly asSetCollatorSelectionLicenseBond: { - readonly amount: Option; - } & Struct; - readonly isSetCollatorSelectionKickThreshold: boolean; - readonly asSetCollatorSelectionKickThreshold: { - readonly threshold: Option; - } & Struct; - readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetAppPromotionConfigurationOverride' | 'SetCollatorSelectionDesiredCollators' | 'SetCollatorSelectionLicenseBond' | 'SetCollatorSelectionKickThreshold'; -} - -/** @name PalletConfigurationError */ -export interface PalletConfigurationError extends Enum { - readonly isInconsistentConfiguration: boolean; - readonly type: 'InconsistentConfiguration'; -} - -/** @name PalletConfigurationEvent */ -export interface PalletConfigurationEvent extends Enum { - readonly isNewDesiredCollators: boolean; - readonly asNewDesiredCollators: { - readonly desiredCollators: Option; - } & Struct; - readonly isNewCollatorLicenseBond: boolean; - readonly asNewCollatorLicenseBond: { - readonly bondCost: Option; - } & Struct; - readonly isNewCollatorKickThreshold: boolean; - readonly asNewCollatorKickThreshold: { - readonly lengthInBlocks: Option; - } & Struct; - readonly type: 'NewDesiredCollators' | 'NewCollatorLicenseBond' | 'NewCollatorKickThreshold'; -} - -/** @name PalletDemocracyCall */ -export interface PalletDemocracyCall extends Enum { - readonly isPropose: boolean; - readonly asPropose: { - readonly proposal: FrameSupportPreimagesBounded; - readonly value: Compact; - } & Struct; - readonly isSecond: boolean; - readonly asSecond: { - readonly proposal: Compact; - } & Struct; - readonly isVote: boolean; - readonly asVote: { - readonly refIndex: Compact; - readonly vote: PalletDemocracyVoteAccountVote; - } & Struct; - readonly isEmergencyCancel: boolean; - readonly asEmergencyCancel: { - readonly refIndex: u32; - } & Struct; - readonly isExternalPropose: boolean; - readonly asExternalPropose: { - readonly proposal: FrameSupportPreimagesBounded; - } & Struct; - readonly isExternalProposeMajority: boolean; - readonly asExternalProposeMajority: { - readonly proposal: FrameSupportPreimagesBounded; - } & Struct; - readonly isExternalProposeDefault: boolean; - readonly asExternalProposeDefault: { - readonly proposal: FrameSupportPreimagesBounded; - } & Struct; - readonly isFastTrack: boolean; - readonly asFastTrack: { - readonly proposalHash: H256; - readonly votingPeriod: u32; - readonly delay: u32; - } & Struct; - readonly isVetoExternal: boolean; - readonly asVetoExternal: { - readonly proposalHash: H256; - } & Struct; - readonly isCancelReferendum: boolean; - readonly asCancelReferendum: { - readonly refIndex: Compact; - } & Struct; - readonly isDelegate: boolean; - readonly asDelegate: { - readonly to: MultiAddress; - readonly conviction: PalletDemocracyConviction; - readonly balance: u128; - } & Struct; - readonly isUndelegate: boolean; - readonly isClearPublicProposals: boolean; - readonly isUnlock: boolean; - readonly asUnlock: { - readonly target: MultiAddress; - } & Struct; - readonly isRemoveVote: boolean; - readonly asRemoveVote: { - readonly index: u32; - } & Struct; - readonly isRemoveOtherVote: boolean; - readonly asRemoveOtherVote: { - readonly target: MultiAddress; - readonly index: u32; - } & Struct; - readonly isBlacklist: boolean; - readonly asBlacklist: { - readonly proposalHash: H256; - readonly maybeRefIndex: Option; - } & Struct; - readonly isCancelProposal: boolean; - readonly asCancelProposal: { - readonly propIndex: Compact; - } & Struct; - readonly isSetMetadata: boolean; - readonly asSetMetadata: { - readonly owner: PalletDemocracyMetadataOwner; - readonly maybeHash: Option; - } & Struct; - readonly type: 'Propose' | 'Second' | 'Vote' | 'EmergencyCancel' | 'ExternalPropose' | 'ExternalProposeMajority' | 'ExternalProposeDefault' | 'FastTrack' | 'VetoExternal' | 'CancelReferendum' | 'Delegate' | 'Undelegate' | 'ClearPublicProposals' | 'Unlock' | 'RemoveVote' | 'RemoveOtherVote' | 'Blacklist' | 'CancelProposal' | 'SetMetadata'; -} - -/** @name PalletDemocracyConviction */ -export interface PalletDemocracyConviction extends Enum { - readonly isNone: boolean; - readonly isLocked1x: boolean; - readonly isLocked2x: boolean; - readonly isLocked3x: boolean; - readonly isLocked4x: boolean; - readonly isLocked5x: boolean; - readonly isLocked6x: boolean; - readonly type: 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x'; -} - -/** @name PalletDemocracyDelegations */ -export interface PalletDemocracyDelegations extends Struct { - readonly votes: u128; - readonly capital: u128; -} - -/** @name PalletDemocracyError */ -export interface PalletDemocracyError extends Enum { - readonly isValueLow: boolean; - readonly isProposalMissing: boolean; - readonly isAlreadyCanceled: boolean; - readonly isDuplicateProposal: boolean; - readonly isProposalBlacklisted: boolean; - readonly isNotSimpleMajority: boolean; - readonly isInvalidHash: boolean; - readonly isNoProposal: boolean; - readonly isAlreadyVetoed: boolean; - readonly isReferendumInvalid: boolean; - readonly isNoneWaiting: boolean; - readonly isNotVoter: boolean; - readonly isNoPermission: boolean; - readonly isAlreadyDelegating: boolean; - readonly isInsufficientFunds: boolean; - readonly isNotDelegating: boolean; - readonly isVotesExist: boolean; - readonly isInstantNotAllowed: boolean; - readonly isNonsense: boolean; - readonly isWrongUpperBound: boolean; - readonly isMaxVotesReached: boolean; - readonly isTooMany: boolean; - readonly isVotingPeriodLow: boolean; - readonly isPreimageNotExist: boolean; - readonly type: 'ValueLow' | 'ProposalMissing' | 'AlreadyCanceled' | 'DuplicateProposal' | 'ProposalBlacklisted' | 'NotSimpleMajority' | 'InvalidHash' | 'NoProposal' | 'AlreadyVetoed' | 'ReferendumInvalid' | 'NoneWaiting' | 'NotVoter' | 'NoPermission' | 'AlreadyDelegating' | 'InsufficientFunds' | 'NotDelegating' | 'VotesExist' | 'InstantNotAllowed' | 'Nonsense' | 'WrongUpperBound' | 'MaxVotesReached' | 'TooMany' | 'VotingPeriodLow' | 'PreimageNotExist'; -} - -/** @name PalletDemocracyEvent */ -export interface PalletDemocracyEvent extends Enum { - readonly isProposed: boolean; - readonly asProposed: { - readonly proposalIndex: u32; - readonly deposit: u128; - } & Struct; - readonly isTabled: boolean; - readonly asTabled: { - readonly proposalIndex: u32; - readonly deposit: u128; - } & Struct; - readonly isExternalTabled: boolean; - readonly isStarted: boolean; - readonly asStarted: { - readonly refIndex: u32; - readonly threshold: PalletDemocracyVoteThreshold; - } & Struct; - readonly isPassed: boolean; - readonly asPassed: { - readonly refIndex: u32; - } & Struct; - readonly isNotPassed: boolean; - readonly asNotPassed: { - readonly refIndex: u32; - } & Struct; - readonly isCancelled: boolean; - readonly asCancelled: { - readonly refIndex: u32; - } & Struct; - readonly isDelegated: boolean; - readonly asDelegated: { - readonly who: AccountId32; - readonly target: AccountId32; - } & Struct; - readonly isUndelegated: boolean; - readonly asUndelegated: { - readonly account: AccountId32; - } & Struct; - readonly isVetoed: boolean; - readonly asVetoed: { - readonly who: AccountId32; - readonly proposalHash: H256; - readonly until: u32; - } & Struct; - readonly isBlacklisted: boolean; - readonly asBlacklisted: { - readonly proposalHash: H256; - } & Struct; - readonly isVoted: boolean; - readonly asVoted: { - readonly voter: AccountId32; - readonly refIndex: u32; - readonly vote: PalletDemocracyVoteAccountVote; - } & Struct; - readonly isSeconded: boolean; - readonly asSeconded: { - readonly seconder: AccountId32; - readonly propIndex: u32; - } & Struct; - readonly isProposalCanceled: boolean; - readonly asProposalCanceled: { - readonly propIndex: u32; - } & Struct; - readonly isMetadataSet: boolean; - readonly asMetadataSet: { - readonly owner: PalletDemocracyMetadataOwner; - readonly hash_: H256; - } & Struct; - readonly isMetadataCleared: boolean; - readonly asMetadataCleared: { - readonly owner: PalletDemocracyMetadataOwner; - readonly hash_: H256; - } & Struct; - readonly isMetadataTransferred: boolean; - readonly asMetadataTransferred: { - readonly prevOwner: PalletDemocracyMetadataOwner; - readonly owner: PalletDemocracyMetadataOwner; - readonly hash_: H256; - } & Struct; - readonly type: 'Proposed' | 'Tabled' | 'ExternalTabled' | 'Started' | 'Passed' | 'NotPassed' | 'Cancelled' | 'Delegated' | 'Undelegated' | 'Vetoed' | 'Blacklisted' | 'Voted' | 'Seconded' | 'ProposalCanceled' | 'MetadataSet' | 'MetadataCleared' | 'MetadataTransferred'; -} - -/** @name PalletDemocracyMetadataOwner */ -export interface PalletDemocracyMetadataOwner extends Enum { - readonly isExternal: boolean; - readonly isProposal: boolean; - readonly asProposal: u32; - readonly isReferendum: boolean; - readonly asReferendum: u32; - readonly type: 'External' | 'Proposal' | 'Referendum'; -} - -/** @name PalletDemocracyReferendumInfo */ -export interface PalletDemocracyReferendumInfo extends Enum { - readonly isOngoing: boolean; - readonly asOngoing: PalletDemocracyReferendumStatus; - readonly isFinished: boolean; - readonly asFinished: { - readonly approved: bool; - readonly end: u32; - } & Struct; - readonly type: 'Ongoing' | 'Finished'; -} - -/** @name PalletDemocracyReferendumStatus */ -export interface PalletDemocracyReferendumStatus extends Struct { - readonly end: u32; - readonly proposal: FrameSupportPreimagesBounded; - readonly threshold: PalletDemocracyVoteThreshold; - readonly delay: u32; - readonly tally: PalletDemocracyTally; -} - -/** @name PalletDemocracyTally */ -export interface PalletDemocracyTally extends Struct { - readonly ayes: u128; - readonly nays: u128; - readonly turnout: u128; -} - -/** @name PalletDemocracyVoteAccountVote */ -export interface PalletDemocracyVoteAccountVote extends Enum { - readonly isStandard: boolean; - readonly asStandard: { - readonly vote: Vote; - readonly balance: u128; - } & Struct; - readonly isSplit: boolean; - readonly asSplit: { - readonly aye: u128; - readonly nay: u128; - } & Struct; - readonly type: 'Standard' | 'Split'; -} - -/** @name PalletDemocracyVotePriorLock */ -export interface PalletDemocracyVotePriorLock extends ITuple<[u32, u128]> {} - -/** @name PalletDemocracyVoteThreshold */ -export interface PalletDemocracyVoteThreshold extends Enum { - readonly isSuperMajorityApprove: boolean; - readonly isSuperMajorityAgainst: boolean; - readonly isSimpleMajority: boolean; - readonly type: 'SuperMajorityApprove' | 'SuperMajorityAgainst' | 'SimpleMajority'; -} - -/** @name PalletDemocracyVoteVoting */ -export interface PalletDemocracyVoteVoting extends Enum { - readonly isDirect: boolean; - readonly asDirect: { - readonly votes: Vec>; - readonly delegations: PalletDemocracyDelegations; - readonly prior: PalletDemocracyVotePriorLock; - } & Struct; - readonly isDelegating: boolean; - readonly asDelegating: { - readonly balance: u128; - readonly target: AccountId32; - readonly conviction: PalletDemocracyConviction; - readonly delegations: PalletDemocracyDelegations; - readonly prior: PalletDemocracyVotePriorLock; - } & Struct; - readonly type: 'Direct' | 'Delegating'; -} - -/** @name PalletEthereumCall */ -export interface PalletEthereumCall extends Enum { - readonly isTransact: boolean; - readonly asTransact: { - readonly transaction: EthereumTransactionTransactionV2; - } & Struct; - readonly type: 'Transact'; -} - -/** @name PalletEthereumError */ -export interface PalletEthereumError extends Enum { - readonly isInvalidSignature: boolean; - readonly isPreLogExists: boolean; - readonly type: 'InvalidSignature' | 'PreLogExists'; -} - -/** @name PalletEthereumEvent */ -export interface PalletEthereumEvent extends Enum { - readonly isExecuted: boolean; - readonly asExecuted: { - readonly from: H160; - readonly to: H160; - readonly transactionHash: H256; - readonly exitReason: EvmCoreErrorExitReason; - readonly extraData: Bytes; - } & Struct; - readonly type: 'Executed'; -} - -/** @name PalletEthereumFakeTransactionFinalizer */ -export interface PalletEthereumFakeTransactionFinalizer extends Null {} - -/** @name PalletEthereumRawOrigin */ -export interface PalletEthereumRawOrigin extends Enum { - readonly isEthereumTransaction: boolean; - readonly asEthereumTransaction: H160; - readonly type: 'EthereumTransaction'; -} - -/** @name PalletEvmAccountBasicCrossAccountIdRepr */ -export interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum { - readonly isSubstrate: boolean; - readonly asSubstrate: AccountId32; - readonly isEthereum: boolean; - readonly asEthereum: H160; - readonly type: 'Substrate' | 'Ethereum'; -} - -/** @name PalletEvmCall */ -export interface PalletEvmCall extends Enum { - readonly isWithdraw: boolean; - readonly asWithdraw: { - readonly address: H160; - readonly value: u128; - } & Struct; - readonly isCall: boolean; - readonly asCall: { - readonly source: H160; - readonly target: H160; - readonly input: Bytes; - readonly value: U256; - readonly gasLimit: u64; - readonly maxFeePerGas: U256; - readonly maxPriorityFeePerGas: Option; - readonly nonce: Option; - readonly accessList: Vec]>>; - } & Struct; - readonly isCreate: boolean; - readonly asCreate: { - readonly source: H160; - readonly init: Bytes; - readonly value: U256; - readonly gasLimit: u64; - readonly maxFeePerGas: U256; - readonly maxPriorityFeePerGas: Option; - readonly nonce: Option; - readonly accessList: Vec]>>; - } & Struct; - readonly isCreate2: boolean; - readonly asCreate2: { - readonly source: H160; - readonly init: Bytes; - readonly salt: H256; - readonly value: U256; - readonly gasLimit: u64; - readonly maxFeePerGas: U256; - readonly maxPriorityFeePerGas: Option; - readonly nonce: Option; - readonly accessList: Vec]>>; - } & Struct; - readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2'; -} - -/** @name PalletEvmCodeMetadata */ -export interface PalletEvmCodeMetadata extends Struct { - readonly size_: u64; - readonly hash_: H256; -} - -/** @name PalletEvmCoderSubstrateError */ -export interface PalletEvmCoderSubstrateError extends Enum { - readonly isOutOfGas: boolean; - readonly isOutOfFund: boolean; - readonly type: 'OutOfGas' | 'OutOfFund'; -} - -/** @name PalletEvmContractHelpersCall */ -export interface PalletEvmContractHelpersCall extends Enum { - readonly isMigrateFromSelfSponsoring: boolean; - readonly asMigrateFromSelfSponsoring: { - readonly addresses: Vec; - } & Struct; - readonly type: 'MigrateFromSelfSponsoring'; -} - -/** @name PalletEvmContractHelpersError */ -export interface PalletEvmContractHelpersError extends Enum { - readonly isNoPermission: boolean; - readonly isNoPendingSponsor: boolean; - readonly isTooManyMethodsHaveSponsoredLimit: boolean; - readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit'; -} - -/** @name PalletEvmContractHelpersEvent */ -export interface PalletEvmContractHelpersEvent extends Enum { - readonly isContractSponsorSet: boolean; - readonly asContractSponsorSet: ITuple<[H160, AccountId32]>; - readonly isContractSponsorshipConfirmed: boolean; - readonly asContractSponsorshipConfirmed: ITuple<[H160, AccountId32]>; - readonly isContractSponsorRemoved: boolean; - readonly asContractSponsorRemoved: H160; - readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved'; -} - -/** @name PalletEvmContractHelpersSponsoringModeT */ -export interface PalletEvmContractHelpersSponsoringModeT extends Enum { - readonly isDisabled: boolean; - readonly isAllowlisted: boolean; - readonly isGenerous: boolean; - readonly type: 'Disabled' | 'Allowlisted' | 'Generous'; -} - -/** @name PalletEvmError */ -export interface PalletEvmError extends Enum { - readonly isBalanceLow: boolean; - readonly isFeeOverflow: boolean; - readonly isPaymentOverflow: boolean; - readonly isWithdrawFailed: boolean; - readonly isGasPriceTooLow: boolean; - readonly isInvalidNonce: boolean; - readonly isGasLimitTooLow: boolean; - readonly isGasLimitTooHigh: boolean; - readonly isUndefined: boolean; - readonly isReentrancy: boolean; - readonly isTransactionMustComeFromEOA: boolean; - readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce' | 'GasLimitTooLow' | 'GasLimitTooHigh' | 'Undefined' | 'Reentrancy' | 'TransactionMustComeFromEOA'; -} - -/** @name PalletEvmEvent */ -export interface PalletEvmEvent extends Enum { - readonly isLog: boolean; - readonly asLog: { - readonly log: EthereumLog; - } & Struct; - readonly isCreated: boolean; - readonly asCreated: { - readonly address: H160; - } & Struct; - readonly isCreatedFailed: boolean; - readonly asCreatedFailed: { - readonly address: H160; - } & Struct; - readonly isExecuted: boolean; - readonly asExecuted: { - readonly address: H160; - } & Struct; - readonly isExecutedFailed: boolean; - readonly asExecutedFailed: { - readonly address: H160; - } & Struct; - readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed'; -} - -/** @name PalletEvmMigrationCall */ -export interface PalletEvmMigrationCall extends Enum { - readonly isBegin: boolean; - readonly asBegin: { - readonly address: H160; - } & Struct; - readonly isSetData: boolean; - readonly asSetData: { - readonly address: H160; - readonly data: Vec>; - } & Struct; - readonly isFinish: boolean; - readonly asFinish: { - readonly address: H160; - readonly code: Bytes; - } & Struct; - readonly isInsertEthLogs: boolean; - readonly asInsertEthLogs: { - readonly logs: Vec; - } & Struct; - readonly isInsertEvents: boolean; - readonly asInsertEvents: { - readonly events: Vec; - } & Struct; - readonly isRemoveRmrkData: boolean; - readonly type: 'Begin' | 'SetData' | 'Finish' | 'InsertEthLogs' | 'InsertEvents' | 'RemoveRmrkData'; -} - -/** @name PalletEvmMigrationError */ -export interface PalletEvmMigrationError extends Enum { - readonly isAccountNotEmpty: boolean; - readonly isAccountIsNotMigrating: boolean; - readonly isBadEvent: boolean; - readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating' | 'BadEvent'; -} - -/** @name PalletEvmMigrationEvent */ -export interface PalletEvmMigrationEvent extends Enum { - readonly isTestEvent: boolean; - readonly type: 'TestEvent'; -} - -/** @name PalletForeignAssetsAssetId */ -export interface PalletForeignAssetsAssetId extends Enum { - readonly isForeignAssetId: boolean; - readonly asForeignAssetId: u32; - readonly isNativeAssetId: boolean; - readonly asNativeAssetId: PalletForeignAssetsNativeCurrency; - readonly type: 'ForeignAssetId' | 'NativeAssetId'; -} - -/** @name PalletForeignAssetsModuleAssetMetadata */ -export interface PalletForeignAssetsModuleAssetMetadata extends Struct { - readonly name: Bytes; - readonly symbol: Bytes; - readonly decimals: u8; - readonly minimalBalance: u128; -} - -/** @name PalletForeignAssetsModuleCall */ -export interface PalletForeignAssetsModuleCall extends Enum { - readonly isRegisterForeignAsset: boolean; - readonly asRegisterForeignAsset: { - readonly owner: AccountId32; - readonly location: StagingXcmVersionedMultiLocation; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly isUpdateForeignAsset: boolean; - readonly asUpdateForeignAsset: { - readonly foreignAssetId: u32; - readonly location: StagingXcmVersionedMultiLocation; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset'; -} - -/** @name PalletForeignAssetsModuleError */ -export interface PalletForeignAssetsModuleError extends Enum { - readonly isBadLocation: boolean; - readonly isMultiLocationExisted: boolean; - readonly isAssetIdNotExists: boolean; - readonly isAssetIdExisted: boolean; - readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted'; -} - -/** @name PalletForeignAssetsModuleEvent */ -export interface PalletForeignAssetsModuleEvent extends Enum { - readonly isForeignAssetRegistered: boolean; - readonly asForeignAssetRegistered: { - readonly assetId: u32; - readonly assetAddress: StagingXcmV3MultiLocation; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly isForeignAssetUpdated: boolean; - readonly asForeignAssetUpdated: { - readonly assetId: u32; - readonly assetAddress: StagingXcmV3MultiLocation; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly isAssetRegistered: boolean; - readonly asAssetRegistered: { - readonly assetId: PalletForeignAssetsAssetId; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly isAssetUpdated: boolean; - readonly asAssetUpdated: { - readonly assetId: PalletForeignAssetsAssetId; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated'; -} - -/** @name PalletForeignAssetsNativeCurrency */ -export interface PalletForeignAssetsNativeCurrency extends Enum { - readonly isHere: boolean; - readonly isParent: boolean; - readonly type: 'Here' | 'Parent'; -} - -/** @name PalletFungibleError */ -export interface PalletFungibleError extends Enum { - readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean; - readonly isFungibleItemsDontHaveData: boolean; - readonly isFungibleDisallowsNesting: boolean; - readonly isSettingPropertiesNotAllowed: boolean; - readonly isSettingAllowanceForAllNotAllowed: boolean; - readonly isFungibleTokensAreAlwaysValid: boolean; - readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingAllowanceForAllNotAllowed' | 'FungibleTokensAreAlwaysValid'; -} - -/** @name PalletGovOriginsOrigin */ -export interface PalletGovOriginsOrigin extends Enum { - readonly isFellowshipProposition: boolean; - readonly type: 'FellowshipProposition'; -} - -/** @name PalletIdentityBitFlags */ -export interface PalletIdentityBitFlags extends Struct { - readonly _bitLength: 64; - readonly Display: 1; - readonly Legal: 2; - readonly Web: 4; - readonly Riot: 8; - readonly Email: 16; - readonly PgpFingerprint: 32; - readonly Image: 64; - readonly Twitter: 128; -} - -/** @name PalletIdentityCall */ -export interface PalletIdentityCall extends Enum { - readonly isAddRegistrar: boolean; - readonly asAddRegistrar: { - readonly account: MultiAddress; - } & Struct; - readonly isSetIdentity: boolean; - readonly asSetIdentity: { - readonly info: PalletIdentityIdentityInfo; - } & Struct; - readonly isSetSubs: boolean; - readonly asSetSubs: { - readonly subs: Vec>; - } & Struct; - readonly isClearIdentity: boolean; - readonly isRequestJudgement: boolean; - readonly asRequestJudgement: { - readonly regIndex: Compact; - readonly maxFee: Compact; - } & Struct; - readonly isCancelRequest: boolean; - readonly asCancelRequest: { - readonly regIndex: u32; - } & Struct; - readonly isSetFee: boolean; - readonly asSetFee: { - readonly index: Compact; - readonly fee: Compact; - } & Struct; - readonly isSetAccountId: boolean; - readonly asSetAccountId: { - readonly index: Compact; - readonly new_: MultiAddress; - } & Struct; - readonly isSetFields: boolean; - readonly asSetFields: { - readonly index: Compact; - readonly fields: PalletIdentityBitFlags; - } & Struct; - readonly isProvideJudgement: boolean; - readonly asProvideJudgement: { - readonly regIndex: Compact; - readonly target: MultiAddress; - readonly judgement: PalletIdentityJudgement; - readonly identity: H256; - } & Struct; - readonly isKillIdentity: boolean; - readonly asKillIdentity: { - readonly target: MultiAddress; - } & Struct; - readonly isAddSub: boolean; - readonly asAddSub: { - readonly sub: MultiAddress; - readonly data: Data; - } & Struct; - readonly isRenameSub: boolean; - readonly asRenameSub: { - readonly sub: MultiAddress; - readonly data: Data; - } & Struct; - readonly isRemoveSub: boolean; - readonly asRemoveSub: { - readonly sub: MultiAddress; - } & Struct; - readonly isQuitSub: boolean; - readonly isForceInsertIdentities: boolean; - readonly asForceInsertIdentities: { - readonly identities: Vec>; - } & Struct; - readonly isForceRemoveIdentities: boolean; - readonly asForceRemoveIdentities: { - readonly identities: Vec; - } & Struct; - readonly isForceSetSubs: boolean; - readonly asForceSetSubs: { - readonly subs: Vec>]>]>>; - } & Struct; - readonly type: 'AddRegistrar' | 'SetIdentity' | 'SetSubs' | 'ClearIdentity' | 'RequestJudgement' | 'CancelRequest' | 'SetFee' | 'SetAccountId' | 'SetFields' | 'ProvideJudgement' | 'KillIdentity' | 'AddSub' | 'RenameSub' | 'RemoveSub' | 'QuitSub' | 'ForceInsertIdentities' | 'ForceRemoveIdentities' | 'ForceSetSubs'; -} - -/** @name PalletIdentityError */ -export interface PalletIdentityError extends Enum { - readonly isTooManySubAccounts: boolean; - readonly isNotFound: boolean; - readonly isNotNamed: boolean; - readonly isEmptyIndex: boolean; - readonly isFeeChanged: boolean; - readonly isNoIdentity: boolean; - readonly isStickyJudgement: boolean; - readonly isJudgementGiven: boolean; - readonly isInvalidJudgement: boolean; - readonly isInvalidIndex: boolean; - readonly isInvalidTarget: boolean; - readonly isTooManyFields: boolean; - readonly isTooManyRegistrars: boolean; - readonly isAlreadyClaimed: boolean; - readonly isNotSub: boolean; - readonly isNotOwned: boolean; - readonly isJudgementForDifferentIdentity: boolean; - readonly isJudgementPaymentFailed: boolean; - readonly type: 'TooManySubAccounts' | 'NotFound' | 'NotNamed' | 'EmptyIndex' | 'FeeChanged' | 'NoIdentity' | 'StickyJudgement' | 'JudgementGiven' | 'InvalidJudgement' | 'InvalidIndex' | 'InvalidTarget' | 'TooManyFields' | 'TooManyRegistrars' | 'AlreadyClaimed' | 'NotSub' | 'NotOwned' | 'JudgementForDifferentIdentity' | 'JudgementPaymentFailed'; -} - -/** @name PalletIdentityEvent */ -export interface PalletIdentityEvent extends Enum { - readonly isIdentitySet: boolean; - readonly asIdentitySet: { - readonly who: AccountId32; - } & Struct; - readonly isIdentityCleared: boolean; - readonly asIdentityCleared: { - readonly who: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isIdentityKilled: boolean; - readonly asIdentityKilled: { - readonly who: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isIdentitiesInserted: boolean; - readonly asIdentitiesInserted: { - readonly amount: u32; - } & Struct; - readonly isIdentitiesRemoved: boolean; - readonly asIdentitiesRemoved: { - readonly amount: u32; - } & Struct; - readonly isJudgementRequested: boolean; - readonly asJudgementRequested: { - readonly who: AccountId32; - readonly registrarIndex: u32; - } & Struct; - readonly isJudgementUnrequested: boolean; - readonly asJudgementUnrequested: { - readonly who: AccountId32; - readonly registrarIndex: u32; - } & Struct; - readonly isJudgementGiven: boolean; - readonly asJudgementGiven: { - readonly target: AccountId32; - readonly registrarIndex: u32; - } & Struct; - readonly isRegistrarAdded: boolean; - readonly asRegistrarAdded: { - readonly registrarIndex: u32; - } & Struct; - readonly isSubIdentityAdded: boolean; - readonly asSubIdentityAdded: { - readonly sub: AccountId32; - readonly main: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isSubIdentityRemoved: boolean; - readonly asSubIdentityRemoved: { - readonly sub: AccountId32; - readonly main: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isSubIdentityRevoked: boolean; - readonly asSubIdentityRevoked: { - readonly sub: AccountId32; - readonly main: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isSubIdentitiesInserted: boolean; - readonly asSubIdentitiesInserted: { - readonly amount: u32; - } & Struct; - readonly type: 'IdentitySet' | 'IdentityCleared' | 'IdentityKilled' | 'IdentitiesInserted' | 'IdentitiesRemoved' | 'JudgementRequested' | 'JudgementUnrequested' | 'JudgementGiven' | 'RegistrarAdded' | 'SubIdentityAdded' | 'SubIdentityRemoved' | 'SubIdentityRevoked' | 'SubIdentitiesInserted'; -} - -/** @name PalletIdentityIdentityField */ -export interface PalletIdentityIdentityField extends Enum { - readonly isDisplay: boolean; - readonly isLegal: boolean; - readonly isWeb: boolean; - readonly isRiot: boolean; - readonly isEmail: boolean; - readonly isPgpFingerprint: boolean; - readonly isImage: boolean; - readonly isTwitter: boolean; - readonly type: 'Display' | 'Legal' | 'Web' | 'Riot' | 'Email' | 'PgpFingerprint' | 'Image' | 'Twitter'; -} - -/** @name PalletIdentityIdentityInfo */ -export interface PalletIdentityIdentityInfo extends Struct { - readonly additional: Vec>; - readonly display: Data; - readonly legal: Data; - readonly web: Data; - readonly riot: Data; - readonly email: Data; - readonly pgpFingerprint: Option; - readonly image: Data; - readonly twitter: Data; -} - -/** @name PalletIdentityJudgement */ -export interface PalletIdentityJudgement extends Enum { - readonly isUnknown: boolean; - readonly isFeePaid: boolean; - readonly asFeePaid: u128; - readonly isReasonable: boolean; - readonly isKnownGood: boolean; - readonly isOutOfDate: boolean; - readonly isLowQuality: boolean; - readonly isErroneous: boolean; - readonly type: 'Unknown' | 'FeePaid' | 'Reasonable' | 'KnownGood' | 'OutOfDate' | 'LowQuality' | 'Erroneous'; -} - -/** @name PalletIdentityRegistrarInfo */ -export interface PalletIdentityRegistrarInfo extends Struct { - readonly account: AccountId32; - readonly fee: u128; - readonly fields: PalletIdentityBitFlags; -} - -/** @name PalletIdentityRegistration */ -export interface PalletIdentityRegistration extends Struct { - readonly judgements: Vec>; - readonly deposit: u128; - readonly info: PalletIdentityIdentityInfo; -} - -/** @name PalletInflationCall */ -export interface PalletInflationCall extends Enum { - readonly isStartInflation: boolean; - readonly asStartInflation: { - readonly inflationStartRelayBlock: u32; - } & Struct; - readonly type: 'StartInflation'; -} - -/** @name PalletMaintenanceCall */ -export interface PalletMaintenanceCall extends Enum { - readonly isEnable: boolean; - readonly isDisable: boolean; - readonly type: 'Enable' | 'Disable'; -} - -/** @name PalletMaintenanceError */ -export interface PalletMaintenanceError extends Null {} - -/** @name PalletMaintenanceEvent */ -export interface PalletMaintenanceEvent extends Enum { - readonly isMaintenanceEnabled: boolean; - readonly isMaintenanceDisabled: boolean; - readonly type: 'MaintenanceEnabled' | 'MaintenanceDisabled'; -} - -/** @name PalletMembershipCall */ -export interface PalletMembershipCall extends Enum { - readonly isAddMember: boolean; - readonly asAddMember: { - readonly who: MultiAddress; - } & Struct; - readonly isRemoveMember: boolean; - readonly asRemoveMember: { - readonly who: MultiAddress; - } & Struct; - readonly isSwapMember: boolean; - readonly asSwapMember: { - readonly remove: MultiAddress; - readonly add: MultiAddress; - } & Struct; - readonly isResetMembers: boolean; - readonly asResetMembers: { - readonly members: Vec; - } & Struct; - readonly isChangeKey: boolean; - readonly asChangeKey: { - readonly new_: MultiAddress; - } & Struct; - readonly isSetPrime: boolean; - readonly asSetPrime: { - readonly who: MultiAddress; - } & Struct; - readonly isClearPrime: boolean; - readonly type: 'AddMember' | 'RemoveMember' | 'SwapMember' | 'ResetMembers' | 'ChangeKey' | 'SetPrime' | 'ClearPrime'; -} - -/** @name PalletMembershipError */ -export interface PalletMembershipError extends Enum { - readonly isAlreadyMember: boolean; - readonly isNotMember: boolean; - readonly isTooManyMembers: boolean; - readonly type: 'AlreadyMember' | 'NotMember' | 'TooManyMembers'; -} - -/** @name PalletMembershipEvent */ -export interface PalletMembershipEvent extends Enum { - readonly isMemberAdded: boolean; - readonly isMemberRemoved: boolean; - readonly isMembersSwapped: boolean; - readonly isMembersReset: boolean; - readonly isKeyChanged: boolean; - readonly isDummy: boolean; - readonly type: 'MemberAdded' | 'MemberRemoved' | 'MembersSwapped' | 'MembersReset' | 'KeyChanged' | 'Dummy'; -} - -/** @name PalletNonfungibleError */ -export interface PalletNonfungibleError extends Enum { - readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean; - readonly isNonfungibleItemsHaveNoAmount: boolean; - readonly isCantBurnNftWithChildren: boolean; - readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren'; -} - -/** @name PalletNonfungibleItemData */ -export interface PalletNonfungibleItemData extends Struct { - readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; -} - -/** @name PalletPreimageCall */ -export interface PalletPreimageCall extends Enum { - readonly isNotePreimage: boolean; - readonly asNotePreimage: { - readonly bytes: Bytes; - } & Struct; - readonly isUnnotePreimage: boolean; - readonly asUnnotePreimage: { - readonly hash_: H256; - } & Struct; - readonly isRequestPreimage: boolean; - readonly asRequestPreimage: { - readonly hash_: H256; - } & Struct; - readonly isUnrequestPreimage: boolean; - readonly asUnrequestPreimage: { - readonly hash_: H256; - } & Struct; - readonly type: 'NotePreimage' | 'UnnotePreimage' | 'RequestPreimage' | 'UnrequestPreimage'; -} - -/** @name PalletPreimageError */ -export interface PalletPreimageError extends Enum { - readonly isTooBig: boolean; - readonly isAlreadyNoted: boolean; - readonly isNotAuthorized: boolean; - readonly isNotNoted: boolean; - readonly isRequested: boolean; - readonly isNotRequested: boolean; - readonly type: 'TooBig' | 'AlreadyNoted' | 'NotAuthorized' | 'NotNoted' | 'Requested' | 'NotRequested'; -} - -/** @name PalletPreimageEvent */ -export interface PalletPreimageEvent extends Enum { - readonly isNoted: boolean; - readonly asNoted: { - readonly hash_: H256; - } & Struct; - readonly isRequested: boolean; - readonly asRequested: { - readonly hash_: H256; - } & Struct; - readonly isCleared: boolean; - readonly asCleared: { - readonly hash_: H256; - } & Struct; - readonly type: 'Noted' | 'Requested' | 'Cleared'; -} - -/** @name PalletPreimageRequestStatus */ -export interface PalletPreimageRequestStatus extends Enum { - readonly isUnrequested: boolean; - readonly asUnrequested: { - readonly deposit: ITuple<[AccountId32, u128]>; - readonly len: u32; - } & Struct; - readonly isRequested: boolean; - readonly asRequested: { - readonly deposit: Option>; - readonly count: u32; - readonly len: Option; - } & Struct; - readonly type: 'Unrequested' | 'Requested'; -} - -/** @name PalletRankedCollectiveCall */ -export interface PalletRankedCollectiveCall extends Enum { - readonly isAddMember: boolean; - readonly asAddMember: { - readonly who: MultiAddress; - } & Struct; - readonly isPromoteMember: boolean; - readonly asPromoteMember: { - readonly who: MultiAddress; - } & Struct; - readonly isDemoteMember: boolean; - readonly asDemoteMember: { - readonly who: MultiAddress; - } & Struct; - readonly isRemoveMember: boolean; - readonly asRemoveMember: { - readonly who: MultiAddress; - readonly minRank: u16; - } & Struct; - readonly isVote: boolean; - readonly asVote: { - readonly poll: u32; - readonly aye: bool; - } & Struct; - readonly isCleanupPoll: boolean; - readonly asCleanupPoll: { - readonly pollIndex: u32; - readonly max: u32; - } & Struct; - readonly type: 'AddMember' | 'PromoteMember' | 'DemoteMember' | 'RemoveMember' | 'Vote' | 'CleanupPoll'; -} - -/** @name PalletRankedCollectiveError */ -export interface PalletRankedCollectiveError extends Enum { - readonly isAlreadyMember: boolean; - readonly isNotMember: boolean; - readonly isNotPolling: boolean; - readonly isOngoing: boolean; - readonly isNoneRemaining: boolean; - readonly isCorruption: boolean; - readonly isRankTooLow: boolean; - readonly isInvalidWitness: boolean; - readonly isNoPermission: boolean; - readonly type: 'AlreadyMember' | 'NotMember' | 'NotPolling' | 'Ongoing' | 'NoneRemaining' | 'Corruption' | 'RankTooLow' | 'InvalidWitness' | 'NoPermission'; -} - -/** @name PalletRankedCollectiveEvent */ -export interface PalletRankedCollectiveEvent extends Enum { - readonly isMemberAdded: boolean; - readonly asMemberAdded: { - readonly who: AccountId32; - } & Struct; - readonly isRankChanged: boolean; - readonly asRankChanged: { - readonly who: AccountId32; - readonly rank: u16; - } & Struct; - readonly isMemberRemoved: boolean; - readonly asMemberRemoved: { - readonly who: AccountId32; - readonly rank: u16; - } & Struct; - readonly isVoted: boolean; - readonly asVoted: { - readonly who: AccountId32; - readonly poll: u32; - readonly vote: PalletRankedCollectiveVoteRecord; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly type: 'MemberAdded' | 'RankChanged' | 'MemberRemoved' | 'Voted'; -} - -/** @name PalletRankedCollectiveMemberRecord */ -export interface PalletRankedCollectiveMemberRecord extends Struct { - readonly rank: u16; -} - -/** @name PalletRankedCollectiveTally */ -export interface PalletRankedCollectiveTally extends Struct { - readonly bareAyes: u32; - readonly ayes: u32; - readonly nays: u32; -} - -/** @name PalletRankedCollectiveVoteRecord */ -export interface PalletRankedCollectiveVoteRecord extends Enum { - readonly isAye: boolean; - readonly asAye: u32; - readonly isNay: boolean; - readonly asNay: u32; - readonly type: 'Aye' | 'Nay'; -} - -/** @name PalletReferendaCall */ -export interface PalletReferendaCall extends Enum { - readonly isSubmit: boolean; - readonly asSubmit: { - readonly proposalOrigin: OpalRuntimeOriginCaller; - readonly proposal: FrameSupportPreimagesBounded; - readonly enactmentMoment: FrameSupportScheduleDispatchTime; - } & Struct; - readonly isPlaceDecisionDeposit: boolean; - readonly asPlaceDecisionDeposit: { - readonly index: u32; - } & Struct; - readonly isRefundDecisionDeposit: boolean; - readonly asRefundDecisionDeposit: { - readonly index: u32; - } & Struct; - readonly isCancel: boolean; - readonly asCancel: { - readonly index: u32; - } & Struct; - readonly isKill: boolean; - readonly asKill: { - readonly index: u32; - } & Struct; - readonly isNudgeReferendum: boolean; - readonly asNudgeReferendum: { - readonly index: u32; - } & Struct; - readonly isOneFewerDeciding: boolean; - readonly asOneFewerDeciding: { - readonly track: u16; - } & Struct; - readonly isRefundSubmissionDeposit: boolean; - readonly asRefundSubmissionDeposit: { - readonly index: u32; - } & Struct; - readonly isSetMetadata: boolean; - readonly asSetMetadata: { - readonly index: u32; - readonly maybeHash: Option; - } & Struct; - readonly type: 'Submit' | 'PlaceDecisionDeposit' | 'RefundDecisionDeposit' | 'Cancel' | 'Kill' | 'NudgeReferendum' | 'OneFewerDeciding' | 'RefundSubmissionDeposit' | 'SetMetadata'; -} - -/** @name PalletReferendaCurve */ -export interface PalletReferendaCurve extends Enum { - readonly isLinearDecreasing: boolean; - readonly asLinearDecreasing: { - readonly length: Perbill; - readonly floor: Perbill; - readonly ceil: Perbill; - } & Struct; - readonly isSteppedDecreasing: boolean; - readonly asSteppedDecreasing: { - readonly begin: Perbill; - readonly end: Perbill; - readonly step: Perbill; - readonly period: Perbill; - } & Struct; - readonly isReciprocal: boolean; - readonly asReciprocal: { - readonly factor: i64; - readonly xOffset: i64; - readonly yOffset: i64; - } & Struct; - readonly type: 'LinearDecreasing' | 'SteppedDecreasing' | 'Reciprocal'; -} - -/** @name PalletReferendaDecidingStatus */ -export interface PalletReferendaDecidingStatus extends Struct { - readonly since: u32; - readonly confirming: Option; -} - -/** @name PalletReferendaDeposit */ -export interface PalletReferendaDeposit extends Struct { - readonly who: AccountId32; - readonly amount: u128; -} - -/** @name PalletReferendaError */ -export interface PalletReferendaError extends Enum { - readonly isNotOngoing: boolean; - readonly isHasDeposit: boolean; - readonly isBadTrack: boolean; - readonly isFull: boolean; - readonly isQueueEmpty: boolean; - readonly isBadReferendum: boolean; - readonly isNothingToDo: boolean; - readonly isNoTrack: boolean; - readonly isUnfinished: boolean; - readonly isNoPermission: boolean; - readonly isNoDeposit: boolean; - readonly isBadStatus: boolean; - readonly isPreimageNotExist: boolean; - readonly type: 'NotOngoing' | 'HasDeposit' | 'BadTrack' | 'Full' | 'QueueEmpty' | 'BadReferendum' | 'NothingToDo' | 'NoTrack' | 'Unfinished' | 'NoPermission' | 'NoDeposit' | 'BadStatus' | 'PreimageNotExist'; -} - -/** @name PalletReferendaEvent */ -export interface PalletReferendaEvent extends Enum { - readonly isSubmitted: boolean; - readonly asSubmitted: { - readonly index: u32; - readonly track: u16; - readonly proposal: FrameSupportPreimagesBounded; - } & Struct; - readonly isDecisionDepositPlaced: boolean; - readonly asDecisionDepositPlaced: { - readonly index: u32; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isDecisionDepositRefunded: boolean; - readonly asDecisionDepositRefunded: { - readonly index: u32; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isDepositSlashed: boolean; - readonly asDepositSlashed: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isDecisionStarted: boolean; - readonly asDecisionStarted: { - readonly index: u32; - readonly track: u16; - readonly proposal: FrameSupportPreimagesBounded; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isConfirmStarted: boolean; - readonly asConfirmStarted: { - readonly index: u32; - } & Struct; - readonly isConfirmAborted: boolean; - readonly asConfirmAborted: { - readonly index: u32; - } & Struct; - readonly isConfirmed: boolean; - readonly asConfirmed: { - readonly index: u32; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isApproved: boolean; - readonly asApproved: { - readonly index: u32; - } & Struct; - readonly isRejected: boolean; - readonly asRejected: { - readonly index: u32; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isTimedOut: boolean; - readonly asTimedOut: { - readonly index: u32; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isCancelled: boolean; - readonly asCancelled: { - readonly index: u32; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isKilled: boolean; - readonly asKilled: { - readonly index: u32; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isSubmissionDepositRefunded: boolean; - readonly asSubmissionDepositRefunded: { - readonly index: u32; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isMetadataSet: boolean; - readonly asMetadataSet: { - readonly index: u32; - readonly hash_: H256; - } & Struct; - readonly isMetadataCleared: boolean; - readonly asMetadataCleared: { - readonly index: u32; - readonly hash_: H256; - } & Struct; - readonly type: 'Submitted' | 'DecisionDepositPlaced' | 'DecisionDepositRefunded' | 'DepositSlashed' | 'DecisionStarted' | 'ConfirmStarted' | 'ConfirmAborted' | 'Confirmed' | 'Approved' | 'Rejected' | 'TimedOut' | 'Cancelled' | 'Killed' | 'SubmissionDepositRefunded' | 'MetadataSet' | 'MetadataCleared'; -} - -/** @name PalletReferendaReferendumInfo */ -export interface PalletReferendaReferendumInfo extends Enum { - readonly isOngoing: boolean; - readonly asOngoing: PalletReferendaReferendumStatus; - readonly isApproved: boolean; - readonly asApproved: ITuple<[u32, Option, Option]>; - readonly isRejected: boolean; - readonly asRejected: ITuple<[u32, Option, Option]>; - readonly isCancelled: boolean; - readonly asCancelled: ITuple<[u32, Option, Option]>; - readonly isTimedOut: boolean; - readonly asTimedOut: ITuple<[u32, Option, Option]>; - readonly isKilled: boolean; - readonly asKilled: u32; - readonly type: 'Ongoing' | 'Approved' | 'Rejected' | 'Cancelled' | 'TimedOut' | 'Killed'; -} - -/** @name PalletReferendaReferendumStatus */ -export interface PalletReferendaReferendumStatus extends Struct { - readonly track: u16; - readonly origin: OpalRuntimeOriginCaller; - readonly proposal: FrameSupportPreimagesBounded; - readonly enactment: FrameSupportScheduleDispatchTime; - readonly submitted: u32; - readonly submissionDeposit: PalletReferendaDeposit; - readonly decisionDeposit: Option; - readonly deciding: Option; - readonly tally: PalletRankedCollectiveTally; - readonly inQueue: bool; - readonly alarm: Option]>>; -} - -/** @name PalletReferendaTrackInfo */ -export interface PalletReferendaTrackInfo extends Struct { - readonly name: Text; - readonly maxDeciding: u32; - readonly decisionDeposit: u128; - readonly preparePeriod: u32; - readonly decisionPeriod: u32; - readonly confirmPeriod: u32; - readonly minEnactmentPeriod: u32; - readonly minApproval: PalletReferendaCurve; - readonly minSupport: PalletReferendaCurve; -} - -/** @name PalletRefungibleError */ -export interface PalletRefungibleError extends Enum { - readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean; - readonly isWrongRefungiblePieces: boolean; - readonly isRepartitionWhileNotOwningAllPieces: boolean; - readonly isRefungibleDisallowsNesting: boolean; - readonly isSettingPropertiesNotAllowed: boolean; - readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; -} - -/** @name PalletSchedulerCall */ -export interface PalletSchedulerCall extends Enum { - readonly isSchedule: boolean; - readonly asSchedule: { - readonly when: u32; - readonly maybePeriodic: Option>; - readonly priority: u8; - readonly call: Call; - } & Struct; - readonly isCancel: boolean; - readonly asCancel: { - readonly when: u32; - readonly index: u32; - } & Struct; - readonly isScheduleNamed: boolean; - readonly asScheduleNamed: { - readonly id: U8aFixed; - readonly when: u32; - readonly maybePeriodic: Option>; - readonly priority: u8; - readonly call: Call; - } & Struct; - readonly isCancelNamed: boolean; - readonly asCancelNamed: { - readonly id: U8aFixed; - } & Struct; - readonly isScheduleAfter: boolean; - readonly asScheduleAfter: { - readonly after: u32; - readonly maybePeriodic: Option>; - readonly priority: u8; - readonly call: Call; - } & Struct; - readonly isScheduleNamedAfter: boolean; - readonly asScheduleNamedAfter: { - readonly id: U8aFixed; - readonly after: u32; - readonly maybePeriodic: Option>; - readonly priority: u8; - readonly call: Call; - } & Struct; - readonly type: 'Schedule' | 'Cancel' | 'ScheduleNamed' | 'CancelNamed' | 'ScheduleAfter' | 'ScheduleNamedAfter'; -} - -/** @name PalletSchedulerError */ -export interface PalletSchedulerError extends Enum { - readonly isFailedToSchedule: boolean; - readonly isNotFound: boolean; - readonly isTargetBlockNumberInPast: boolean; - readonly isRescheduleNoChange: boolean; - readonly isNamed: boolean; - readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange' | 'Named'; -} - -/** @name PalletSchedulerEvent */ -export interface PalletSchedulerEvent extends Enum { - readonly isScheduled: boolean; - readonly asScheduled: { - readonly when: u32; - readonly index: u32; - } & Struct; - readonly isCanceled: boolean; - readonly asCanceled: { - readonly when: u32; - readonly index: u32; - } & Struct; - readonly isDispatched: boolean; - readonly asDispatched: { - readonly task: ITuple<[u32, u32]>; - readonly id: Option; - readonly result: Result; - } & Struct; - readonly isCallUnavailable: boolean; - readonly asCallUnavailable: { - readonly task: ITuple<[u32, u32]>; - readonly id: Option; - } & Struct; - readonly isPeriodicFailed: boolean; - readonly asPeriodicFailed: { - readonly task: ITuple<[u32, u32]>; - readonly id: Option; - } & Struct; - readonly isPermanentlyOverweight: boolean; - readonly asPermanentlyOverweight: { - readonly task: ITuple<[u32, u32]>; - readonly id: Option; - } & Struct; - readonly type: 'Scheduled' | 'Canceled' | 'Dispatched' | 'CallUnavailable' | 'PeriodicFailed' | 'PermanentlyOverweight'; -} - -/** @name PalletSchedulerScheduled */ -export interface PalletSchedulerScheduled extends Struct { - readonly maybeId: Option; - readonly priority: u8; - readonly call: FrameSupportPreimagesBounded; - readonly maybePeriodic: Option>; - readonly origin: OpalRuntimeOriginCaller; -} - -/** @name PalletSessionCall */ -export interface PalletSessionCall extends Enum { - readonly isSetKeys: boolean; - readonly asSetKeys: { - readonly keys_: OpalRuntimeRuntimeCommonSessionKeys; - readonly proof: Bytes; - } & Struct; - readonly isPurgeKeys: boolean; - readonly type: 'SetKeys' | 'PurgeKeys'; -} - -/** @name PalletSessionError */ -export interface PalletSessionError extends Enum { - readonly isInvalidProof: boolean; - readonly isNoAssociatedValidatorId: boolean; - readonly isDuplicatedKey: boolean; - readonly isNoKeys: boolean; - readonly isNoAccount: boolean; - readonly type: 'InvalidProof' | 'NoAssociatedValidatorId' | 'DuplicatedKey' | 'NoKeys' | 'NoAccount'; -} - -/** @name PalletSessionEvent */ -export interface PalletSessionEvent extends Enum { - readonly isNewSession: boolean; - readonly asNewSession: { - readonly sessionIndex: u32; - } & Struct; - readonly type: 'NewSession'; -} - -/** @name PalletStateTrieMigrationCall */ -export interface PalletStateTrieMigrationCall extends Enum { - readonly isControlAutoMigration: boolean; - readonly asControlAutoMigration: { - readonly maybeConfig: Option; - } & Struct; - readonly isContinueMigrate: boolean; - readonly asContinueMigrate: { - readonly limits: PalletStateTrieMigrationMigrationLimits; - readonly realSizeUpper: u32; - readonly witnessTask: PalletStateTrieMigrationMigrationTask; - } & Struct; - readonly isMigrateCustomTop: boolean; - readonly asMigrateCustomTop: { - readonly keys_: Vec; - readonly witnessSize: u32; - } & Struct; - readonly isMigrateCustomChild: boolean; - readonly asMigrateCustomChild: { - readonly root: Bytes; - readonly childKeys: Vec; - readonly totalSize: u32; - } & Struct; - readonly isSetSignedMaxLimits: boolean; - readonly asSetSignedMaxLimits: { - readonly limits: PalletStateTrieMigrationMigrationLimits; - } & Struct; - readonly isForceSetProgress: boolean; - readonly asForceSetProgress: { - readonly progressTop: PalletStateTrieMigrationProgress; - readonly progressChild: PalletStateTrieMigrationProgress; - } & Struct; - readonly type: 'ControlAutoMigration' | 'ContinueMigrate' | 'MigrateCustomTop' | 'MigrateCustomChild' | 'SetSignedMaxLimits' | 'ForceSetProgress'; -} - -/** @name PalletStateTrieMigrationError */ -export interface PalletStateTrieMigrationError extends Enum { - readonly isMaxSignedLimits: boolean; - readonly isKeyTooLong: boolean; - readonly isNotEnoughFunds: boolean; - readonly isBadWitness: boolean; - readonly isSignedMigrationNotAllowed: boolean; - readonly isBadChildRoot: boolean; - readonly type: 'MaxSignedLimits' | 'KeyTooLong' | 'NotEnoughFunds' | 'BadWitness' | 'SignedMigrationNotAllowed' | 'BadChildRoot'; -} - -/** @name PalletStateTrieMigrationEvent */ -export interface PalletStateTrieMigrationEvent extends Enum { - readonly isMigrated: boolean; - readonly asMigrated: { - readonly top: u32; - readonly child: u32; - readonly compute: PalletStateTrieMigrationMigrationCompute; - } & Struct; - readonly isSlashed: boolean; - readonly asSlashed: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isAutoMigrationFinished: boolean; - readonly isHalted: boolean; - readonly asHalted: { - readonly error: PalletStateTrieMigrationError; - } & Struct; - readonly type: 'Migrated' | 'Slashed' | 'AutoMigrationFinished' | 'Halted'; -} - -/** @name PalletStateTrieMigrationMigrationCompute */ -export interface PalletStateTrieMigrationMigrationCompute extends Enum { - readonly isSigned: boolean; - readonly isAuto: boolean; - readonly type: 'Signed' | 'Auto'; -} - -/** @name PalletStateTrieMigrationMigrationLimits */ -export interface PalletStateTrieMigrationMigrationLimits extends Struct { - readonly size_: u32; - readonly item: u32; -} - -/** @name PalletStateTrieMigrationMigrationTask */ -export interface PalletStateTrieMigrationMigrationTask extends Struct { - readonly progressTop: PalletStateTrieMigrationProgress; - readonly progressChild: PalletStateTrieMigrationProgress; - readonly size_: u32; - readonly topItems: u32; - readonly childItems: u32; -} - -/** @name PalletStateTrieMigrationProgress */ -export interface PalletStateTrieMigrationProgress extends Enum { - readonly isToStart: boolean; - readonly isLastKey: boolean; - readonly asLastKey: Bytes; - readonly isComplete: boolean; - readonly type: 'ToStart' | 'LastKey' | 'Complete'; -} - -/** @name PalletStructureCall */ -export interface PalletStructureCall extends Null {} - -/** @name PalletStructureError */ -export interface PalletStructureError extends Enum { - readonly isOuroborosDetected: boolean; - readonly isDepthLimit: boolean; - readonly isBreadthLimit: boolean; - readonly isTokenNotFound: boolean; - readonly isCantNestTokenUnderCollection: boolean; - readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound' | 'CantNestTokenUnderCollection'; -} - -/** @name PalletStructureEvent */ -export interface PalletStructureEvent extends Enum { - readonly isExecuted: boolean; - readonly asExecuted: Result; - readonly type: 'Executed'; -} - -/** @name PalletSudoCall */ -export interface PalletSudoCall extends Enum { - readonly isSudo: boolean; - readonly asSudo: { - readonly call: Call; - } & Struct; - readonly isSudoUncheckedWeight: boolean; - readonly asSudoUncheckedWeight: { - readonly call: Call; - readonly weight: SpWeightsWeightV2Weight; - } & Struct; - readonly isSetKey: boolean; - readonly asSetKey: { - readonly new_: MultiAddress; - } & Struct; - readonly isSudoAs: boolean; - readonly asSudoAs: { - readonly who: MultiAddress; - readonly call: Call; - } & Struct; - readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs'; -} - -/** @name PalletSudoError */ -export interface PalletSudoError extends Enum { - readonly isRequireSudo: boolean; - readonly type: 'RequireSudo'; -} - -/** @name PalletSudoEvent */ -export interface PalletSudoEvent extends Enum { - readonly isSudid: boolean; - readonly asSudid: { - readonly sudoResult: Result; - } & Struct; - readonly isKeyChanged: boolean; - readonly asKeyChanged: { - readonly oldSudoer: Option; - } & Struct; - readonly isSudoAsDone: boolean; - readonly asSudoAsDone: { - readonly sudoResult: Result; - } & Struct; - readonly type: 'Sudid' | 'KeyChanged' | 'SudoAsDone'; -} - -/** @name PalletTemplateTransactionPaymentChargeTransactionPayment */ -export interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact {} - -/** @name PalletTestUtilsCall */ -export interface PalletTestUtilsCall extends Enum { - readonly isEnable: boolean; - readonly isSetTestValue: boolean; - readonly asSetTestValue: { - readonly value: u32; - } & Struct; - readonly isSetTestValueAndRollback: boolean; - readonly asSetTestValueAndRollback: { - readonly value: u32; - } & Struct; - readonly isIncTestValue: boolean; - readonly isJustTakeFee: boolean; - readonly isBatchAll: boolean; - readonly asBatchAll: { - readonly calls: Vec; - } & Struct; - readonly type: 'Enable' | 'SetTestValue' | 'SetTestValueAndRollback' | 'IncTestValue' | 'JustTakeFee' | 'BatchAll'; -} - -/** @name PalletTestUtilsError */ -export interface PalletTestUtilsError extends Enum { - readonly isTestPalletDisabled: boolean; - readonly isTriggerRollback: boolean; - readonly type: 'TestPalletDisabled' | 'TriggerRollback'; -} - -/** @name PalletTestUtilsEvent */ -export interface PalletTestUtilsEvent extends Enum { - readonly isValueIsSet: boolean; - readonly isShouldRollback: boolean; - readonly isBatchCompleted: boolean; - readonly type: 'ValueIsSet' | 'ShouldRollback' | 'BatchCompleted'; -} - -/** @name PalletTimestampCall */ -export interface PalletTimestampCall extends Enum { - readonly isSet: boolean; - readonly asSet: { - readonly now: Compact; - } & Struct; - readonly type: 'Set'; -} - -/** @name PalletTransactionPaymentEvent */ -export interface PalletTransactionPaymentEvent extends Enum { - readonly isTransactionFeePaid: boolean; - readonly asTransactionFeePaid: { - readonly who: AccountId32; - readonly actualFee: u128; - readonly tip: u128; - } & Struct; - readonly type: 'TransactionFeePaid'; -} - -/** @name PalletTransactionPaymentReleases */ -export interface PalletTransactionPaymentReleases extends Enum { - readonly isV1Ancient: boolean; - readonly isV2: boolean; - readonly type: 'V1Ancient' | 'V2'; -} - -/** @name PalletTreasuryCall */ -export interface PalletTreasuryCall extends Enum { - readonly isProposeSpend: boolean; - readonly asProposeSpend: { - readonly value: Compact; - readonly beneficiary: MultiAddress; - } & Struct; - readonly isRejectProposal: boolean; - readonly asRejectProposal: { - readonly proposalId: Compact; - } & Struct; - readonly isApproveProposal: boolean; - readonly asApproveProposal: { - readonly proposalId: Compact; - } & Struct; - readonly isSpend: boolean; - readonly asSpend: { - readonly amount: Compact; - readonly beneficiary: MultiAddress; - } & Struct; - readonly isRemoveApproval: boolean; - readonly asRemoveApproval: { - readonly proposalId: Compact; - } & Struct; - readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval'; -} - -/** @name PalletTreasuryError */ -export interface PalletTreasuryError extends Enum { - readonly isInsufficientProposersBalance: boolean; - readonly isInvalidIndex: boolean; - readonly isTooManyApprovals: boolean; - readonly isInsufficientPermission: boolean; - readonly isProposalNotApproved: boolean; - readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved'; -} - -/** @name PalletTreasuryEvent */ -export interface PalletTreasuryEvent extends Enum { - readonly isProposed: boolean; - readonly asProposed: { - readonly proposalIndex: u32; - } & Struct; - readonly isSpending: boolean; - readonly asSpending: { - readonly budgetRemaining: u128; - } & Struct; - readonly isAwarded: boolean; - readonly asAwarded: { - readonly proposalIndex: u32; - readonly award: u128; - readonly account: AccountId32; - } & Struct; - readonly isRejected: boolean; - readonly asRejected: { - readonly proposalIndex: u32; - readonly slashed: u128; - } & Struct; - readonly isBurnt: boolean; - readonly asBurnt: { - readonly burntFunds: u128; - } & Struct; - readonly isRollover: boolean; - readonly asRollover: { - readonly rolloverBalance: u128; - } & Struct; - readonly isDeposit: boolean; - readonly asDeposit: { - readonly value: u128; - } & Struct; - readonly isSpendApproved: boolean; - readonly asSpendApproved: { - readonly proposalIndex: u32; - readonly amount: u128; - readonly beneficiary: AccountId32; - } & Struct; - readonly isUpdatedInactive: boolean; - readonly asUpdatedInactive: { - readonly reactivated: u128; - readonly deactivated: u128; - } & Struct; - readonly type: 'Proposed' | 'Spending' | 'Awarded' | 'Rejected' | 'Burnt' | 'Rollover' | 'Deposit' | 'SpendApproved' | 'UpdatedInactive'; -} - -/** @name PalletTreasuryProposal */ -export interface PalletTreasuryProposal extends Struct { - readonly proposer: AccountId32; - readonly value: u128; - readonly beneficiary: AccountId32; - readonly bond: u128; -} - -/** @name PalletUniqueCall */ -export interface PalletUniqueCall extends Enum { - readonly isCreateCollection: boolean; - readonly asCreateCollection: { - readonly collectionName: Vec; - readonly collectionDescription: Vec; - readonly tokenPrefix: Bytes; - readonly mode: UpDataStructsCollectionMode; - } & Struct; - readonly isCreateCollectionEx: boolean; - readonly asCreateCollectionEx: { - readonly data: UpDataStructsCreateCollectionData; - } & Struct; - readonly isDestroyCollection: boolean; - readonly asDestroyCollection: { - readonly collectionId: u32; - } & Struct; - readonly isAddToAllowList: boolean; - readonly asAddToAllowList: { - readonly collectionId: u32; - readonly address: PalletEvmAccountBasicCrossAccountIdRepr; - } & Struct; - readonly isRemoveFromAllowList: boolean; - readonly asRemoveFromAllowList: { - readonly collectionId: u32; - readonly address: PalletEvmAccountBasicCrossAccountIdRepr; - } & Struct; - readonly isChangeCollectionOwner: boolean; - readonly asChangeCollectionOwner: { - readonly collectionId: u32; - readonly newOwner: AccountId32; - } & Struct; - readonly isAddCollectionAdmin: boolean; - readonly asAddCollectionAdmin: { - readonly collectionId: u32; - readonly newAdminId: PalletEvmAccountBasicCrossAccountIdRepr; - } & Struct; - readonly isRemoveCollectionAdmin: boolean; - readonly asRemoveCollectionAdmin: { - readonly collectionId: u32; - readonly accountId: PalletEvmAccountBasicCrossAccountIdRepr; - } & Struct; - readonly isSetCollectionSponsor: boolean; - readonly asSetCollectionSponsor: { - readonly collectionId: u32; - readonly newSponsor: AccountId32; - } & Struct; - readonly isConfirmSponsorship: boolean; - readonly asConfirmSponsorship: { - readonly collectionId: u32; - } & Struct; - readonly isRemoveCollectionSponsor: boolean; - readonly asRemoveCollectionSponsor: { - readonly collectionId: u32; - } & Struct; - readonly isCreateItem: boolean; - readonly asCreateItem: { - readonly collectionId: u32; - readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; - readonly data: UpDataStructsCreateItemData; - } & Struct; - readonly isCreateMultipleItems: boolean; - readonly asCreateMultipleItems: { - readonly collectionId: u32; - readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; - readonly itemsData: Vec; - } & Struct; - readonly isSetCollectionProperties: boolean; - readonly asSetCollectionProperties: { - readonly collectionId: u32; - readonly properties: Vec; - } & Struct; - readonly isDeleteCollectionProperties: boolean; - readonly asDeleteCollectionProperties: { - readonly collectionId: u32; - readonly propertyKeys: Vec; - } & Struct; - readonly isSetTokenProperties: boolean; - readonly asSetTokenProperties: { - readonly collectionId: u32; - readonly tokenId: u32; - readonly properties: Vec; - } & Struct; - readonly isDeleteTokenProperties: boolean; - readonly asDeleteTokenProperties: { - readonly collectionId: u32; - readonly tokenId: u32; - readonly propertyKeys: Vec; - } & Struct; - readonly isSetTokenPropertyPermissions: boolean; - readonly asSetTokenPropertyPermissions: { - readonly collectionId: u32; - readonly propertyPermissions: Vec; - } & Struct; - readonly isCreateMultipleItemsEx: boolean; - readonly asCreateMultipleItemsEx: { - readonly collectionId: u32; - readonly data: UpDataStructsCreateItemExData; - } & Struct; - readonly isSetTransfersEnabledFlag: boolean; - readonly asSetTransfersEnabledFlag: { - readonly collectionId: u32; - readonly value: bool; - } & Struct; - readonly isBurnItem: boolean; - readonly asBurnItem: { - readonly collectionId: u32; - readonly itemId: u32; - readonly value: u128; - } & Struct; - readonly isBurnFrom: boolean; - readonly asBurnFrom: { - readonly collectionId: u32; - readonly from: PalletEvmAccountBasicCrossAccountIdRepr; - readonly itemId: u32; - readonly value: u128; - } & Struct; - readonly isTransfer: boolean; - readonly asTransfer: { - readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr; - readonly collectionId: u32; - readonly itemId: u32; - readonly value: u128; - } & Struct; - readonly isApprove: boolean; - readonly asApprove: { - readonly spender: PalletEvmAccountBasicCrossAccountIdRepr; - readonly collectionId: u32; - readonly itemId: u32; - readonly amount: u128; - } & Struct; - readonly isApproveFrom: boolean; - readonly asApproveFrom: { - readonly from: PalletEvmAccountBasicCrossAccountIdRepr; - readonly to: PalletEvmAccountBasicCrossAccountIdRepr; - readonly collectionId: u32; - readonly itemId: u32; - readonly amount: u128; - } & Struct; - readonly isTransferFrom: boolean; - readonly asTransferFrom: { - readonly from: PalletEvmAccountBasicCrossAccountIdRepr; - readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr; - readonly collectionId: u32; - readonly itemId: u32; - readonly value: u128; - } & Struct; - readonly isSetCollectionLimits: boolean; - readonly asSetCollectionLimits: { - readonly collectionId: u32; - readonly newLimit: UpDataStructsCollectionLimits; - } & Struct; - readonly isSetCollectionPermissions: boolean; - readonly asSetCollectionPermissions: { - readonly collectionId: u32; - readonly newPermission: UpDataStructsCollectionPermissions; - } & Struct; - readonly isRepartition: boolean; - readonly asRepartition: { - readonly collectionId: u32; - readonly tokenId: u32; - readonly amount: u128; - } & Struct; - readonly isSetAllowanceForAll: boolean; - readonly asSetAllowanceForAll: { - readonly collectionId: u32; - readonly operator: PalletEvmAccountBasicCrossAccountIdRepr; - readonly approve: bool; - } & Struct; - readonly isForceRepairCollection: boolean; - readonly asForceRepairCollection: { - readonly collectionId: u32; - } & Struct; - readonly isForceRepairItem: boolean; - readonly asForceRepairItem: { - readonly collectionId: u32; - readonly itemId: u32; - } & Struct; - readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'ApproveFrom' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem'; -} - -/** @name PalletUniqueError */ -export interface PalletUniqueError extends Enum { - readonly isCollectionDecimalPointLimitExceeded: boolean; - readonly isEmptyArgument: boolean; - readonly isRepartitionCalledOnNonRefungibleCollection: boolean; - readonly type: 'CollectionDecimalPointLimitExceeded' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection'; -} - -/** @name PalletUtilityCall */ -export interface PalletUtilityCall extends Enum { - readonly isBatch: boolean; - readonly asBatch: { - readonly calls: Vec; - } & Struct; - readonly isAsDerivative: boolean; - readonly asAsDerivative: { - readonly index: u16; - readonly call: Call; - } & Struct; - readonly isBatchAll: boolean; - readonly asBatchAll: { - readonly calls: Vec; - } & Struct; - readonly isDispatchAs: boolean; - readonly asDispatchAs: { - readonly asOrigin: OpalRuntimeOriginCaller; - readonly call: Call; - } & Struct; - readonly isForceBatch: boolean; - readonly asForceBatch: { - readonly calls: Vec; - } & Struct; - readonly isWithWeight: boolean; - readonly asWithWeight: { - readonly call: Call; - readonly weight: SpWeightsWeightV2Weight; - } & Struct; - readonly type: 'Batch' | 'AsDerivative' | 'BatchAll' | 'DispatchAs' | 'ForceBatch' | 'WithWeight'; -} - -/** @name PalletUtilityError */ -export interface PalletUtilityError extends Enum { - readonly isTooManyCalls: boolean; - readonly type: 'TooManyCalls'; -} - -/** @name PalletUtilityEvent */ -export interface PalletUtilityEvent extends Enum { - readonly isBatchInterrupted: boolean; - readonly asBatchInterrupted: { - readonly index: u32; - readonly error: SpRuntimeDispatchError; - } & Struct; - readonly isBatchCompleted: boolean; - readonly isBatchCompletedWithErrors: boolean; - readonly isItemCompleted: boolean; - readonly isItemFailed: boolean; - readonly asItemFailed: { - readonly error: SpRuntimeDispatchError; - } & Struct; - readonly isDispatchedAs: boolean; - readonly asDispatchedAs: { - readonly result: Result; - } & Struct; - readonly type: 'BatchInterrupted' | 'BatchCompleted' | 'BatchCompletedWithErrors' | 'ItemCompleted' | 'ItemFailed' | 'DispatchedAs'; -} - -/** @name PalletXcmCall */ -export interface PalletXcmCall extends Enum { - readonly isSend: boolean; - readonly asSend: { - readonly dest: StagingXcmVersionedMultiLocation; - readonly message: StagingXcmVersionedXcm; - } & Struct; - readonly isTeleportAssets: boolean; - readonly asTeleportAssets: { - readonly dest: StagingXcmVersionedMultiLocation; - readonly beneficiary: StagingXcmVersionedMultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - readonly feeAssetItem: u32; - } & Struct; - readonly isReserveTransferAssets: boolean; - readonly asReserveTransferAssets: { - readonly dest: StagingXcmVersionedMultiLocation; - readonly beneficiary: StagingXcmVersionedMultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - readonly feeAssetItem: u32; - } & Struct; - readonly isExecute: boolean; - readonly asExecute: { - readonly message: StagingXcmVersionedXcm; - readonly maxWeight: SpWeightsWeightV2Weight; - } & Struct; - readonly isForceXcmVersion: boolean; - readonly asForceXcmVersion: { - readonly location: StagingXcmV3MultiLocation; - readonly version: u32; - } & Struct; - readonly isForceDefaultXcmVersion: boolean; - readonly asForceDefaultXcmVersion: { - readonly maybeXcmVersion: Option; - } & Struct; - readonly isForceSubscribeVersionNotify: boolean; - readonly asForceSubscribeVersionNotify: { - readonly location: StagingXcmVersionedMultiLocation; - } & Struct; - readonly isForceUnsubscribeVersionNotify: boolean; - readonly asForceUnsubscribeVersionNotify: { - readonly location: StagingXcmVersionedMultiLocation; - } & Struct; - readonly isLimitedReserveTransferAssets: boolean; - readonly asLimitedReserveTransferAssets: { - readonly dest: StagingXcmVersionedMultiLocation; - readonly beneficiary: StagingXcmVersionedMultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - readonly feeAssetItem: u32; - readonly weightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isLimitedTeleportAssets: boolean; - readonly asLimitedTeleportAssets: { - readonly dest: StagingXcmVersionedMultiLocation; - readonly beneficiary: StagingXcmVersionedMultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - readonly feeAssetItem: u32; - readonly weightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isForceSuspension: boolean; - readonly asForceSuspension: { - readonly suspended: bool; - } & Struct; - readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets' | 'ForceSuspension'; -} - -/** @name PalletXcmError */ -export interface PalletXcmError extends Enum { - readonly isUnreachable: boolean; - readonly isSendFailure: boolean; - readonly isFiltered: boolean; - readonly isUnweighableMessage: boolean; - readonly isDestinationNotInvertible: boolean; - readonly isEmpty: boolean; - readonly isCannotReanchor: boolean; - readonly isTooManyAssets: boolean; - readonly isInvalidOrigin: boolean; - readonly isBadVersion: boolean; - readonly isBadLocation: boolean; - readonly isNoSubscription: boolean; - readonly isAlreadySubscribed: boolean; - readonly isInvalidAsset: boolean; - readonly isLowBalance: boolean; - readonly isTooManyLocks: boolean; - readonly isAccountNotSovereign: boolean; - readonly isFeesNotMet: boolean; - readonly isLockNotFound: boolean; - readonly isInUse: boolean; - readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed' | 'InvalidAsset' | 'LowBalance' | 'TooManyLocks' | 'AccountNotSovereign' | 'FeesNotMet' | 'LockNotFound' | 'InUse'; -} - -/** @name PalletXcmEvent */ -export interface PalletXcmEvent extends Enum { - readonly isAttempted: boolean; - readonly asAttempted: { - readonly outcome: StagingXcmV3TraitsOutcome; - } & Struct; - readonly isSent: boolean; - readonly asSent: { - readonly origin: StagingXcmV3MultiLocation; - readonly destination: StagingXcmV3MultiLocation; - readonly message: StagingXcmV3Xcm; - readonly messageId: U8aFixed; - } & Struct; - readonly isUnexpectedResponse: boolean; - readonly asUnexpectedResponse: { - readonly origin: StagingXcmV3MultiLocation; - readonly queryId: u64; - } & Struct; - readonly isResponseReady: boolean; - readonly asResponseReady: { - readonly queryId: u64; - readonly response: StagingXcmV3Response; - } & Struct; - readonly isNotified: boolean; - readonly asNotified: { - readonly queryId: u64; - readonly palletIndex: u8; - readonly callIndex: u8; - } & Struct; - readonly isNotifyOverweight: boolean; - readonly asNotifyOverweight: { - readonly queryId: u64; - readonly palletIndex: u8; - readonly callIndex: u8; - readonly actualWeight: SpWeightsWeightV2Weight; - readonly maxBudgetedWeight: SpWeightsWeightV2Weight; - } & Struct; - readonly isNotifyDispatchError: boolean; - readonly asNotifyDispatchError: { - readonly queryId: u64; - readonly palletIndex: u8; - readonly callIndex: u8; - } & Struct; - readonly isNotifyDecodeFailed: boolean; - readonly asNotifyDecodeFailed: { - readonly queryId: u64; - readonly palletIndex: u8; - readonly callIndex: u8; - } & Struct; - readonly isInvalidResponder: boolean; - readonly asInvalidResponder: { - readonly origin: StagingXcmV3MultiLocation; - readonly queryId: u64; - readonly expectedLocation: Option; - } & Struct; - readonly isInvalidResponderVersion: boolean; - readonly asInvalidResponderVersion: { - readonly origin: StagingXcmV3MultiLocation; - readonly queryId: u64; - } & Struct; - readonly isResponseTaken: boolean; - readonly asResponseTaken: { - readonly queryId: u64; - } & Struct; - readonly isAssetsTrapped: boolean; - readonly asAssetsTrapped: { - readonly hash_: H256; - readonly origin: StagingXcmV3MultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - } & Struct; - readonly isVersionChangeNotified: boolean; - readonly asVersionChangeNotified: { - readonly destination: StagingXcmV3MultiLocation; - readonly result: u32; - readonly cost: StagingXcmV3MultiassetMultiAssets; - readonly messageId: U8aFixed; - } & Struct; - readonly isSupportedVersionChanged: boolean; - readonly asSupportedVersionChanged: { - readonly location: StagingXcmV3MultiLocation; - readonly version: u32; - } & Struct; - readonly isNotifyTargetSendFail: boolean; - readonly asNotifyTargetSendFail: { - readonly location: StagingXcmV3MultiLocation; - readonly queryId: u64; - readonly error: StagingXcmV3TraitsError; - } & Struct; - readonly isNotifyTargetMigrationFail: boolean; - readonly asNotifyTargetMigrationFail: { - readonly location: StagingXcmVersionedMultiLocation; - readonly queryId: u64; - } & Struct; - readonly isInvalidQuerierVersion: boolean; - readonly asInvalidQuerierVersion: { - readonly origin: StagingXcmV3MultiLocation; - readonly queryId: u64; - } & Struct; - readonly isInvalidQuerier: boolean; - readonly asInvalidQuerier: { - readonly origin: StagingXcmV3MultiLocation; - readonly queryId: u64; - readonly expectedQuerier: StagingXcmV3MultiLocation; - readonly maybeActualQuerier: Option; - } & Struct; - readonly isVersionNotifyStarted: boolean; - readonly asVersionNotifyStarted: { - readonly destination: StagingXcmV3MultiLocation; - readonly cost: StagingXcmV3MultiassetMultiAssets; - readonly messageId: U8aFixed; - } & Struct; - readonly isVersionNotifyRequested: boolean; - readonly asVersionNotifyRequested: { - readonly destination: StagingXcmV3MultiLocation; - readonly cost: StagingXcmV3MultiassetMultiAssets; - readonly messageId: U8aFixed; - } & Struct; - readonly isVersionNotifyUnrequested: boolean; - readonly asVersionNotifyUnrequested: { - readonly destination: StagingXcmV3MultiLocation; - readonly cost: StagingXcmV3MultiassetMultiAssets; - readonly messageId: U8aFixed; - } & Struct; - readonly isFeesPaid: boolean; - readonly asFeesPaid: { - readonly paying: StagingXcmV3MultiLocation; - readonly fees: StagingXcmV3MultiassetMultiAssets; - } & Struct; - readonly isAssetsClaimed: boolean; - readonly asAssetsClaimed: { - readonly hash_: H256; - readonly origin: StagingXcmV3MultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - } & Struct; - readonly type: 'Attempted' | 'Sent' | 'UnexpectedResponse' | 'ResponseReady' | 'Notified' | 'NotifyOverweight' | 'NotifyDispatchError' | 'NotifyDecodeFailed' | 'InvalidResponder' | 'InvalidResponderVersion' | 'ResponseTaken' | 'AssetsTrapped' | 'VersionChangeNotified' | 'SupportedVersionChanged' | 'NotifyTargetSendFail' | 'NotifyTargetMigrationFail' | 'InvalidQuerierVersion' | 'InvalidQuerier' | 'VersionNotifyStarted' | 'VersionNotifyRequested' | 'VersionNotifyUnrequested' | 'FeesPaid' | 'AssetsClaimed'; -} - -/** @name PalletXcmOrigin */ -export interface PalletXcmOrigin extends Enum { - readonly isXcm: boolean; - readonly asXcm: StagingXcmV3MultiLocation; - readonly isResponse: boolean; - readonly asResponse: StagingXcmV3MultiLocation; - readonly type: 'Xcm' | 'Response'; -} - -/** @name PalletXcmQueryStatus */ -export interface PalletXcmQueryStatus extends Enum { - readonly isPending: boolean; - readonly asPending: { - readonly responder: StagingXcmVersionedMultiLocation; - readonly maybeMatchQuerier: Option; - readonly maybeNotify: Option>; - readonly timeout: u32; - } & Struct; - readonly isVersionNotifier: boolean; - readonly asVersionNotifier: { - readonly origin: StagingXcmVersionedMultiLocation; - readonly isActive: bool; - } & Struct; - readonly isReady: boolean; - readonly asReady: { - readonly response: StagingXcmVersionedResponse; - readonly at: u32; - } & Struct; - readonly type: 'Pending' | 'VersionNotifier' | 'Ready'; -} - -/** @name PalletXcmRemoteLockedFungibleRecord */ -export interface PalletXcmRemoteLockedFungibleRecord extends Struct { - readonly amount: u128; - readonly owner: StagingXcmVersionedMultiLocation; - readonly locker: StagingXcmVersionedMultiLocation; - readonly consumers: Vec>; -} - -/** @name PalletXcmVersionMigrationStage */ -export interface PalletXcmVersionMigrationStage extends Enum { - readonly isMigrateSupportedVersion: boolean; - readonly isMigrateVersionNotifiers: boolean; - readonly isNotifyCurrentTargets: boolean; - readonly asNotifyCurrentTargets: Option; - readonly isMigrateAndNotifyOldTargets: boolean; - readonly type: 'MigrateSupportedVersion' | 'MigrateVersionNotifiers' | 'NotifyCurrentTargets' | 'MigrateAndNotifyOldTargets'; -} - -/** @name ParachainInfoCall */ -export interface ParachainInfoCall extends Null {} - -/** @name PhantomTypeUpDataStructs */ -export interface PhantomTypeUpDataStructs extends Vec> {} - -/** @name PolkadotCorePrimitivesInboundDownwardMessage */ -export interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct { - readonly sentAt: u32; - readonly msg: Bytes; -} - -/** @name PolkadotCorePrimitivesInboundHrmpMessage */ -export interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct { - readonly sentAt: u32; - readonly data: Bytes; -} - -/** @name PolkadotCorePrimitivesOutboundHrmpMessage */ -export interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct { - readonly recipient: u32; - readonly data: Bytes; -} - -/** @name PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat */ -export interface PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat extends Enum { - readonly isConcatenatedVersionedXcm: boolean; - readonly isConcatenatedEncodedBlob: boolean; - readonly isSignals: boolean; - readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals'; -} - -/** @name PolkadotPrimitivesV5AbridgedHostConfiguration */ -export interface PolkadotPrimitivesV5AbridgedHostConfiguration extends Struct { - readonly maxCodeSize: u32; - readonly maxHeadDataSize: u32; - readonly maxUpwardQueueCount: u32; - readonly maxUpwardQueueSize: u32; - readonly maxUpwardMessageSize: u32; - readonly maxUpwardMessageNumPerCandidate: u32; - readonly hrmpMaxMessageNumPerCandidate: u32; - readonly validationUpgradeCooldown: u32; - readonly validationUpgradeDelay: u32; - readonly asyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams; -} - -/** @name PolkadotPrimitivesV5AbridgedHrmpChannel */ -export interface PolkadotPrimitivesV5AbridgedHrmpChannel extends Struct { - readonly maxCapacity: u32; - readonly maxTotalSize: u32; - readonly maxMessageSize: u32; - readonly msgCount: u32; - readonly totalSize: u32; - readonly mqcHead: Option; -} - -/** @name PolkadotPrimitivesV5PersistedValidationData */ -export interface PolkadotPrimitivesV5PersistedValidationData extends Struct { - readonly parentHead: Bytes; - readonly relayParentNumber: u32; - readonly relayParentStorageRoot: H256; - readonly maxPovSize: u32; -} - -/** @name PolkadotPrimitivesV5UpgradeGoAhead */ -export interface PolkadotPrimitivesV5UpgradeGoAhead extends Enum { - readonly isAbort: boolean; - readonly isGoAhead: boolean; - readonly type: 'Abort' | 'GoAhead'; -} - -/** @name PolkadotPrimitivesV5UpgradeRestriction */ -export interface PolkadotPrimitivesV5UpgradeRestriction extends Enum { - readonly isPresent: boolean; - readonly type: 'Present'; -} - -/** @name PolkadotPrimitivesVstagingAsyncBackingParams */ -export interface PolkadotPrimitivesVstagingAsyncBackingParams extends Struct { - readonly maxCandidateDepth: u32; - readonly allowedAncestryLen: u32; -} - -/** @name SpArithmeticArithmeticError */ -export interface SpArithmeticArithmeticError extends Enum { - readonly isUnderflow: boolean; - readonly isOverflow: boolean; - readonly isDivisionByZero: boolean; - readonly type: 'Underflow' | 'Overflow' | 'DivisionByZero'; -} - -/** @name SpConsensusAuraSr25519AppSr25519Public */ -export interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public {} - -/** @name SpCoreCryptoKeyTypeId */ -export interface SpCoreCryptoKeyTypeId extends U8aFixed {} - -/** @name SpCoreEcdsaSignature */ -export interface SpCoreEcdsaSignature extends U8aFixed {} - -/** @name SpCoreEd25519Signature */ -export interface SpCoreEd25519Signature extends U8aFixed {} - -/** @name SpCoreSr25519Public */ -export interface SpCoreSr25519Public extends U8aFixed {} - -/** @name SpCoreSr25519Signature */ -export interface SpCoreSr25519Signature extends U8aFixed {} - -/** @name SpCoreVoid */ -export interface SpCoreVoid extends Null {} - -/** @name SpRuntimeDigest */ -export interface SpRuntimeDigest extends Struct { - readonly logs: Vec; -} - -/** @name SpRuntimeDigestDigestItem */ -export interface SpRuntimeDigestDigestItem extends Enum { - readonly isOther: boolean; - readonly asOther: Bytes; - readonly isConsensus: boolean; - readonly asConsensus: ITuple<[U8aFixed, Bytes]>; - readonly isSeal: boolean; - readonly asSeal: ITuple<[U8aFixed, Bytes]>; - readonly isPreRuntime: boolean; - readonly asPreRuntime: ITuple<[U8aFixed, Bytes]>; - readonly isRuntimeEnvironmentUpdated: boolean; - readonly type: 'Other' | 'Consensus' | 'Seal' | 'PreRuntime' | 'RuntimeEnvironmentUpdated'; -} - -/** @name SpRuntimeDispatchError */ -export interface SpRuntimeDispatchError extends Enum { - readonly isOther: boolean; - readonly isCannotLookup: boolean; - readonly isBadOrigin: boolean; - readonly isModule: boolean; - readonly asModule: SpRuntimeModuleError; - readonly isConsumerRemaining: boolean; - readonly isNoProviders: boolean; - readonly isTooManyConsumers: boolean; - readonly isToken: boolean; - readonly asToken: SpRuntimeTokenError; - readonly isArithmetic: boolean; - readonly asArithmetic: SpArithmeticArithmeticError; - readonly isTransactional: boolean; - readonly asTransactional: SpRuntimeTransactionalError; - readonly isExhausted: boolean; - readonly isCorruption: boolean; - readonly isUnavailable: boolean; - readonly isRootNotAllowed: boolean; - readonly type: 'Other' | 'CannotLookup' | 'BadOrigin' | 'Module' | 'ConsumerRemaining' | 'NoProviders' | 'TooManyConsumers' | 'Token' | 'Arithmetic' | 'Transactional' | 'Exhausted' | 'Corruption' | 'Unavailable' | 'RootNotAllowed'; -} - -/** @name SpRuntimeModuleError */ -export interface SpRuntimeModuleError extends Struct { - readonly index: u8; - readonly error: U8aFixed; -} - -/** @name SpRuntimeMultiSignature */ -export interface SpRuntimeMultiSignature extends Enum { - readonly isEd25519: boolean; - readonly asEd25519: SpCoreEd25519Signature; - readonly isSr25519: boolean; - readonly asSr25519: SpCoreSr25519Signature; - readonly isEcdsa: boolean; - readonly asEcdsa: SpCoreEcdsaSignature; - readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa'; -} - -/** @name SpRuntimeTokenError */ -export interface SpRuntimeTokenError extends Enum { - readonly isFundsUnavailable: boolean; - readonly isOnlyProvider: boolean; - readonly isBelowMinimum: boolean; - readonly isCannotCreate: boolean; - readonly isUnknownAsset: boolean; - readonly isFrozen: boolean; - readonly isUnsupported: boolean; - readonly isCannotCreateHold: boolean; - readonly isNotExpendable: boolean; - readonly isBlocked: boolean; - readonly type: 'FundsUnavailable' | 'OnlyProvider' | 'BelowMinimum' | 'CannotCreate' | 'UnknownAsset' | 'Frozen' | 'Unsupported' | 'CannotCreateHold' | 'NotExpendable' | 'Blocked'; -} - -/** @name SpRuntimeTransactionalError */ -export interface SpRuntimeTransactionalError extends Enum { - readonly isLimitReached: boolean; - readonly isNoLayer: boolean; - readonly type: 'LimitReached' | 'NoLayer'; -} - -/** @name SpRuntimeTransactionValidityInvalidTransaction */ -export interface SpRuntimeTransactionValidityInvalidTransaction extends Enum { - readonly isCall: boolean; - readonly isPayment: boolean; - readonly isFuture: boolean; - readonly isStale: boolean; - readonly isBadProof: boolean; - readonly isAncientBirthBlock: boolean; - readonly isExhaustsResources: boolean; - readonly isCustom: boolean; - readonly asCustom: u8; - readonly isBadMandatory: boolean; - readonly isMandatoryValidation: boolean; - readonly isBadSigner: boolean; - readonly type: 'Call' | 'Payment' | 'Future' | 'Stale' | 'BadProof' | 'AncientBirthBlock' | 'ExhaustsResources' | 'Custom' | 'BadMandatory' | 'MandatoryValidation' | 'BadSigner'; -} - -/** @name SpRuntimeTransactionValidityTransactionValidityError */ -export interface SpRuntimeTransactionValidityTransactionValidityError extends Enum { - readonly isInvalid: boolean; - readonly asInvalid: SpRuntimeTransactionValidityInvalidTransaction; - readonly isUnknown: boolean; - readonly asUnknown: SpRuntimeTransactionValidityUnknownTransaction; - readonly type: 'Invalid' | 'Unknown'; -} - -/** @name SpRuntimeTransactionValidityUnknownTransaction */ -export interface SpRuntimeTransactionValidityUnknownTransaction extends Enum { - readonly isCannotLookup: boolean; - readonly isNoUnsignedValidator: boolean; - readonly isCustom: boolean; - readonly asCustom: u8; - readonly type: 'CannotLookup' | 'NoUnsignedValidator' | 'Custom'; -} - -/** @name SpTrieStorageProof */ -export interface SpTrieStorageProof extends Struct { - readonly trieNodes: BTreeSet; -} - -/** @name SpVersionRuntimeVersion */ -export interface SpVersionRuntimeVersion extends Struct { - readonly specName: Text; - readonly implName: Text; - readonly authoringVersion: u32; - readonly specVersion: u32; - readonly implVersion: u32; - readonly apis: Vec>; - readonly transactionVersion: u32; - readonly stateVersion: u8; -} - -/** @name SpWeightsRuntimeDbWeight */ -export interface SpWeightsRuntimeDbWeight extends Struct { - readonly read: u64; - readonly write: u64; -} - -/** @name SpWeightsWeightV2Weight */ -export interface SpWeightsWeightV2Weight extends Struct { - readonly refTime: Compact; - readonly proofSize: Compact; -} - -/** @name StagingXcmDoubleEncoded */ -export interface StagingXcmDoubleEncoded extends Struct { - readonly encoded: Bytes; -} - -/** @name StagingXcmV2BodyId */ -export interface StagingXcmV2BodyId extends Enum { - readonly isUnit: boolean; - readonly isNamed: boolean; - readonly asNamed: Bytes; - readonly isIndex: boolean; - readonly asIndex: Compact; - readonly isExecutive: boolean; - readonly isTechnical: boolean; - readonly isLegislative: boolean; - readonly isJudicial: boolean; - readonly isDefense: boolean; - readonly isAdministration: boolean; - readonly isTreasury: boolean; - readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury'; -} - -/** @name StagingXcmV2BodyPart */ -export interface StagingXcmV2BodyPart extends Enum { - readonly isVoice: boolean; - readonly isMembers: boolean; - readonly asMembers: { - readonly count: Compact; - } & Struct; - readonly isFraction: boolean; - readonly asFraction: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly isAtLeastProportion: boolean; - readonly asAtLeastProportion: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly isMoreThanProportion: boolean; - readonly asMoreThanProportion: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion'; -} - -/** @name StagingXcmV2Instruction */ -export interface StagingXcmV2Instruction extends Enum { - readonly isWithdrawAsset: boolean; - readonly asWithdrawAsset: StagingXcmV2MultiassetMultiAssets; - readonly isReserveAssetDeposited: boolean; - readonly asReserveAssetDeposited: StagingXcmV2MultiassetMultiAssets; - readonly isReceiveTeleportedAsset: boolean; - readonly asReceiveTeleportedAsset: StagingXcmV2MultiassetMultiAssets; - readonly isQueryResponse: boolean; - readonly asQueryResponse: { - readonly queryId: Compact; - readonly response: StagingXcmV2Response; - readonly maxWeight: Compact; - } & Struct; - readonly isTransferAsset: boolean; - readonly asTransferAsset: { - readonly assets: StagingXcmV2MultiassetMultiAssets; - readonly beneficiary: StagingXcmV2MultiLocation; - } & Struct; - readonly isTransferReserveAsset: boolean; - readonly asTransferReserveAsset: { - readonly assets: StagingXcmV2MultiassetMultiAssets; - readonly dest: StagingXcmV2MultiLocation; - readonly xcm: StagingXcmV2Xcm; - } & Struct; - readonly isTransact: boolean; - readonly asTransact: { - readonly originType: StagingXcmV2OriginKind; - readonly requireWeightAtMost: Compact; - readonly call: StagingXcmDoubleEncoded; - } & Struct; - readonly isHrmpNewChannelOpenRequest: boolean; - readonly asHrmpNewChannelOpenRequest: { - readonly sender: Compact; - readonly maxMessageSize: Compact; - readonly maxCapacity: Compact; - } & Struct; - readonly isHrmpChannelAccepted: boolean; - readonly asHrmpChannelAccepted: { - readonly recipient: Compact; - } & Struct; - readonly isHrmpChannelClosing: boolean; - readonly asHrmpChannelClosing: { - readonly initiator: Compact; - readonly sender: Compact; - readonly recipient: Compact; - } & Struct; - readonly isClearOrigin: boolean; - readonly isDescendOrigin: boolean; - readonly asDescendOrigin: StagingXcmV2MultilocationJunctions; - readonly isReportError: boolean; - readonly asReportError: { - readonly queryId: Compact; - readonly dest: StagingXcmV2MultiLocation; - readonly maxResponseWeight: Compact; - } & Struct; - readonly isDepositAsset: boolean; - readonly asDepositAsset: { - readonly assets: StagingXcmV2MultiassetMultiAssetFilter; - readonly maxAssets: Compact; - readonly beneficiary: StagingXcmV2MultiLocation; - } & Struct; - readonly isDepositReserveAsset: boolean; - readonly asDepositReserveAsset: { - readonly assets: StagingXcmV2MultiassetMultiAssetFilter; - readonly maxAssets: Compact; - readonly dest: StagingXcmV2MultiLocation; - readonly xcm: StagingXcmV2Xcm; - } & Struct; - readonly isExchangeAsset: boolean; - readonly asExchangeAsset: { - readonly give: StagingXcmV2MultiassetMultiAssetFilter; - readonly receive: StagingXcmV2MultiassetMultiAssets; - } & Struct; - readonly isInitiateReserveWithdraw: boolean; - readonly asInitiateReserveWithdraw: { - readonly assets: StagingXcmV2MultiassetMultiAssetFilter; - readonly reserve: StagingXcmV2MultiLocation; - readonly xcm: StagingXcmV2Xcm; - } & Struct; - readonly isInitiateTeleport: boolean; - readonly asInitiateTeleport: { - readonly assets: StagingXcmV2MultiassetMultiAssetFilter; - readonly dest: StagingXcmV2MultiLocation; - readonly xcm: StagingXcmV2Xcm; - } & Struct; - readonly isQueryHolding: boolean; - readonly asQueryHolding: { - readonly queryId: Compact; - readonly dest: StagingXcmV2MultiLocation; - readonly assets: StagingXcmV2MultiassetMultiAssetFilter; - readonly maxResponseWeight: Compact; - } & Struct; - readonly isBuyExecution: boolean; - readonly asBuyExecution: { - readonly fees: StagingXcmV2MultiAsset; - readonly weightLimit: StagingXcmV2WeightLimit; - } & Struct; - readonly isRefundSurplus: boolean; - readonly isSetErrorHandler: boolean; - readonly asSetErrorHandler: StagingXcmV2Xcm; - readonly isSetAppendix: boolean; - readonly asSetAppendix: StagingXcmV2Xcm; - readonly isClearError: boolean; - readonly isClaimAsset: boolean; - readonly asClaimAsset: { - readonly assets: StagingXcmV2MultiassetMultiAssets; - readonly ticket: StagingXcmV2MultiLocation; - } & Struct; - readonly isTrap: boolean; - readonly asTrap: Compact; - readonly isSubscribeVersion: boolean; - readonly asSubscribeVersion: { - readonly queryId: Compact; - readonly maxResponseWeight: Compact; - } & Struct; - readonly isUnsubscribeVersion: boolean; - readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion'; -} - -/** @name StagingXcmV2Junction */ -export interface StagingXcmV2Junction extends Enum { - readonly isParachain: boolean; - readonly asParachain: Compact; - readonly isAccountId32: boolean; - readonly asAccountId32: { - readonly network: StagingXcmV2NetworkId; - readonly id: U8aFixed; - } & Struct; - readonly isAccountIndex64: boolean; - readonly asAccountIndex64: { - readonly network: StagingXcmV2NetworkId; - readonly index: Compact; - } & Struct; - readonly isAccountKey20: boolean; - readonly asAccountKey20: { - readonly network: StagingXcmV2NetworkId; - readonly key: U8aFixed; - } & Struct; - readonly isPalletInstance: boolean; - readonly asPalletInstance: u8; - readonly isGeneralIndex: boolean; - readonly asGeneralIndex: Compact; - readonly isGeneralKey: boolean; - readonly asGeneralKey: Bytes; - readonly isOnlyChild: boolean; - readonly isPlurality: boolean; - readonly asPlurality: { - readonly id: StagingXcmV2BodyId; - readonly part: StagingXcmV2BodyPart; - } & Struct; - readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality'; -} - -/** @name StagingXcmV2MultiAsset */ -export interface StagingXcmV2MultiAsset extends Struct { - readonly id: StagingXcmV2MultiassetAssetId; - readonly fun: StagingXcmV2MultiassetFungibility; -} - -/** @name StagingXcmV2MultiassetAssetId */ -export interface StagingXcmV2MultiassetAssetId extends Enum { - readonly isConcrete: boolean; - readonly asConcrete: StagingXcmV2MultiLocation; - readonly isAbstract: boolean; - readonly asAbstract: Bytes; - readonly type: 'Concrete' | 'Abstract'; -} - -/** @name StagingXcmV2MultiassetAssetInstance */ -export interface StagingXcmV2MultiassetAssetInstance extends Enum { - readonly isUndefined: boolean; - readonly isIndex: boolean; - readonly asIndex: Compact; - readonly isArray4: boolean; - readonly asArray4: U8aFixed; - readonly isArray8: boolean; - readonly asArray8: U8aFixed; - readonly isArray16: boolean; - readonly asArray16: U8aFixed; - readonly isArray32: boolean; - readonly asArray32: U8aFixed; - readonly isBlob: boolean; - readonly asBlob: Bytes; - readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob'; -} - -/** @name StagingXcmV2MultiassetFungibility */ -export interface StagingXcmV2MultiassetFungibility extends Enum { - readonly isFungible: boolean; - readonly asFungible: Compact; - readonly isNonFungible: boolean; - readonly asNonFungible: StagingXcmV2MultiassetAssetInstance; - readonly type: 'Fungible' | 'NonFungible'; -} - -/** @name StagingXcmV2MultiassetMultiAssetFilter */ -export interface StagingXcmV2MultiassetMultiAssetFilter extends Enum { - readonly isDefinite: boolean; - readonly asDefinite: StagingXcmV2MultiassetMultiAssets; - readonly isWild: boolean; - readonly asWild: StagingXcmV2MultiassetWildMultiAsset; - readonly type: 'Definite' | 'Wild'; -} - -/** @name StagingXcmV2MultiassetMultiAssets */ -export interface StagingXcmV2MultiassetMultiAssets extends Vec {} - -/** @name StagingXcmV2MultiassetWildFungibility */ -export interface StagingXcmV2MultiassetWildFungibility extends Enum { - readonly isFungible: boolean; - readonly isNonFungible: boolean; - readonly type: 'Fungible' | 'NonFungible'; -} - -/** @name StagingXcmV2MultiassetWildMultiAsset */ -export interface StagingXcmV2MultiassetWildMultiAsset extends Enum { - readonly isAll: boolean; - readonly isAllOf: boolean; - readonly asAllOf: { - readonly id: StagingXcmV2MultiassetAssetId; - readonly fun: StagingXcmV2MultiassetWildFungibility; - } & Struct; - readonly type: 'All' | 'AllOf'; -} - -/** @name StagingXcmV2MultiLocation */ -export interface StagingXcmV2MultiLocation extends Struct { - readonly parents: u8; - readonly interior: StagingXcmV2MultilocationJunctions; -} - -/** @name StagingXcmV2MultilocationJunctions */ -export interface StagingXcmV2MultilocationJunctions extends Enum { - readonly isHere: boolean; - readonly isX1: boolean; - readonly asX1: StagingXcmV2Junction; - readonly isX2: boolean; - readonly asX2: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX3: boolean; - readonly asX3: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX4: boolean; - readonly asX4: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX5: boolean; - readonly asX5: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX6: boolean; - readonly asX6: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX7: boolean; - readonly asX7: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX8: boolean; - readonly asX8: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; -} - -/** @name StagingXcmV2NetworkId */ -export interface StagingXcmV2NetworkId extends Enum { - readonly isAny: boolean; - readonly isNamed: boolean; - readonly asNamed: Bytes; - readonly isPolkadot: boolean; - readonly isKusama: boolean; - readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama'; -} - -/** @name StagingXcmV2OriginKind */ -export interface StagingXcmV2OriginKind extends Enum { - readonly isNative: boolean; - readonly isSovereignAccount: boolean; - readonly isSuperuser: boolean; - readonly isXcm: boolean; - readonly type: 'Native' | 'SovereignAccount' | 'Superuser' | 'Xcm'; -} - -/** @name StagingXcmV2Response */ -export interface StagingXcmV2Response extends Enum { - readonly isNull: boolean; - readonly isAssets: boolean; - readonly asAssets: StagingXcmV2MultiassetMultiAssets; - readonly isExecutionResult: boolean; - readonly asExecutionResult: Option>; - readonly isVersion: boolean; - readonly asVersion: u32; - readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version'; -} - -/** @name StagingXcmV2TraitsError */ -export interface StagingXcmV2TraitsError extends Enum { - readonly isOverflow: boolean; - readonly isUnimplemented: boolean; - readonly isUntrustedReserveLocation: boolean; - readonly isUntrustedTeleportLocation: boolean; - readonly isMultiLocationFull: boolean; - readonly isMultiLocationNotInvertible: boolean; - readonly isBadOrigin: boolean; - readonly isInvalidLocation: boolean; - readonly isAssetNotFound: boolean; - readonly isFailedToTransactAsset: boolean; - readonly isNotWithdrawable: boolean; - readonly isLocationCannotHold: boolean; - readonly isExceedsMaxMessageSize: boolean; - readonly isDestinationUnsupported: boolean; - readonly isTransport: boolean; - readonly isUnroutable: boolean; - readonly isUnknownClaim: boolean; - readonly isFailedToDecode: boolean; - readonly isMaxWeightInvalid: boolean; - readonly isNotHoldingFees: boolean; - readonly isTooExpensive: boolean; - readonly isTrap: boolean; - readonly asTrap: u64; - readonly isUnhandledXcmVersion: boolean; - readonly isWeightLimitReached: boolean; - readonly asWeightLimitReached: u64; - readonly isBarrier: boolean; - readonly isWeightNotComputable: boolean; - readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'MultiLocationFull' | 'MultiLocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable'; -} - -/** @name StagingXcmV2WeightLimit */ -export interface StagingXcmV2WeightLimit extends Enum { - readonly isUnlimited: boolean; - readonly isLimited: boolean; - readonly asLimited: Compact; - readonly type: 'Unlimited' | 'Limited'; -} - -/** @name StagingXcmV2Xcm */ -export interface StagingXcmV2Xcm extends Vec {} - -/** @name StagingXcmV3Instruction */ -export interface StagingXcmV3Instruction extends Enum { - readonly isWithdrawAsset: boolean; - readonly asWithdrawAsset: StagingXcmV3MultiassetMultiAssets; - readonly isReserveAssetDeposited: boolean; - readonly asReserveAssetDeposited: StagingXcmV3MultiassetMultiAssets; - readonly isReceiveTeleportedAsset: boolean; - readonly asReceiveTeleportedAsset: StagingXcmV3MultiassetMultiAssets; - readonly isQueryResponse: boolean; - readonly asQueryResponse: { - readonly queryId: Compact; - readonly response: StagingXcmV3Response; - readonly maxWeight: SpWeightsWeightV2Weight; - readonly querier: Option; - } & Struct; - readonly isTransferAsset: boolean; - readonly asTransferAsset: { - readonly assets: StagingXcmV3MultiassetMultiAssets; - readonly beneficiary: StagingXcmV3MultiLocation; - } & Struct; - readonly isTransferReserveAsset: boolean; - readonly asTransferReserveAsset: { - readonly assets: StagingXcmV3MultiassetMultiAssets; - readonly dest: StagingXcmV3MultiLocation; - readonly xcm: StagingXcmV3Xcm; - } & Struct; - readonly isTransact: boolean; - readonly asTransact: { - readonly originKind: StagingXcmV2OriginKind; - readonly requireWeightAtMost: SpWeightsWeightV2Weight; - readonly call: StagingXcmDoubleEncoded; - } & Struct; - readonly isHrmpNewChannelOpenRequest: boolean; - readonly asHrmpNewChannelOpenRequest: { - readonly sender: Compact; - readonly maxMessageSize: Compact; - readonly maxCapacity: Compact; - } & Struct; - readonly isHrmpChannelAccepted: boolean; - readonly asHrmpChannelAccepted: { - readonly recipient: Compact; - } & Struct; - readonly isHrmpChannelClosing: boolean; - readonly asHrmpChannelClosing: { - readonly initiator: Compact; - readonly sender: Compact; - readonly recipient: Compact; - } & Struct; - readonly isClearOrigin: boolean; - readonly isDescendOrigin: boolean; - readonly asDescendOrigin: StagingXcmV3Junctions; - readonly isReportError: boolean; - readonly asReportError: StagingXcmV3QueryResponseInfo; - readonly isDepositAsset: boolean; - readonly asDepositAsset: { - readonly assets: StagingXcmV3MultiassetMultiAssetFilter; - readonly beneficiary: StagingXcmV3MultiLocation; - } & Struct; - readonly isDepositReserveAsset: boolean; - readonly asDepositReserveAsset: { - readonly assets: StagingXcmV3MultiassetMultiAssetFilter; - readonly dest: StagingXcmV3MultiLocation; - readonly xcm: StagingXcmV3Xcm; - } & Struct; - readonly isExchangeAsset: boolean; - readonly asExchangeAsset: { - readonly give: StagingXcmV3MultiassetMultiAssetFilter; - readonly want: StagingXcmV3MultiassetMultiAssets; - readonly maximal: bool; - } & Struct; - readonly isInitiateReserveWithdraw: boolean; - readonly asInitiateReserveWithdraw: { - readonly assets: StagingXcmV3MultiassetMultiAssetFilter; - readonly reserve: StagingXcmV3MultiLocation; - readonly xcm: StagingXcmV3Xcm; - } & Struct; - readonly isInitiateTeleport: boolean; - readonly asInitiateTeleport: { - readonly assets: StagingXcmV3MultiassetMultiAssetFilter; - readonly dest: StagingXcmV3MultiLocation; - readonly xcm: StagingXcmV3Xcm; - } & Struct; - readonly isReportHolding: boolean; - readonly asReportHolding: { - readonly responseInfo: StagingXcmV3QueryResponseInfo; - readonly assets: StagingXcmV3MultiassetMultiAssetFilter; - } & Struct; - readonly isBuyExecution: boolean; - readonly asBuyExecution: { - readonly fees: StagingXcmV3MultiAsset; - readonly weightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isRefundSurplus: boolean; - readonly isSetErrorHandler: boolean; - readonly asSetErrorHandler: StagingXcmV3Xcm; - readonly isSetAppendix: boolean; - readonly asSetAppendix: StagingXcmV3Xcm; - readonly isClearError: boolean; - readonly isClaimAsset: boolean; - readonly asClaimAsset: { - readonly assets: StagingXcmV3MultiassetMultiAssets; - readonly ticket: StagingXcmV3MultiLocation; - } & Struct; - readonly isTrap: boolean; - readonly asTrap: Compact; - readonly isSubscribeVersion: boolean; - readonly asSubscribeVersion: { - readonly queryId: Compact; - readonly maxResponseWeight: SpWeightsWeightV2Weight; - } & Struct; - readonly isUnsubscribeVersion: boolean; - readonly isBurnAsset: boolean; - readonly asBurnAsset: StagingXcmV3MultiassetMultiAssets; - readonly isExpectAsset: boolean; - readonly asExpectAsset: StagingXcmV3MultiassetMultiAssets; - readonly isExpectOrigin: boolean; - readonly asExpectOrigin: Option; - readonly isExpectError: boolean; - readonly asExpectError: Option>; - readonly isExpectTransactStatus: boolean; - readonly asExpectTransactStatus: StagingXcmV3MaybeErrorCode; - readonly isQueryPallet: boolean; - readonly asQueryPallet: { - readonly moduleName: Bytes; - readonly responseInfo: StagingXcmV3QueryResponseInfo; - } & Struct; - readonly isExpectPallet: boolean; - readonly asExpectPallet: { - readonly index: Compact; - readonly name: Bytes; - readonly moduleName: Bytes; - readonly crateMajor: Compact; - readonly minCrateMinor: Compact; - } & Struct; - readonly isReportTransactStatus: boolean; - readonly asReportTransactStatus: StagingXcmV3QueryResponseInfo; - readonly isClearTransactStatus: boolean; - readonly isUniversalOrigin: boolean; - readonly asUniversalOrigin: StagingXcmV3Junction; - readonly isExportMessage: boolean; - readonly asExportMessage: { - readonly network: StagingXcmV3JunctionNetworkId; - readonly destination: StagingXcmV3Junctions; - readonly xcm: StagingXcmV3Xcm; - } & Struct; - readonly isLockAsset: boolean; - readonly asLockAsset: { - readonly asset: StagingXcmV3MultiAsset; - readonly unlocker: StagingXcmV3MultiLocation; - } & Struct; - readonly isUnlockAsset: boolean; - readonly asUnlockAsset: { - readonly asset: StagingXcmV3MultiAsset; - readonly target: StagingXcmV3MultiLocation; - } & Struct; - readonly isNoteUnlockable: boolean; - readonly asNoteUnlockable: { - readonly asset: StagingXcmV3MultiAsset; - readonly owner: StagingXcmV3MultiLocation; - } & Struct; - readonly isRequestUnlock: boolean; - readonly asRequestUnlock: { - readonly asset: StagingXcmV3MultiAsset; - readonly locker: StagingXcmV3MultiLocation; - } & Struct; - readonly isSetFeesMode: boolean; - readonly asSetFeesMode: { - readonly jitWithdraw: bool; - } & Struct; - readonly isSetTopic: boolean; - readonly asSetTopic: U8aFixed; - readonly isClearTopic: boolean; - readonly isAliasOrigin: boolean; - readonly asAliasOrigin: StagingXcmV3MultiLocation; - readonly isUnpaidExecution: boolean; - readonly asUnpaidExecution: { - readonly weightLimit: StagingXcmV3WeightLimit; - readonly checkOrigin: Option; - } & Struct; - readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'ReportHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion' | 'BurnAsset' | 'ExpectAsset' | 'ExpectOrigin' | 'ExpectError' | 'ExpectTransactStatus' | 'QueryPallet' | 'ExpectPallet' | 'ReportTransactStatus' | 'ClearTransactStatus' | 'UniversalOrigin' | 'ExportMessage' | 'LockAsset' | 'UnlockAsset' | 'NoteUnlockable' | 'RequestUnlock' | 'SetFeesMode' | 'SetTopic' | 'ClearTopic' | 'AliasOrigin' | 'UnpaidExecution'; -} - -/** @name StagingXcmV3Junction */ -export interface StagingXcmV3Junction extends Enum { - readonly isParachain: boolean; - readonly asParachain: Compact; - readonly isAccountId32: boolean; - readonly asAccountId32: { - readonly network: Option; - readonly id: U8aFixed; - } & Struct; - readonly isAccountIndex64: boolean; - readonly asAccountIndex64: { - readonly network: Option; - readonly index: Compact; - } & Struct; - readonly isAccountKey20: boolean; - readonly asAccountKey20: { - readonly network: Option; - readonly key: U8aFixed; - } & Struct; - readonly isPalletInstance: boolean; - readonly asPalletInstance: u8; - readonly isGeneralIndex: boolean; - readonly asGeneralIndex: Compact; - readonly isGeneralKey: boolean; - readonly asGeneralKey: { - readonly length: u8; - readonly data: U8aFixed; - } & Struct; - readonly isOnlyChild: boolean; - readonly isPlurality: boolean; - readonly asPlurality: { - readonly id: StagingXcmV3JunctionBodyId; - readonly part: StagingXcmV3JunctionBodyPart; - } & Struct; - readonly isGlobalConsensus: boolean; - readonly asGlobalConsensus: StagingXcmV3JunctionNetworkId; - readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality' | 'GlobalConsensus'; -} - -/** @name StagingXcmV3JunctionBodyId */ -export interface StagingXcmV3JunctionBodyId extends Enum { - readonly isUnit: boolean; - readonly isMoniker: boolean; - readonly asMoniker: U8aFixed; - readonly isIndex: boolean; - readonly asIndex: Compact; - readonly isExecutive: boolean; - readonly isTechnical: boolean; - readonly isLegislative: boolean; - readonly isJudicial: boolean; - readonly isDefense: boolean; - readonly isAdministration: boolean; - readonly isTreasury: boolean; - readonly type: 'Unit' | 'Moniker' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury'; -} - -/** @name StagingXcmV3JunctionBodyPart */ -export interface StagingXcmV3JunctionBodyPart extends Enum { - readonly isVoice: boolean; - readonly isMembers: boolean; - readonly asMembers: { - readonly count: Compact; - } & Struct; - readonly isFraction: boolean; - readonly asFraction: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly isAtLeastProportion: boolean; - readonly asAtLeastProportion: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly isMoreThanProportion: boolean; - readonly asMoreThanProportion: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion'; -} - -/** @name StagingXcmV3JunctionNetworkId */ -export interface StagingXcmV3JunctionNetworkId extends Enum { - readonly isByGenesis: boolean; - readonly asByGenesis: U8aFixed; - readonly isByFork: boolean; - readonly asByFork: { - readonly blockNumber: u64; - readonly blockHash: U8aFixed; - } & Struct; - readonly isPolkadot: boolean; - readonly isKusama: boolean; - readonly isWestend: boolean; - readonly isRococo: boolean; - readonly isWococo: boolean; - readonly isEthereum: boolean; - readonly asEthereum: { - readonly chainId: Compact; - } & Struct; - readonly isBitcoinCore: boolean; - readonly isBitcoinCash: boolean; - readonly type: 'ByGenesis' | 'ByFork' | 'Polkadot' | 'Kusama' | 'Westend' | 'Rococo' | 'Wococo' | 'Ethereum' | 'BitcoinCore' | 'BitcoinCash'; -} - -/** @name StagingXcmV3Junctions */ -export interface StagingXcmV3Junctions extends Enum { - readonly isHere: boolean; - readonly isX1: boolean; - readonly asX1: StagingXcmV3Junction; - readonly isX2: boolean; - readonly asX2: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX3: boolean; - readonly asX3: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX4: boolean; - readonly asX4: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX5: boolean; - readonly asX5: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX6: boolean; - readonly asX6: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX7: boolean; - readonly asX7: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX8: boolean; - readonly asX8: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; -} - -/** @name StagingXcmV3MaybeErrorCode */ -export interface StagingXcmV3MaybeErrorCode extends Enum { - readonly isSuccess: boolean; - readonly isError: boolean; - readonly asError: Bytes; - readonly isTruncatedError: boolean; - readonly asTruncatedError: Bytes; - readonly type: 'Success' | 'Error' | 'TruncatedError'; -} - -/** @name StagingXcmV3MultiAsset */ -export interface StagingXcmV3MultiAsset extends Struct { - readonly id: StagingXcmV3MultiassetAssetId; - readonly fun: StagingXcmV3MultiassetFungibility; -} - -/** @name StagingXcmV3MultiassetAssetId */ -export interface StagingXcmV3MultiassetAssetId extends Enum { - readonly isConcrete: boolean; - readonly asConcrete: StagingXcmV3MultiLocation; - readonly isAbstract: boolean; - readonly asAbstract: U8aFixed; - readonly type: 'Concrete' | 'Abstract'; -} - -/** @name StagingXcmV3MultiassetAssetInstance */ -export interface StagingXcmV3MultiassetAssetInstance extends Enum { - readonly isUndefined: boolean; - readonly isIndex: boolean; - readonly asIndex: Compact; - readonly isArray4: boolean; - readonly asArray4: U8aFixed; - readonly isArray8: boolean; - readonly asArray8: U8aFixed; - readonly isArray16: boolean; - readonly asArray16: U8aFixed; - readonly isArray32: boolean; - readonly asArray32: U8aFixed; - readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32'; -} - -/** @name StagingXcmV3MultiassetFungibility */ -export interface StagingXcmV3MultiassetFungibility extends Enum { - readonly isFungible: boolean; - readonly asFungible: Compact; - readonly isNonFungible: boolean; - readonly asNonFungible: StagingXcmV3MultiassetAssetInstance; - readonly type: 'Fungible' | 'NonFungible'; -} - -/** @name StagingXcmV3MultiassetMultiAssetFilter */ -export interface StagingXcmV3MultiassetMultiAssetFilter extends Enum { - readonly isDefinite: boolean; - readonly asDefinite: StagingXcmV3MultiassetMultiAssets; - readonly isWild: boolean; - readonly asWild: StagingXcmV3MultiassetWildMultiAsset; - readonly type: 'Definite' | 'Wild'; -} - -/** @name StagingXcmV3MultiassetMultiAssets */ -export interface StagingXcmV3MultiassetMultiAssets extends Vec {} - -/** @name StagingXcmV3MultiassetWildFungibility */ -export interface StagingXcmV3MultiassetWildFungibility extends Enum { - readonly isFungible: boolean; - readonly isNonFungible: boolean; - readonly type: 'Fungible' | 'NonFungible'; -} - -/** @name StagingXcmV3MultiassetWildMultiAsset */ -export interface StagingXcmV3MultiassetWildMultiAsset extends Enum { - readonly isAll: boolean; - readonly isAllOf: boolean; - readonly asAllOf: { - readonly id: StagingXcmV3MultiassetAssetId; - readonly fun: StagingXcmV3MultiassetWildFungibility; - } & Struct; - readonly isAllCounted: boolean; - readonly asAllCounted: Compact; - readonly isAllOfCounted: boolean; - readonly asAllOfCounted: { - readonly id: StagingXcmV3MultiassetAssetId; - readonly fun: StagingXcmV3MultiassetWildFungibility; - readonly count: Compact; - } & Struct; - readonly type: 'All' | 'AllOf' | 'AllCounted' | 'AllOfCounted'; -} - -/** @name StagingXcmV3MultiLocation */ -export interface StagingXcmV3MultiLocation extends Struct { - readonly parents: u8; - readonly interior: StagingXcmV3Junctions; -} - -/** @name StagingXcmV3PalletInfo */ -export interface StagingXcmV3PalletInfo extends Struct { - readonly index: Compact; - readonly name: Bytes; - readonly moduleName: Bytes; - readonly major: Compact; - readonly minor: Compact; - readonly patch: Compact; -} - -/** @name StagingXcmV3QueryResponseInfo */ -export interface StagingXcmV3QueryResponseInfo extends Struct { - readonly destination: StagingXcmV3MultiLocation; - readonly queryId: Compact; - readonly maxWeight: SpWeightsWeightV2Weight; -} - -/** @name StagingXcmV3Response */ -export interface StagingXcmV3Response extends Enum { - readonly isNull: boolean; - readonly isAssets: boolean; - readonly asAssets: StagingXcmV3MultiassetMultiAssets; - readonly isExecutionResult: boolean; - readonly asExecutionResult: Option>; - readonly isVersion: boolean; - readonly asVersion: u32; - readonly isPalletsInfo: boolean; - readonly asPalletsInfo: Vec; - readonly isDispatchResult: boolean; - readonly asDispatchResult: StagingXcmV3MaybeErrorCode; - readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version' | 'PalletsInfo' | 'DispatchResult'; -} - -/** @name StagingXcmV3TraitsError */ -export interface StagingXcmV3TraitsError extends Enum { - readonly isOverflow: boolean; - readonly isUnimplemented: boolean; - readonly isUntrustedReserveLocation: boolean; - readonly isUntrustedTeleportLocation: boolean; - readonly isLocationFull: boolean; - readonly isLocationNotInvertible: boolean; - readonly isBadOrigin: boolean; - readonly isInvalidLocation: boolean; - readonly isAssetNotFound: boolean; - readonly isFailedToTransactAsset: boolean; - readonly isNotWithdrawable: boolean; - readonly isLocationCannotHold: boolean; - readonly isExceedsMaxMessageSize: boolean; - readonly isDestinationUnsupported: boolean; - readonly isTransport: boolean; - readonly isUnroutable: boolean; - readonly isUnknownClaim: boolean; - readonly isFailedToDecode: boolean; - readonly isMaxWeightInvalid: boolean; - readonly isNotHoldingFees: boolean; - readonly isTooExpensive: boolean; - readonly isTrap: boolean; - readonly asTrap: u64; - readonly isExpectationFalse: boolean; - readonly isPalletNotFound: boolean; - readonly isNameMismatch: boolean; - readonly isVersionIncompatible: boolean; - readonly isHoldingWouldOverflow: boolean; - readonly isExportError: boolean; - readonly isReanchorFailed: boolean; - readonly isNoDeal: boolean; - readonly isFeesNotMet: boolean; - readonly isLockError: boolean; - readonly isNoPermission: boolean; - readonly isUnanchored: boolean; - readonly isNotDepositable: boolean; - readonly isUnhandledXcmVersion: boolean; - readonly isWeightLimitReached: boolean; - readonly asWeightLimitReached: SpWeightsWeightV2Weight; - readonly isBarrier: boolean; - readonly isWeightNotComputable: boolean; - readonly isExceedsStackLimit: boolean; - readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'LocationFull' | 'LocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'ExpectationFalse' | 'PalletNotFound' | 'NameMismatch' | 'VersionIncompatible' | 'HoldingWouldOverflow' | 'ExportError' | 'ReanchorFailed' | 'NoDeal' | 'FeesNotMet' | 'LockError' | 'NoPermission' | 'Unanchored' | 'NotDepositable' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable' | 'ExceedsStackLimit'; -} - -/** @name StagingXcmV3TraitsOutcome */ -export interface StagingXcmV3TraitsOutcome extends Enum { - readonly isComplete: boolean; - readonly asComplete: SpWeightsWeightV2Weight; - readonly isIncomplete: boolean; - readonly asIncomplete: ITuple<[SpWeightsWeightV2Weight, StagingXcmV3TraitsError]>; - readonly isError: boolean; - readonly asError: StagingXcmV3TraitsError; - readonly type: 'Complete' | 'Incomplete' | 'Error'; -} - -/** @name StagingXcmV3WeightLimit */ -export interface StagingXcmV3WeightLimit extends Enum { - readonly isUnlimited: boolean; - readonly isLimited: boolean; - readonly asLimited: SpWeightsWeightV2Weight; - readonly type: 'Unlimited' | 'Limited'; -} - -/** @name StagingXcmV3Xcm */ -export interface StagingXcmV3Xcm extends Vec {} - -/** @name StagingXcmVersionedAssetId */ -export interface StagingXcmVersionedAssetId extends Enum { - readonly isV3: boolean; - readonly asV3: StagingXcmV3MultiassetAssetId; - readonly type: 'V3'; -} - -/** @name StagingXcmVersionedMultiAsset */ -export interface StagingXcmVersionedMultiAsset extends Enum { - readonly isV2: boolean; - readonly asV2: StagingXcmV2MultiAsset; - readonly isV3: boolean; - readonly asV3: StagingXcmV3MultiAsset; - readonly type: 'V2' | 'V3'; -} - -/** @name StagingXcmVersionedMultiAssets */ -export interface StagingXcmVersionedMultiAssets extends Enum { - readonly isV2: boolean; - readonly asV2: StagingXcmV2MultiassetMultiAssets; - readonly isV3: boolean; - readonly asV3: StagingXcmV3MultiassetMultiAssets; - readonly type: 'V2' | 'V3'; -} - -/** @name StagingXcmVersionedMultiLocation */ -export interface StagingXcmVersionedMultiLocation extends Enum { - readonly isV2: boolean; - readonly asV2: StagingXcmV2MultiLocation; - readonly isV3: boolean; - readonly asV3: StagingXcmV3MultiLocation; - readonly type: 'V2' | 'V3'; -} - -/** @name StagingXcmVersionedResponse */ -export interface StagingXcmVersionedResponse extends Enum { - readonly isV2: boolean; - readonly asV2: StagingXcmV2Response; - readonly isV3: boolean; - readonly asV3: StagingXcmV3Response; - readonly type: 'V2' | 'V3'; -} - -/** @name StagingXcmVersionedXcm */ -export interface StagingXcmVersionedXcm extends Enum { - readonly isV2: boolean; - readonly asV2: StagingXcmV2Xcm; - readonly isV3: boolean; - readonly asV3: StagingXcmV3Xcm; - readonly type: 'V2' | 'V3'; -} - -/** @name UpDataStructsAccessMode */ -export interface UpDataStructsAccessMode extends Enum { - readonly isNormal: boolean; - readonly isAllowList: boolean; - readonly type: 'Normal' | 'AllowList'; -} - -/** @name UpDataStructsCollection */ -export interface UpDataStructsCollection extends Struct { - readonly owner: AccountId32; - readonly mode: UpDataStructsCollectionMode; - readonly name: Vec; - readonly description: Vec; - readonly tokenPrefix: Bytes; - readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; - readonly limits: UpDataStructsCollectionLimits; - readonly permissions: UpDataStructsCollectionPermissions; - readonly flags: U8aFixed; -} - -/** @name UpDataStructsCollectionLimits */ -export interface UpDataStructsCollectionLimits extends Struct { - readonly accountTokenOwnershipLimit: Option; - readonly sponsoredDataSize: Option; - readonly sponsoredDataRateLimit: Option; - readonly tokenLimit: Option; - readonly sponsorTransferTimeout: Option; - readonly sponsorApproveTimeout: Option; - readonly ownerCanTransfer: Option; - readonly ownerCanDestroy: Option; - readonly transfersEnabled: Option; -} - -/** @name UpDataStructsCollectionMode */ -export interface UpDataStructsCollectionMode extends Enum { - readonly isNft: boolean; - readonly isFungible: boolean; - readonly asFungible: u8; - readonly isReFungible: boolean; - readonly type: 'Nft' | 'Fungible' | 'ReFungible'; -} - -/** @name UpDataStructsCollectionPermissions */ -export interface UpDataStructsCollectionPermissions extends Struct { - readonly access: Option; - readonly mintMode: Option; - readonly nesting: Option; -} - -/** @name UpDataStructsCollectionStats */ -export interface UpDataStructsCollectionStats extends Struct { - readonly created: u32; - readonly destroyed: u32; - readonly alive: u32; -} - -/** @name UpDataStructsCreateCollectionData */ -export interface UpDataStructsCreateCollectionData extends Struct { - readonly mode: UpDataStructsCollectionMode; - readonly access: Option; - readonly name: Vec; - readonly description: Vec; - readonly tokenPrefix: Bytes; - readonly limits: Option; - readonly permissions: Option; - readonly tokenPropertyPermissions: Vec; - readonly properties: Vec; - readonly adminList: Vec; - readonly pendingSponsor: Option; - readonly flags: U8aFixed; -} - -/** @name UpDataStructsCreateFungibleData */ -export interface UpDataStructsCreateFungibleData extends Struct { - readonly value: u128; -} - -/** @name UpDataStructsCreateItemData */ -export interface UpDataStructsCreateItemData extends Enum { - readonly isNft: boolean; - readonly asNft: UpDataStructsCreateNftData; - readonly isFungible: boolean; - readonly asFungible: UpDataStructsCreateFungibleData; - readonly isReFungible: boolean; - readonly asReFungible: UpDataStructsCreateReFungibleData; - readonly type: 'Nft' | 'Fungible' | 'ReFungible'; -} - -/** @name UpDataStructsCreateItemExData */ -export interface UpDataStructsCreateItemExData extends Enum { - readonly isNft: boolean; - readonly asNft: Vec; - readonly isFungible: boolean; - readonly asFungible: BTreeMap; - readonly isRefungibleMultipleItems: boolean; - readonly asRefungibleMultipleItems: Vec; - readonly isRefungibleMultipleOwners: boolean; - readonly asRefungibleMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners; - readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners'; -} - -/** @name UpDataStructsCreateNftData */ -export interface UpDataStructsCreateNftData extends Struct { - readonly properties: Vec; -} - -/** @name UpDataStructsCreateNftExData */ -export interface UpDataStructsCreateNftExData extends Struct { - readonly properties: Vec; - readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; -} - -/** @name UpDataStructsCreateReFungibleData */ -export interface UpDataStructsCreateReFungibleData extends Struct { - readonly pieces: u128; - readonly properties: Vec; -} - -/** @name UpDataStructsCreateRefungibleExMultipleOwners */ -export interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct { - readonly users: BTreeMap; - readonly properties: Vec; -} - -/** @name UpDataStructsCreateRefungibleExSingleOwner */ -export interface UpDataStructsCreateRefungibleExSingleOwner extends Struct { - readonly user: PalletEvmAccountBasicCrossAccountIdRepr; - readonly pieces: u128; - readonly properties: Vec; -} - -/** @name UpDataStructsNestingPermissions */ -export interface UpDataStructsNestingPermissions extends Struct { - readonly tokenOwner: bool; - readonly collectionAdmin: bool; - readonly restricted: Option; -} - -/** @name UpDataStructsOwnerRestrictedSet */ -export interface UpDataStructsOwnerRestrictedSet extends BTreeSet {} - -/** @name UpDataStructsProperties */ -export interface UpDataStructsProperties extends Struct { - readonly map: UpDataStructsPropertiesMapBoundedVec; - readonly consumedSpace: u32; - readonly reserved: u32; -} - -/** @name UpDataStructsPropertiesMapBoundedVec */ -export interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap {} - -/** @name UpDataStructsPropertiesMapPropertyPermission */ -export interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap {} - -/** @name UpDataStructsProperty */ -export interface UpDataStructsProperty extends Struct { - readonly key: Bytes; - readonly value: Bytes; -} - -/** @name UpDataStructsPropertyKeyPermission */ -export interface UpDataStructsPropertyKeyPermission extends Struct { - readonly key: Bytes; - readonly permission: UpDataStructsPropertyPermission; -} - -/** @name UpDataStructsPropertyPermission */ -export interface UpDataStructsPropertyPermission extends Struct { - readonly mutable: bool; - readonly collectionAdmin: bool; - readonly tokenOwner: bool; -} - -/** @name UpDataStructsPropertyScope */ -export interface UpDataStructsPropertyScope extends Enum { - readonly isNone: boolean; - readonly isRmrk: boolean; - readonly type: 'None' | 'Rmrk'; -} - -/** @name UpDataStructsRpcCollection */ -export interface UpDataStructsRpcCollection extends Struct { - readonly owner: AccountId32; - readonly mode: UpDataStructsCollectionMode; - readonly name: Vec; - readonly description: Vec; - readonly tokenPrefix: Bytes; - readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; - readonly limits: UpDataStructsCollectionLimits; - readonly permissions: UpDataStructsCollectionPermissions; - readonly tokenPropertyPermissions: Vec; - readonly properties: Vec; - readonly readOnly: bool; - readonly flags: UpDataStructsRpcCollectionFlags; -} - -/** @name UpDataStructsRpcCollectionFlags */ -export interface UpDataStructsRpcCollectionFlags extends Struct { - readonly foreign: bool; - readonly erc721metadata: bool; -} - -/** @name UpDataStructsSponsoringRateLimit */ -export interface UpDataStructsSponsoringRateLimit extends Enum { - readonly isSponsoringDisabled: boolean; - readonly isBlocks: boolean; - readonly asBlocks: u32; - readonly type: 'SponsoringDisabled' | 'Blocks'; -} - -/** @name UpDataStructsSponsorshipStateAccountId32 */ -export interface UpDataStructsSponsorshipStateAccountId32 extends Enum { - readonly isDisabled: boolean; - readonly isUnconfirmed: boolean; - readonly asUnconfirmed: AccountId32; - readonly isConfirmed: boolean; - readonly asConfirmed: AccountId32; - readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; -} - -/** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr */ -export interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum { - readonly isDisabled: boolean; - readonly isUnconfirmed: boolean; - readonly asUnconfirmed: PalletEvmAccountBasicCrossAccountIdRepr; - readonly isConfirmed: boolean; - readonly asConfirmed: PalletEvmAccountBasicCrossAccountIdRepr; - readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; -} - -/** @name UpDataStructsTokenChild */ -export interface UpDataStructsTokenChild extends Struct { - readonly token: u32; - readonly collection: u32; -} - -/** @name UpDataStructsTokenData */ -export interface UpDataStructsTokenData extends Struct { - readonly properties: Vec; - readonly owner: Option; - readonly pieces: u128; -} - -/** @name UpPovEstimateRpcPovInfo */ -export interface UpPovEstimateRpcPovInfo extends Struct { - readonly proofSize: u64; - readonly compactProofSize: u64; - readonly compressedProofSize: u64; - readonly results: Vec, SpRuntimeTransactionValidityTransactionValidityError>>; - readonly keyValues: Vec; -} - -/** @name UpPovEstimateRpcTrieKeyValue */ -export interface UpPovEstimateRpcTrieKeyValue extends Struct { - readonly key: Bytes; - readonly value: Bytes; -} - -export type PHANTOM_DEFAULT = 'default'; --- a/js-packages/types/src/definitions.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -export {default as unique} from './unique/definitions.js'; -export {default as appPromotion} from './appPromotion/definitions.js'; -export {default as povinfo} from './povinfo/definitions.js'; -export {default as default} from './default/definitions.js'; --- a/js-packages/types/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -export * from './types.js'; --- a/js-packages/types/src/lookup.ts +++ /dev/null @@ -1,5165 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -/* eslint-disable sort-keys */ - -export default { - /** - * Lookup3: frame_system::AccountInfo> - **/ - FrameSystemAccountInfo: { - nonce: 'u32', - consumers: 'u32', - providers: 'u32', - sufficients: 'u32', - data: 'PalletBalancesAccountData' - }, - /** - * Lookup5: pallet_balances::types::AccountData - **/ - PalletBalancesAccountData: { - free: 'u128', - reserved: 'u128', - frozen: 'u128', - flags: 'u128' - }, - /** - * Lookup8: frame_support::dispatch::PerDispatchClass - **/ - FrameSupportDispatchPerDispatchClassWeight: { - normal: 'SpWeightsWeightV2Weight', - operational: 'SpWeightsWeightV2Weight', - mandatory: 'SpWeightsWeightV2Weight' - }, - /** - * Lookup9: sp_weights::weight_v2::Weight - **/ - SpWeightsWeightV2Weight: { - refTime: 'Compact', - proofSize: 'Compact' - }, - /** - * Lookup14: sp_runtime::generic::digest::Digest - **/ - SpRuntimeDigest: { - logs: 'Vec' - }, - /** - * Lookup16: sp_runtime::generic::digest::DigestItem - **/ - SpRuntimeDigestDigestItem: { - _enum: { - Other: 'Bytes', - __Unused1: 'Null', - __Unused2: 'Null', - __Unused3: 'Null', - Consensus: '([u8;4],Bytes)', - Seal: '([u8;4],Bytes)', - PreRuntime: '([u8;4],Bytes)', - __Unused7: 'Null', - RuntimeEnvironmentUpdated: 'Null' - } - }, - /** - * Lookup19: frame_system::EventRecord - **/ - FrameSystemEventRecord: { - phase: 'FrameSystemPhase', - event: 'Event', - topics: 'Vec' - }, - /** - * Lookup21: frame_system::pallet::Event - **/ - FrameSystemEvent: { - _enum: { - ExtrinsicSuccess: { - dispatchInfo: 'FrameSupportDispatchDispatchInfo', - }, - ExtrinsicFailed: { - dispatchError: 'SpRuntimeDispatchError', - dispatchInfo: 'FrameSupportDispatchDispatchInfo', - }, - CodeUpdated: 'Null', - NewAccount: { - account: 'AccountId32', - }, - KilledAccount: { - account: 'AccountId32', - }, - Remarked: { - _alias: { - hash_: 'hash', - }, - sender: 'AccountId32', - hash_: 'H256' - } - } - }, - /** - * Lookup22: frame_support::dispatch::DispatchInfo - **/ - FrameSupportDispatchDispatchInfo: { - weight: 'SpWeightsWeightV2Weight', - class: 'FrameSupportDispatchDispatchClass', - paysFee: 'FrameSupportDispatchPays' - }, - /** - * Lookup23: frame_support::dispatch::DispatchClass - **/ - FrameSupportDispatchDispatchClass: { - _enum: ['Normal', 'Operational', 'Mandatory'] - }, - /** - * Lookup24: frame_support::dispatch::Pays - **/ - FrameSupportDispatchPays: { - _enum: ['Yes', 'No'] - }, - /** - * Lookup25: sp_runtime::DispatchError - **/ - SpRuntimeDispatchError: { - _enum: { - Other: 'Null', - CannotLookup: 'Null', - BadOrigin: 'Null', - Module: 'SpRuntimeModuleError', - ConsumerRemaining: 'Null', - NoProviders: 'Null', - TooManyConsumers: 'Null', - Token: 'SpRuntimeTokenError', - Arithmetic: 'SpArithmeticArithmeticError', - Transactional: 'SpRuntimeTransactionalError', - Exhausted: 'Null', - Corruption: 'Null', - Unavailable: 'Null', - RootNotAllowed: 'Null' - } - }, - /** - * Lookup26: sp_runtime::ModuleError - **/ - SpRuntimeModuleError: { - index: 'u8', - error: '[u8;4]' - }, - /** - * Lookup27: sp_runtime::TokenError - **/ - SpRuntimeTokenError: { - _enum: ['FundsUnavailable', 'OnlyProvider', 'BelowMinimum', 'CannotCreate', 'UnknownAsset', 'Frozen', 'Unsupported', 'CannotCreateHold', 'NotExpendable', 'Blocked'] - }, - /** - * Lookup28: sp_arithmetic::ArithmeticError - **/ - SpArithmeticArithmeticError: { - _enum: ['Underflow', 'Overflow', 'DivisionByZero'] - }, - /** - * Lookup29: sp_runtime::TransactionalError - **/ - SpRuntimeTransactionalError: { - _enum: ['LimitReached', 'NoLayer'] - }, - /** - * Lookup30: pallet_state_trie_migration::pallet::Event - **/ - PalletStateTrieMigrationEvent: { - _enum: { - Migrated: { - top: 'u32', - child: 'u32', - compute: 'PalletStateTrieMigrationMigrationCompute', - }, - Slashed: { - who: 'AccountId32', - amount: 'u128', - }, - AutoMigrationFinished: 'Null', - Halted: { - error: 'PalletStateTrieMigrationError' - } - } - }, - /** - * Lookup31: pallet_state_trie_migration::pallet::MigrationCompute - **/ - PalletStateTrieMigrationMigrationCompute: { - _enum: ['Signed', 'Auto'] - }, - /** - * Lookup32: pallet_state_trie_migration::pallet::Error - **/ - PalletStateTrieMigrationError: { - _enum: ['MaxSignedLimits', 'KeyTooLong', 'NotEnoughFunds', 'BadWitness', 'SignedMigrationNotAllowed', 'BadChildRoot'] - }, - /** - * Lookup33: cumulus_pallet_parachain_system::pallet::Event - **/ - CumulusPalletParachainSystemEvent: { - _enum: { - ValidationFunctionStored: 'Null', - ValidationFunctionApplied: { - relayChainBlockNum: 'u32', - }, - ValidationFunctionDiscarded: 'Null', - UpgradeAuthorized: { - codeHash: 'H256', - }, - DownwardMessagesReceived: { - count: 'u32', - }, - DownwardMessagesProcessed: { - weightUsed: 'SpWeightsWeightV2Weight', - dmqHead: 'H256', - }, - UpwardMessageSent: { - messageHash: 'Option<[u8;32]>' - } - } - }, - /** - * Lookup35: pallet_collator_selection::pallet::Event - **/ - PalletCollatorSelectionEvent: { - _enum: { - InvulnerableAdded: { - invulnerable: 'AccountId32', - }, - InvulnerableRemoved: { - invulnerable: 'AccountId32', - }, - LicenseObtained: { - accountId: 'AccountId32', - deposit: 'u128', - }, - LicenseReleased: { - accountId: 'AccountId32', - depositReturned: 'u128', - }, - CandidateAdded: { - accountId: 'AccountId32', - }, - CandidateRemoved: { - accountId: 'AccountId32' - } - } - }, - /** - * Lookup36: pallet_session::pallet::Event - **/ - PalletSessionEvent: { - _enum: { - NewSession: { - sessionIndex: 'u32' - } - } - }, - /** - * Lookup37: pallet_balances::pallet::Event - **/ - PalletBalancesEvent: { - _enum: { - Endowed: { - account: 'AccountId32', - freeBalance: 'u128', - }, - DustLost: { - account: 'AccountId32', - amount: 'u128', - }, - Transfer: { - from: 'AccountId32', - to: 'AccountId32', - amount: 'u128', - }, - BalanceSet: { - who: 'AccountId32', - free: 'u128', - }, - Reserved: { - who: 'AccountId32', - amount: 'u128', - }, - Unreserved: { - who: 'AccountId32', - amount: 'u128', - }, - ReserveRepatriated: { - from: 'AccountId32', - to: 'AccountId32', - amount: 'u128', - destinationStatus: 'FrameSupportTokensMiscBalanceStatus', - }, - Deposit: { - who: 'AccountId32', - amount: 'u128', - }, - Withdraw: { - who: 'AccountId32', - amount: 'u128', - }, - Slashed: { - who: 'AccountId32', - amount: 'u128', - }, - Minted: { - who: 'AccountId32', - amount: 'u128', - }, - Burned: { - who: 'AccountId32', - amount: 'u128', - }, - Suspended: { - who: 'AccountId32', - amount: 'u128', - }, - Restored: { - who: 'AccountId32', - amount: 'u128', - }, - Upgraded: { - who: 'AccountId32', - }, - Issued: { - amount: 'u128', - }, - Rescinded: { - amount: 'u128', - }, - Locked: { - who: 'AccountId32', - amount: 'u128', - }, - Unlocked: { - who: 'AccountId32', - amount: 'u128', - }, - Frozen: { - who: 'AccountId32', - amount: 'u128', - }, - Thawed: { - who: 'AccountId32', - amount: 'u128' - } - } - }, - /** - * Lookup38: frame_support::traits::tokens::misc::BalanceStatus - **/ - FrameSupportTokensMiscBalanceStatus: { - _enum: ['Free', 'Reserved'] - }, - /** - * Lookup39: pallet_transaction_payment::pallet::Event - **/ - PalletTransactionPaymentEvent: { - _enum: { - TransactionFeePaid: { - who: 'AccountId32', - actualFee: 'u128', - tip: 'u128' - } - } - }, - /** - * Lookup40: pallet_treasury::pallet::Event - **/ - PalletTreasuryEvent: { - _enum: { - Proposed: { - proposalIndex: 'u32', - }, - Spending: { - budgetRemaining: 'u128', - }, - Awarded: { - proposalIndex: 'u32', - award: 'u128', - account: 'AccountId32', - }, - Rejected: { - proposalIndex: 'u32', - slashed: 'u128', - }, - Burnt: { - burntFunds: 'u128', - }, - Rollover: { - rolloverBalance: 'u128', - }, - Deposit: { - value: 'u128', - }, - SpendApproved: { - proposalIndex: 'u32', - amount: 'u128', - beneficiary: 'AccountId32', - }, - UpdatedInactive: { - reactivated: 'u128', - deactivated: 'u128' - } - } - }, - /** - * Lookup41: pallet_sudo::pallet::Event - **/ - PalletSudoEvent: { - _enum: { - Sudid: { - sudoResult: 'Result', - }, - KeyChanged: { - oldSudoer: 'Option', - }, - SudoAsDone: { - sudoResult: 'Result' - } - } - }, - /** - * Lookup45: orml_vesting::module::Event - **/ - OrmlVestingModuleEvent: { - _enum: { - VestingScheduleAdded: { - from: 'AccountId32', - to: 'AccountId32', - vestingSchedule: 'OrmlVestingVestingSchedule', - }, - Claimed: { - who: 'AccountId32', - amount: 'u128', - }, - VestingSchedulesUpdated: { - who: 'AccountId32' - } - } - }, - /** - * Lookup46: orml_vesting::VestingSchedule - **/ - OrmlVestingVestingSchedule: { - start: 'u32', - period: 'u32', - periodCount: 'u32', - perPeriod: 'Compact' - }, - /** - * Lookup48: orml_xtokens::module::Event - **/ - OrmlXtokensModuleEvent: { - _enum: { - TransferredMultiAssets: { - sender: 'AccountId32', - assets: 'StagingXcmV3MultiassetMultiAssets', - fee: 'StagingXcmV3MultiAsset', - dest: 'StagingXcmV3MultiLocation' - } - } - }, - /** - * Lookup49: staging_xcm::v3::multiasset::MultiAssets - **/ - StagingXcmV3MultiassetMultiAssets: 'Vec', - /** - * Lookup51: staging_xcm::v3::multiasset::MultiAsset - **/ - StagingXcmV3MultiAsset: { - id: 'StagingXcmV3MultiassetAssetId', - fun: 'StagingXcmV3MultiassetFungibility' - }, - /** - * Lookup52: staging_xcm::v3::multiasset::AssetId - **/ - StagingXcmV3MultiassetAssetId: { - _enum: { - Concrete: 'StagingXcmV3MultiLocation', - Abstract: '[u8;32]' - } - }, - /** - * Lookup53: staging_xcm::v3::multilocation::MultiLocation - **/ - StagingXcmV3MultiLocation: { - parents: 'u8', - interior: 'StagingXcmV3Junctions' - }, - /** - * Lookup54: staging_xcm::v3::junctions::Junctions - **/ - StagingXcmV3Junctions: { - _enum: { - Here: 'Null', - X1: 'StagingXcmV3Junction', - X2: '(StagingXcmV3Junction,StagingXcmV3Junction)', - X3: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)', - X4: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)', - X5: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)', - X6: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)', - X7: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)', - X8: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)' - } - }, - /** - * Lookup55: staging_xcm::v3::junction::Junction - **/ - StagingXcmV3Junction: { - _enum: { - Parachain: 'Compact', - AccountId32: { - network: 'Option', - id: '[u8;32]', - }, - AccountIndex64: { - network: 'Option', - index: 'Compact', - }, - AccountKey20: { - network: 'Option', - key: '[u8;20]', - }, - PalletInstance: 'u8', - GeneralIndex: 'Compact', - GeneralKey: { - length: 'u8', - data: '[u8;32]', - }, - OnlyChild: 'Null', - Plurality: { - id: 'StagingXcmV3JunctionBodyId', - part: 'StagingXcmV3JunctionBodyPart', - }, - GlobalConsensus: 'StagingXcmV3JunctionNetworkId' - } - }, - /** - * Lookup58: staging_xcm::v3::junction::NetworkId - **/ - StagingXcmV3JunctionNetworkId: { - _enum: { - ByGenesis: '[u8;32]', - ByFork: { - blockNumber: 'u64', - blockHash: '[u8;32]', - }, - Polkadot: 'Null', - Kusama: 'Null', - Westend: 'Null', - Rococo: 'Null', - Wococo: 'Null', - Ethereum: { - chainId: 'Compact', - }, - BitcoinCore: 'Null', - BitcoinCash: 'Null' - } - }, - /** - * Lookup60: staging_xcm::v3::junction::BodyId - **/ - StagingXcmV3JunctionBodyId: { - _enum: { - Unit: 'Null', - Moniker: '[u8;4]', - Index: 'Compact', - Executive: 'Null', - Technical: 'Null', - Legislative: 'Null', - Judicial: 'Null', - Defense: 'Null', - Administration: 'Null', - Treasury: 'Null' - } - }, - /** - * Lookup61: staging_xcm::v3::junction::BodyPart - **/ - StagingXcmV3JunctionBodyPart: { - _enum: { - Voice: 'Null', - Members: { - count: 'Compact', - }, - Fraction: { - nom: 'Compact', - denom: 'Compact', - }, - AtLeastProportion: { - nom: 'Compact', - denom: 'Compact', - }, - MoreThanProportion: { - nom: 'Compact', - denom: 'Compact' - } - } - }, - /** - * Lookup62: staging_xcm::v3::multiasset::Fungibility - **/ - StagingXcmV3MultiassetFungibility: { - _enum: { - Fungible: 'Compact', - NonFungible: 'StagingXcmV3MultiassetAssetInstance' - } - }, - /** - * Lookup63: staging_xcm::v3::multiasset::AssetInstance - **/ - StagingXcmV3MultiassetAssetInstance: { - _enum: { - Undefined: 'Null', - Index: 'Compact', - Array4: '[u8;4]', - Array8: '[u8;8]', - Array16: '[u8;16]', - Array32: '[u8;32]' - } - }, - /** - * Lookup66: orml_tokens::module::Event - **/ - OrmlTokensModuleEvent: { - _enum: { - Endowed: { - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - amount: 'u128', - }, - DustLost: { - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - amount: 'u128', - }, - Transfer: { - currencyId: 'PalletForeignAssetsAssetId', - from: 'AccountId32', - to: 'AccountId32', - amount: 'u128', - }, - Reserved: { - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - amount: 'u128', - }, - Unreserved: { - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - amount: 'u128', - }, - ReserveRepatriated: { - currencyId: 'PalletForeignAssetsAssetId', - from: 'AccountId32', - to: 'AccountId32', - amount: 'u128', - status: 'FrameSupportTokensMiscBalanceStatus', - }, - BalanceSet: { - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - free: 'u128', - reserved: 'u128', - }, - TotalIssuanceSet: { - currencyId: 'PalletForeignAssetsAssetId', - amount: 'u128', - }, - Withdrawn: { - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - amount: 'u128', - }, - Slashed: { - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - freeAmount: 'u128', - reservedAmount: 'u128', - }, - Deposited: { - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - amount: 'u128', - }, - LockSet: { - lockId: '[u8;8]', - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - amount: 'u128', - }, - LockRemoved: { - lockId: '[u8;8]', - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - }, - Locked: { - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - amount: 'u128', - }, - Unlocked: { - currencyId: 'PalletForeignAssetsAssetId', - who: 'AccountId32', - amount: 'u128', - }, - Issued: { - currencyId: 'PalletForeignAssetsAssetId', - amount: 'u128', - }, - Rescinded: { - currencyId: 'PalletForeignAssetsAssetId', - amount: 'u128' - } - } - }, - /** - * Lookup67: pallet_foreign_assets::AssetId - **/ - PalletForeignAssetsAssetId: { - _enum: { - ForeignAssetId: 'u32', - NativeAssetId: 'PalletForeignAssetsNativeCurrency' - } - }, - /** - * Lookup68: pallet_foreign_assets::NativeCurrency - **/ - PalletForeignAssetsNativeCurrency: { - _enum: ['Here', 'Parent'] - }, - /** - * Lookup69: pallet_identity::pallet::Event - **/ - PalletIdentityEvent: { - _enum: { - IdentitySet: { - who: 'AccountId32', - }, - IdentityCleared: { - who: 'AccountId32', - deposit: 'u128', - }, - IdentityKilled: { - who: 'AccountId32', - deposit: 'u128', - }, - IdentitiesInserted: { - amount: 'u32', - }, - IdentitiesRemoved: { - amount: 'u32', - }, - JudgementRequested: { - who: 'AccountId32', - registrarIndex: 'u32', - }, - JudgementUnrequested: { - who: 'AccountId32', - registrarIndex: 'u32', - }, - JudgementGiven: { - target: 'AccountId32', - registrarIndex: 'u32', - }, - RegistrarAdded: { - registrarIndex: 'u32', - }, - SubIdentityAdded: { - sub: 'AccountId32', - main: 'AccountId32', - deposit: 'u128', - }, - SubIdentityRemoved: { - sub: 'AccountId32', - main: 'AccountId32', - deposit: 'u128', - }, - SubIdentityRevoked: { - sub: 'AccountId32', - main: 'AccountId32', - deposit: 'u128', - }, - SubIdentitiesInserted: { - amount: 'u32' - } - } - }, - /** - * Lookup70: pallet_preimage::pallet::Event - **/ - PalletPreimageEvent: { - _enum: { - Noted: { - _alias: { - hash_: 'hash', - }, - hash_: 'H256', - }, - Requested: { - _alias: { - hash_: 'hash', - }, - hash_: 'H256', - }, - Cleared: { - _alias: { - hash_: 'hash', - }, - hash_: 'H256' - } - } - }, - /** - * Lookup71: pallet_democracy::pallet::Event - **/ - PalletDemocracyEvent: { - _enum: { - Proposed: { - proposalIndex: 'u32', - deposit: 'u128', - }, - Tabled: { - proposalIndex: 'u32', - deposit: 'u128', - }, - ExternalTabled: 'Null', - Started: { - refIndex: 'u32', - threshold: 'PalletDemocracyVoteThreshold', - }, - Passed: { - refIndex: 'u32', - }, - NotPassed: { - refIndex: 'u32', - }, - Cancelled: { - refIndex: 'u32', - }, - Delegated: { - who: 'AccountId32', - target: 'AccountId32', - }, - Undelegated: { - account: 'AccountId32', - }, - Vetoed: { - who: 'AccountId32', - proposalHash: 'H256', - until: 'u32', - }, - Blacklisted: { - proposalHash: 'H256', - }, - Voted: { - voter: 'AccountId32', - refIndex: 'u32', - vote: 'PalletDemocracyVoteAccountVote', - }, - Seconded: { - seconder: 'AccountId32', - propIndex: 'u32', - }, - ProposalCanceled: { - propIndex: 'u32', - }, - MetadataSet: { - _alias: { - hash_: 'hash', - }, - owner: 'PalletDemocracyMetadataOwner', - hash_: 'H256', - }, - MetadataCleared: { - _alias: { - hash_: 'hash', - }, - owner: 'PalletDemocracyMetadataOwner', - hash_: 'H256', - }, - MetadataTransferred: { - _alias: { - hash_: 'hash', - }, - prevOwner: 'PalletDemocracyMetadataOwner', - owner: 'PalletDemocracyMetadataOwner', - hash_: 'H256' - } - } - }, - /** - * Lookup72: pallet_democracy::vote_threshold::VoteThreshold - **/ - PalletDemocracyVoteThreshold: { - _enum: ['SuperMajorityApprove', 'SuperMajorityAgainst', 'SimpleMajority'] - }, - /** - * Lookup73: pallet_democracy::vote::AccountVote - **/ - PalletDemocracyVoteAccountVote: { - _enum: { - Standard: { - vote: 'Vote', - balance: 'u128', - }, - Split: { - aye: 'u128', - nay: 'u128' - } - } - }, - /** - * Lookup75: pallet_democracy::types::MetadataOwner - **/ - PalletDemocracyMetadataOwner: { - _enum: { - External: 'Null', - Proposal: 'u32', - Referendum: 'u32' - } - }, - /** - * Lookup76: pallet_collective::pallet::Event - **/ - PalletCollectiveEvent: { - _enum: { - Proposed: { - account: 'AccountId32', - proposalIndex: 'u32', - proposalHash: 'H256', - threshold: 'u32', - }, - Voted: { - account: 'AccountId32', - proposalHash: 'H256', - voted: 'bool', - yes: 'u32', - no: 'u32', - }, - Approved: { - proposalHash: 'H256', - }, - Disapproved: { - proposalHash: 'H256', - }, - Executed: { - proposalHash: 'H256', - result: 'Result', - }, - MemberExecuted: { - proposalHash: 'H256', - result: 'Result', - }, - Closed: { - proposalHash: 'H256', - yes: 'u32', - no: 'u32' - } - } - }, - /** - * Lookup79: pallet_membership::pallet::Event - **/ - PalletMembershipEvent: { - _enum: ['MemberAdded', 'MemberRemoved', 'MembersSwapped', 'MembersReset', 'KeyChanged', 'Dummy'] - }, - /** - * Lookup81: pallet_ranked_collective::pallet::Event - **/ - PalletRankedCollectiveEvent: { - _enum: { - MemberAdded: { - who: 'AccountId32', - }, - RankChanged: { - who: 'AccountId32', - rank: 'u16', - }, - MemberRemoved: { - who: 'AccountId32', - rank: 'u16', - }, - Voted: { - who: 'AccountId32', - poll: 'u32', - vote: 'PalletRankedCollectiveVoteRecord', - tally: 'PalletRankedCollectiveTally' - } - } - }, - /** - * Lookup83: pallet_ranked_collective::VoteRecord - **/ - PalletRankedCollectiveVoteRecord: { - _enum: { - Aye: 'u32', - Nay: 'u32' - } - }, - /** - * Lookup84: pallet_ranked_collective::Tally - **/ - PalletRankedCollectiveTally: { - bareAyes: 'u32', - ayes: 'u32', - nays: 'u32' - }, - /** - * Lookup85: pallet_referenda::pallet::Event - **/ - PalletReferendaEvent: { - _enum: { - Submitted: { - index: 'u32', - track: 'u16', - proposal: 'FrameSupportPreimagesBounded', - }, - DecisionDepositPlaced: { - index: 'u32', - who: 'AccountId32', - amount: 'u128', - }, - DecisionDepositRefunded: { - index: 'u32', - who: 'AccountId32', - amount: 'u128', - }, - DepositSlashed: { - who: 'AccountId32', - amount: 'u128', - }, - DecisionStarted: { - index: 'u32', - track: 'u16', - proposal: 'FrameSupportPreimagesBounded', - tally: 'PalletRankedCollectiveTally', - }, - ConfirmStarted: { - index: 'u32', - }, - ConfirmAborted: { - index: 'u32', - }, - Confirmed: { - index: 'u32', - tally: 'PalletRankedCollectiveTally', - }, - Approved: { - index: 'u32', - }, - Rejected: { - index: 'u32', - tally: 'PalletRankedCollectiveTally', - }, - TimedOut: { - index: 'u32', - tally: 'PalletRankedCollectiveTally', - }, - Cancelled: { - index: 'u32', - tally: 'PalletRankedCollectiveTally', - }, - Killed: { - index: 'u32', - tally: 'PalletRankedCollectiveTally', - }, - SubmissionDepositRefunded: { - index: 'u32', - who: 'AccountId32', - amount: 'u128', - }, - MetadataSet: { - _alias: { - hash_: 'hash', - }, - index: 'u32', - hash_: 'H256', - }, - MetadataCleared: { - _alias: { - hash_: 'hash', - }, - index: 'u32', - hash_: 'H256' - } - } - }, - /** - * Lookup86: frame_support::traits::preimages::Bounded - **/ - FrameSupportPreimagesBounded: { - _enum: { - Legacy: { - _alias: { - hash_: 'hash', - }, - hash_: 'H256', - }, - Inline: 'Bytes', - Lookup: { - _alias: { - hash_: 'hash', - }, - hash_: 'H256', - len: 'u32' - } - } - }, - /** - * Lookup88: frame_system::pallet::Call - **/ - FrameSystemCall: { - _enum: { - remark: { - remark: 'Bytes', - }, - set_heap_pages: { - pages: 'u64', - }, - set_code: { - code: 'Bytes', - }, - set_code_without_checks: { - code: 'Bytes', - }, - set_storage: { - items: 'Vec<(Bytes,Bytes)>', - }, - kill_storage: { - _alias: { - keys_: 'keys', - }, - keys_: 'Vec', - }, - kill_prefix: { - prefix: 'Bytes', - subkeys: 'u32', - }, - remark_with_event: { - remark: 'Bytes' - } - } - }, - /** - * Lookup92: pallet_state_trie_migration::pallet::Call - **/ - PalletStateTrieMigrationCall: { - _enum: { - control_auto_migration: { - maybeConfig: 'Option', - }, - continue_migrate: { - limits: 'PalletStateTrieMigrationMigrationLimits', - realSizeUpper: 'u32', - witnessTask: 'PalletStateTrieMigrationMigrationTask', - }, - migrate_custom_top: { - _alias: { - keys_: 'keys', - }, - keys_: 'Vec', - witnessSize: 'u32', - }, - migrate_custom_child: { - root: 'Bytes', - childKeys: 'Vec', - totalSize: 'u32', - }, - set_signed_max_limits: { - limits: 'PalletStateTrieMigrationMigrationLimits', - }, - force_set_progress: { - progressTop: 'PalletStateTrieMigrationProgress', - progressChild: 'PalletStateTrieMigrationProgress' - } - } - }, - /** - * Lookup94: pallet_state_trie_migration::pallet::MigrationLimits - **/ - PalletStateTrieMigrationMigrationLimits: { - _alias: { - size_: 'size' - }, - size_: 'u32', - item: 'u32' - }, - /** - * Lookup95: pallet_state_trie_migration::pallet::MigrationTask - **/ - PalletStateTrieMigrationMigrationTask: { - _alias: { - size_: 'size' - }, - progressTop: 'PalletStateTrieMigrationProgress', - progressChild: 'PalletStateTrieMigrationProgress', - size_: 'u32', - topItems: 'u32', - childItems: 'u32' - }, - /** - * Lookup96: pallet_state_trie_migration::pallet::Progress - **/ - PalletStateTrieMigrationProgress: { - _enum: { - ToStart: 'Null', - LastKey: 'Bytes', - Complete: 'Null' - } - }, - /** - * Lookup98: cumulus_pallet_parachain_system::pallet::Call - **/ - CumulusPalletParachainSystemCall: { - _enum: { - set_validation_data: { - data: 'CumulusPrimitivesParachainInherentParachainInherentData', - }, - sudo_send_upward_message: { - message: 'Bytes', - }, - authorize_upgrade: { - codeHash: 'H256', - checkVersion: 'bool', - }, - enact_authorized_upgrade: { - code: 'Bytes' - } - } - }, - /** - * Lookup99: cumulus_primitives_parachain_inherent::ParachainInherentData - **/ - CumulusPrimitivesParachainInherentParachainInherentData: { - validationData: 'PolkadotPrimitivesV5PersistedValidationData', - relayChainState: 'SpTrieStorageProof', - downwardMessages: 'Vec', - horizontalMessages: 'BTreeMap>' - }, - /** - * Lookup100: polkadot_primitives::v5::PersistedValidationData - **/ - PolkadotPrimitivesV5PersistedValidationData: { - parentHead: 'Bytes', - relayParentNumber: 'u32', - relayParentStorageRoot: 'H256', - maxPovSize: 'u32' - }, - /** - * Lookup102: sp_trie::storage_proof::StorageProof - **/ - SpTrieStorageProof: { - trieNodes: 'BTreeSet' - }, - /** - * Lookup105: polkadot_core_primitives::InboundDownwardMessage - **/ - PolkadotCorePrimitivesInboundDownwardMessage: { - sentAt: 'u32', - msg: 'Bytes' - }, - /** - * Lookup109: polkadot_core_primitives::InboundHrmpMessage - **/ - PolkadotCorePrimitivesInboundHrmpMessage: { - sentAt: 'u32', - data: 'Bytes' - }, - /** - * Lookup112: parachain_info::pallet::Call - **/ - ParachainInfoCall: 'Null', - /** - * Lookup113: pallet_collator_selection::pallet::Call - **/ - PalletCollatorSelectionCall: { - _enum: { - add_invulnerable: { - _alias: { - new_: 'new', - }, - new_: 'AccountId32', - }, - remove_invulnerable: { - who: 'AccountId32', - }, - get_license: 'Null', - onboard: 'Null', - offboard: 'Null', - release_license: 'Null', - force_release_license: { - who: 'AccountId32' - } - } - }, - /** - * Lookup114: pallet_session::pallet::Call - **/ - PalletSessionCall: { - _enum: { - set_keys: { - _alias: { - keys_: 'keys', - }, - keys_: 'OpalRuntimeRuntimeCommonSessionKeys', - proof: 'Bytes', - }, - purge_keys: 'Null' - } - }, - /** - * Lookup115: opal_runtime::runtime_common::SessionKeys - **/ - OpalRuntimeRuntimeCommonSessionKeys: { - aura: 'SpConsensusAuraSr25519AppSr25519Public' - }, - /** - * Lookup116: sp_consensus_aura::sr25519::app_sr25519::Public - **/ - SpConsensusAuraSr25519AppSr25519Public: 'SpCoreSr25519Public', - /** - * Lookup117: sp_core::sr25519::Public - **/ - SpCoreSr25519Public: '[u8;32]', - /** - * Lookup118: pallet_balances::pallet::Call - **/ - PalletBalancesCall: { - _enum: { - transfer_allow_death: { - dest: 'MultiAddress', - value: 'Compact', - }, - set_balance_deprecated: { - who: 'MultiAddress', - newFree: 'Compact', - oldReserved: 'Compact', - }, - force_transfer: { - source: 'MultiAddress', - dest: 'MultiAddress', - value: 'Compact', - }, - transfer_keep_alive: { - dest: 'MultiAddress', - value: 'Compact', - }, - transfer_all: { - dest: 'MultiAddress', - keepAlive: 'bool', - }, - force_unreserve: { - who: 'MultiAddress', - amount: 'u128', - }, - upgrade_accounts: { - who: 'Vec', - }, - transfer: { - dest: 'MultiAddress', - value: 'Compact', - }, - force_set_balance: { - who: 'MultiAddress', - newFree: 'Compact' - } - } - }, - /** - * Lookup122: pallet_timestamp::pallet::Call - **/ - PalletTimestampCall: { - _enum: { - set: { - now: 'Compact' - } - } - }, - /** - * Lookup123: pallet_treasury::pallet::Call - **/ - PalletTreasuryCall: { - _enum: { - propose_spend: { - value: 'Compact', - beneficiary: 'MultiAddress', - }, - reject_proposal: { - proposalId: 'Compact', - }, - approve_proposal: { - proposalId: 'Compact', - }, - spend: { - amount: 'Compact', - beneficiary: 'MultiAddress', - }, - remove_approval: { - proposalId: 'Compact' - } - } - }, - /** - * Lookup124: pallet_sudo::pallet::Call - **/ - PalletSudoCall: { - _enum: { - sudo: { - call: 'Call', - }, - sudo_unchecked_weight: { - call: 'Call', - weight: 'SpWeightsWeightV2Weight', - }, - set_key: { - _alias: { - new_: 'new', - }, - new_: 'MultiAddress', - }, - sudo_as: { - who: 'MultiAddress', - call: 'Call' - } - } - }, - /** - * Lookup125: orml_vesting::module::Call - **/ - OrmlVestingModuleCall: { - _enum: { - claim: 'Null', - vested_transfer: { - dest: 'MultiAddress', - schedule: 'OrmlVestingVestingSchedule', - }, - update_vesting_schedules: { - who: 'MultiAddress', - vestingSchedules: 'Vec', - }, - claim_for: { - dest: 'MultiAddress' - } - } - }, - /** - * Lookup127: orml_xtokens::module::Call - **/ - OrmlXtokensModuleCall: { - _enum: { - transfer: { - currencyId: 'PalletForeignAssetsAssetId', - amount: 'u128', - dest: 'StagingXcmVersionedMultiLocation', - destWeightLimit: 'StagingXcmV3WeightLimit', - }, - transfer_multiasset: { - asset: 'StagingXcmVersionedMultiAsset', - dest: 'StagingXcmVersionedMultiLocation', - destWeightLimit: 'StagingXcmV3WeightLimit', - }, - transfer_with_fee: { - currencyId: 'PalletForeignAssetsAssetId', - amount: 'u128', - fee: 'u128', - dest: 'StagingXcmVersionedMultiLocation', - destWeightLimit: 'StagingXcmV3WeightLimit', - }, - transfer_multiasset_with_fee: { - asset: 'StagingXcmVersionedMultiAsset', - fee: 'StagingXcmVersionedMultiAsset', - dest: 'StagingXcmVersionedMultiLocation', - destWeightLimit: 'StagingXcmV3WeightLimit', - }, - transfer_multicurrencies: { - currencies: 'Vec<(PalletForeignAssetsAssetId,u128)>', - feeItem: 'u32', - dest: 'StagingXcmVersionedMultiLocation', - destWeightLimit: 'StagingXcmV3WeightLimit', - }, - transfer_multiassets: { - assets: 'StagingXcmVersionedMultiAssets', - feeItem: 'u32', - dest: 'StagingXcmVersionedMultiLocation', - destWeightLimit: 'StagingXcmV3WeightLimit' - } - } - }, - /** - * Lookup128: staging_xcm::VersionedMultiLocation - **/ - StagingXcmVersionedMultiLocation: { - _enum: { - __Unused0: 'Null', - V2: 'StagingXcmV2MultiLocation', - __Unused2: 'Null', - V3: 'StagingXcmV3MultiLocation' - } - }, - /** - * Lookup129: staging_xcm::v2::multilocation::MultiLocation - **/ - StagingXcmV2MultiLocation: { - parents: 'u8', - interior: 'StagingXcmV2MultilocationJunctions' - }, - /** - * Lookup130: staging_xcm::v2::multilocation::Junctions - **/ - StagingXcmV2MultilocationJunctions: { - _enum: { - Here: 'Null', - X1: 'StagingXcmV2Junction', - X2: '(StagingXcmV2Junction,StagingXcmV2Junction)', - X3: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)', - X4: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)', - X5: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)', - X6: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)', - X7: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)', - X8: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)' - } - }, - /** - * Lookup131: staging_xcm::v2::junction::Junction - **/ - StagingXcmV2Junction: { - _enum: { - Parachain: 'Compact', - AccountId32: { - network: 'StagingXcmV2NetworkId', - id: '[u8;32]', - }, - AccountIndex64: { - network: 'StagingXcmV2NetworkId', - index: 'Compact', - }, - AccountKey20: { - network: 'StagingXcmV2NetworkId', - key: '[u8;20]', - }, - PalletInstance: 'u8', - GeneralIndex: 'Compact', - GeneralKey: 'Bytes', - OnlyChild: 'Null', - Plurality: { - id: 'StagingXcmV2BodyId', - part: 'StagingXcmV2BodyPart' - } - } - }, - /** - * Lookup132: staging_xcm::v2::NetworkId - **/ - StagingXcmV2NetworkId: { - _enum: { - Any: 'Null', - Named: 'Bytes', - Polkadot: 'Null', - Kusama: 'Null' - } - }, - /** - * Lookup134: staging_xcm::v2::BodyId - **/ - StagingXcmV2BodyId: { - _enum: { - Unit: 'Null', - Named: 'Bytes', - Index: 'Compact', - Executive: 'Null', - Technical: 'Null', - Legislative: 'Null', - Judicial: 'Null', - Defense: 'Null', - Administration: 'Null', - Treasury: 'Null' - } - }, - /** - * Lookup135: staging_xcm::v2::BodyPart - **/ - StagingXcmV2BodyPart: { - _enum: { - Voice: 'Null', - Members: { - count: 'Compact', - }, - Fraction: { - nom: 'Compact', - denom: 'Compact', - }, - AtLeastProportion: { - nom: 'Compact', - denom: 'Compact', - }, - MoreThanProportion: { - nom: 'Compact', - denom: 'Compact' - } - } - }, - /** - * Lookup136: staging_xcm::v3::WeightLimit - **/ - StagingXcmV3WeightLimit: { - _enum: { - Unlimited: 'Null', - Limited: 'SpWeightsWeightV2Weight' - } - }, - /** - * Lookup137: staging_xcm::VersionedMultiAsset - **/ - StagingXcmVersionedMultiAsset: { - _enum: { - __Unused0: 'Null', - V2: 'StagingXcmV2MultiAsset', - __Unused2: 'Null', - V3: 'StagingXcmV3MultiAsset' - } - }, - /** - * Lookup138: staging_xcm::v2::multiasset::MultiAsset - **/ - StagingXcmV2MultiAsset: { - id: 'StagingXcmV2MultiassetAssetId', - fun: 'StagingXcmV2MultiassetFungibility' - }, - /** - * Lookup139: staging_xcm::v2::multiasset::AssetId - **/ - StagingXcmV2MultiassetAssetId: { - _enum: { - Concrete: 'StagingXcmV2MultiLocation', - Abstract: 'Bytes' - } - }, - /** - * Lookup140: staging_xcm::v2::multiasset::Fungibility - **/ - StagingXcmV2MultiassetFungibility: { - _enum: { - Fungible: 'Compact', - NonFungible: 'StagingXcmV2MultiassetAssetInstance' - } - }, - /** - * Lookup141: staging_xcm::v2::multiasset::AssetInstance - **/ - StagingXcmV2MultiassetAssetInstance: { - _enum: { - Undefined: 'Null', - Index: 'Compact', - Array4: '[u8;4]', - Array8: '[u8;8]', - Array16: '[u8;16]', - Array32: '[u8;32]', - Blob: 'Bytes' - } - }, - /** - * Lookup144: staging_xcm::VersionedMultiAssets - **/ - StagingXcmVersionedMultiAssets: { - _enum: { - __Unused0: 'Null', - V2: 'StagingXcmV2MultiassetMultiAssets', - __Unused2: 'Null', - V3: 'StagingXcmV3MultiassetMultiAssets' - } - }, - /** - * Lookup145: staging_xcm::v2::multiasset::MultiAssets - **/ - StagingXcmV2MultiassetMultiAssets: 'Vec', - /** - * Lookup147: orml_tokens::module::Call - **/ - OrmlTokensModuleCall: { - _enum: { - transfer: { - dest: 'MultiAddress', - currencyId: 'PalletForeignAssetsAssetId', - amount: 'Compact', - }, - transfer_all: { - dest: 'MultiAddress', - currencyId: 'PalletForeignAssetsAssetId', - keepAlive: 'bool', - }, - transfer_keep_alive: { - dest: 'MultiAddress', - currencyId: 'PalletForeignAssetsAssetId', - amount: 'Compact', - }, - force_transfer: { - source: 'MultiAddress', - dest: 'MultiAddress', - currencyId: 'PalletForeignAssetsAssetId', - amount: 'Compact', - }, - set_balance: { - who: 'MultiAddress', - currencyId: 'PalletForeignAssetsAssetId', - newFree: 'Compact', - newReserved: 'Compact' - } - } - }, - /** - * Lookup148: pallet_identity::pallet::Call - **/ - PalletIdentityCall: { - _enum: { - add_registrar: { - account: 'MultiAddress', - }, - set_identity: { - info: 'PalletIdentityIdentityInfo', - }, - set_subs: { - subs: 'Vec<(AccountId32,Data)>', - }, - clear_identity: 'Null', - request_judgement: { - regIndex: 'Compact', - maxFee: 'Compact', - }, - cancel_request: { - regIndex: 'u32', - }, - set_fee: { - index: 'Compact', - fee: 'Compact', - }, - set_account_id: { - _alias: { - new_: 'new', - }, - index: 'Compact', - new_: 'MultiAddress', - }, - set_fields: { - index: 'Compact', - fields: 'PalletIdentityBitFlags', - }, - provide_judgement: { - regIndex: 'Compact', - target: 'MultiAddress', - judgement: 'PalletIdentityJudgement', - identity: 'H256', - }, - kill_identity: { - target: 'MultiAddress', - }, - add_sub: { - sub: 'MultiAddress', - data: 'Data', - }, - rename_sub: { - sub: 'MultiAddress', - data: 'Data', - }, - remove_sub: { - sub: 'MultiAddress', - }, - quit_sub: 'Null', - force_insert_identities: { - identities: 'Vec<(AccountId32,PalletIdentityRegistration)>', - }, - force_remove_identities: { - identities: 'Vec', - }, - force_set_subs: { - subs: 'Vec<(AccountId32,(u128,Vec<(AccountId32,Data)>))>' - } - } - }, - /** - * Lookup149: pallet_identity::types::IdentityInfo - **/ - PalletIdentityIdentityInfo: { - additional: 'Vec<(Data,Data)>', - display: 'Data', - legal: 'Data', - web: 'Data', - riot: 'Data', - email: 'Data', - pgpFingerprint: 'Option<[u8;20]>', - image: 'Data', - twitter: 'Data' - }, - /** - * Lookup185: pallet_identity::types::BitFlags - **/ - PalletIdentityBitFlags: { - _bitLength: 64, - Display: 1, - Legal: 2, - Web: 4, - Riot: 8, - Email: 16, - PgpFingerprint: 32, - Image: 64, - Twitter: 128 - }, - /** - * Lookup186: pallet_identity::types::IdentityField - **/ - PalletIdentityIdentityField: { - _enum: ['__Unused0', 'Display', 'Legal', '__Unused3', 'Web', '__Unused5', '__Unused6', '__Unused7', 'Riot', '__Unused9', '__Unused10', '__Unused11', '__Unused12', '__Unused13', '__Unused14', '__Unused15', 'Email', '__Unused17', '__Unused18', '__Unused19', '__Unused20', '__Unused21', '__Unused22', '__Unused23', '__Unused24', '__Unused25', '__Unused26', '__Unused27', '__Unused28', '__Unused29', '__Unused30', '__Unused31', 'PgpFingerprint', '__Unused33', '__Unused34', '__Unused35', '__Unused36', '__Unused37', '__Unused38', '__Unused39', '__Unused40', '__Unused41', '__Unused42', '__Unused43', '__Unused44', '__Unused45', '__Unused46', '__Unused47', '__Unused48', '__Unused49', '__Unused50', '__Unused51', '__Unused52', '__Unused53', '__Unused54', '__Unused55', '__Unused56', '__Unused57', '__Unused58', '__Unused59', '__Unused60', '__Unused61', '__Unused62', '__Unused63', 'Image', '__Unused65', '__Unused66', '__Unused67', '__Unused68', '__Unused69', '__Unused70', '__Unused71', '__Unused72', '__Unused73', '__Unused74', '__Unused75', '__Unused76', '__Unused77', '__Unused78', '__Unused79', '__Unused80', '__Unused81', '__Unused82', '__Unused83', '__Unused84', '__Unused85', '__Unused86', '__Unused87', '__Unused88', '__Unused89', '__Unused90', '__Unused91', '__Unused92', '__Unused93', '__Unused94', '__Unused95', '__Unused96', '__Unused97', '__Unused98', '__Unused99', '__Unused100', '__Unused101', '__Unused102', '__Unused103', '__Unused104', '__Unused105', '__Unused106', '__Unused107', '__Unused108', '__Unused109', '__Unused110', '__Unused111', '__Unused112', '__Unused113', '__Unused114', '__Unused115', '__Unused116', '__Unused117', '__Unused118', '__Unused119', '__Unused120', '__Unused121', '__Unused122', '__Unused123', '__Unused124', '__Unused125', '__Unused126', '__Unused127', 'Twitter'] - }, - /** - * Lookup187: pallet_identity::types::Judgement - **/ - PalletIdentityJudgement: { - _enum: { - Unknown: 'Null', - FeePaid: 'u128', - Reasonable: 'Null', - KnownGood: 'Null', - OutOfDate: 'Null', - LowQuality: 'Null', - Erroneous: 'Null' - } - }, - /** - * Lookup190: pallet_identity::types::Registration - **/ - PalletIdentityRegistration: { - judgements: 'Vec<(u32,PalletIdentityJudgement)>', - deposit: 'u128', - info: 'PalletIdentityIdentityInfo' - }, - /** - * Lookup198: pallet_preimage::pallet::Call - **/ - PalletPreimageCall: { - _enum: { - note_preimage: { - bytes: 'Bytes', - }, - unnote_preimage: { - _alias: { - hash_: 'hash', - }, - hash_: 'H256', - }, - request_preimage: { - _alias: { - hash_: 'hash', - }, - hash_: 'H256', - }, - unrequest_preimage: { - _alias: { - hash_: 'hash', - }, - hash_: 'H256' - } - } - }, - /** - * Lookup199: pallet_democracy::pallet::Call - **/ - PalletDemocracyCall: { - _enum: { - propose: { - proposal: 'FrameSupportPreimagesBounded', - value: 'Compact', - }, - second: { - proposal: 'Compact', - }, - vote: { - refIndex: 'Compact', - vote: 'PalletDemocracyVoteAccountVote', - }, - emergency_cancel: { - refIndex: 'u32', - }, - external_propose: { - proposal: 'FrameSupportPreimagesBounded', - }, - external_propose_majority: { - proposal: 'FrameSupportPreimagesBounded', - }, - external_propose_default: { - proposal: 'FrameSupportPreimagesBounded', - }, - fast_track: { - proposalHash: 'H256', - votingPeriod: 'u32', - delay: 'u32', - }, - veto_external: { - proposalHash: 'H256', - }, - cancel_referendum: { - refIndex: 'Compact', - }, - delegate: { - to: 'MultiAddress', - conviction: 'PalletDemocracyConviction', - balance: 'u128', - }, - undelegate: 'Null', - clear_public_proposals: 'Null', - unlock: { - target: 'MultiAddress', - }, - remove_vote: { - index: 'u32', - }, - remove_other_vote: { - target: 'MultiAddress', - index: 'u32', - }, - blacklist: { - proposalHash: 'H256', - maybeRefIndex: 'Option', - }, - cancel_proposal: { - propIndex: 'Compact', - }, - set_metadata: { - owner: 'PalletDemocracyMetadataOwner', - maybeHash: 'Option' - } - } - }, - /** - * Lookup200: pallet_democracy::conviction::Conviction - **/ - PalletDemocracyConviction: { - _enum: ['None', 'Locked1x', 'Locked2x', 'Locked3x', 'Locked4x', 'Locked5x', 'Locked6x'] - }, - /** - * Lookup203: pallet_collective::pallet::Call - **/ - PalletCollectiveCall: { - _enum: { - set_members: { - newMembers: 'Vec', - prime: 'Option', - oldCount: 'u32', - }, - execute: { - proposal: 'Call', - lengthBound: 'Compact', - }, - propose: { - threshold: 'Compact', - proposal: 'Call', - lengthBound: 'Compact', - }, - vote: { - proposal: 'H256', - index: 'Compact', - approve: 'bool', - }, - __Unused4: 'Null', - disapprove_proposal: { - proposalHash: 'H256', - }, - close: { - proposalHash: 'H256', - index: 'Compact', - proposalWeightBound: 'SpWeightsWeightV2Weight', - lengthBound: 'Compact' - } - } - }, - /** - * Lookup205: pallet_membership::pallet::Call - **/ - PalletMembershipCall: { - _enum: { - add_member: { - who: 'MultiAddress', - }, - remove_member: { - who: 'MultiAddress', - }, - swap_member: { - remove: 'MultiAddress', - add: 'MultiAddress', - }, - reset_members: { - members: 'Vec', - }, - change_key: { - _alias: { - new_: 'new', - }, - new_: 'MultiAddress', - }, - set_prime: { - who: 'MultiAddress', - }, - clear_prime: 'Null' - } - }, - /** - * Lookup207: pallet_ranked_collective::pallet::Call - **/ - PalletRankedCollectiveCall: { - _enum: { - add_member: { - who: 'MultiAddress', - }, - promote_member: { - who: 'MultiAddress', - }, - demote_member: { - who: 'MultiAddress', - }, - remove_member: { - who: 'MultiAddress', - minRank: 'u16', - }, - vote: { - poll: 'u32', - aye: 'bool', - }, - cleanup_poll: { - pollIndex: 'u32', - max: 'u32' - } - } - }, - /** - * Lookup208: pallet_referenda::pallet::Call - **/ - PalletReferendaCall: { - _enum: { - submit: { - proposalOrigin: 'OpalRuntimeOriginCaller', - proposal: 'FrameSupportPreimagesBounded', - enactmentMoment: 'FrameSupportScheduleDispatchTime', - }, - place_decision_deposit: { - index: 'u32', - }, - refund_decision_deposit: { - index: 'u32', - }, - cancel: { - index: 'u32', - }, - kill: { - index: 'u32', - }, - nudge_referendum: { - index: 'u32', - }, - one_fewer_deciding: { - track: 'u16', - }, - refund_submission_deposit: { - index: 'u32', - }, - set_metadata: { - index: 'u32', - maybeHash: 'Option' - } - } - }, - /** - * Lookup209: opal_runtime::OriginCaller - **/ - OpalRuntimeOriginCaller: { - _enum: { - system: 'FrameSupportDispatchRawOrigin', - __Unused1: 'Null', - __Unused2: 'Null', - __Unused3: 'Null', - __Unused4: 'Null', - __Unused5: 'Null', - __Unused6: 'Null', - Void: 'SpCoreVoid', - __Unused8: 'Null', - __Unused9: 'Null', - __Unused10: 'Null', - __Unused11: 'Null', - __Unused12: 'Null', - __Unused13: 'Null', - __Unused14: 'Null', - __Unused15: 'Null', - __Unused16: 'Null', - __Unused17: 'Null', - __Unused18: 'Null', - __Unused19: 'Null', - __Unused20: 'Null', - __Unused21: 'Null', - __Unused22: 'Null', - __Unused23: 'Null', - __Unused24: 'Null', - __Unused25: 'Null', - __Unused26: 'Null', - __Unused27: 'Null', - __Unused28: 'Null', - __Unused29: 'Null', - __Unused30: 'Null', - __Unused31: 'Null', - __Unused32: 'Null', - __Unused33: 'Null', - __Unused34: 'Null', - __Unused35: 'Null', - __Unused36: 'Null', - __Unused37: 'Null', - __Unused38: 'Null', - __Unused39: 'Null', - __Unused40: 'Null', - __Unused41: 'Null', - __Unused42: 'Null', - Council: 'PalletCollectiveRawOrigin', - TechnicalCommittee: 'PalletCollectiveRawOrigin', - __Unused45: 'Null', - __Unused46: 'Null', - __Unused47: 'Null', - __Unused48: 'Null', - __Unused49: 'Null', - __Unused50: 'Null', - PolkadotXcm: 'PalletXcmOrigin', - CumulusXcm: 'CumulusPalletXcmOrigin', - __Unused53: 'Null', - __Unused54: 'Null', - __Unused55: 'Null', - __Unused56: 'Null', - __Unused57: 'Null', - __Unused58: 'Null', - __Unused59: 'Null', - __Unused60: 'Null', - __Unused61: 'Null', - __Unused62: 'Null', - __Unused63: 'Null', - __Unused64: 'Null', - __Unused65: 'Null', - __Unused66: 'Null', - __Unused67: 'Null', - __Unused68: 'Null', - __Unused69: 'Null', - __Unused70: 'Null', - __Unused71: 'Null', - __Unused72: 'Null', - __Unused73: 'Null', - __Unused74: 'Null', - __Unused75: 'Null', - __Unused76: 'Null', - __Unused77: 'Null', - __Unused78: 'Null', - __Unused79: 'Null', - __Unused80: 'Null', - __Unused81: 'Null', - __Unused82: 'Null', - __Unused83: 'Null', - __Unused84: 'Null', - __Unused85: 'Null', - __Unused86: 'Null', - __Unused87: 'Null', - __Unused88: 'Null', - __Unused89: 'Null', - __Unused90: 'Null', - __Unused91: 'Null', - __Unused92: 'Null', - __Unused93: 'Null', - __Unused94: 'Null', - __Unused95: 'Null', - __Unused96: 'Null', - __Unused97: 'Null', - __Unused98: 'Null', - Origins: 'PalletGovOriginsOrigin', - __Unused100: 'Null', - Ethereum: 'PalletEthereumRawOrigin' - } - }, - /** - * Lookup210: frame_support::dispatch::RawOrigin - **/ - FrameSupportDispatchRawOrigin: { - _enum: { - Root: 'Null', - Signed: 'AccountId32', - None: 'Null' - } - }, - /** - * Lookup211: pallet_collective::RawOrigin - **/ - PalletCollectiveRawOrigin: { - _enum: { - Members: '(u32,u32)', - Member: 'AccountId32', - _Phantom: 'Null' - } - }, - /** - * Lookup213: pallet_gov_origins::pallet::Origin - **/ - PalletGovOriginsOrigin: { - _enum: ['FellowshipProposition'] - }, - /** - * Lookup214: pallet_xcm::pallet::Origin - **/ - PalletXcmOrigin: { - _enum: { - Xcm: 'StagingXcmV3MultiLocation', - Response: 'StagingXcmV3MultiLocation' - } - }, - /** - * Lookup215: cumulus_pallet_xcm::pallet::Origin - **/ - CumulusPalletXcmOrigin: { - _enum: { - Relay: 'Null', - SiblingParachain: 'u32' - } - }, - /** - * Lookup216: pallet_ethereum::RawOrigin - **/ - PalletEthereumRawOrigin: { - _enum: { - EthereumTransaction: 'H160' - } - }, - /** - * Lookup218: sp_core::Void - **/ - SpCoreVoid: 'Null', - /** - * Lookup219: frame_support::traits::schedule::DispatchTime - **/ - FrameSupportScheduleDispatchTime: { - _enum: { - At: 'u32', - After: 'u32' - } - }, - /** - * Lookup220: pallet_scheduler::pallet::Call - **/ - PalletSchedulerCall: { - _enum: { - schedule: { - when: 'u32', - maybePeriodic: 'Option<(u32,u32)>', - priority: 'u8', - call: 'Call', - }, - cancel: { - when: 'u32', - index: 'u32', - }, - schedule_named: { - id: '[u8;32]', - when: 'u32', - maybePeriodic: 'Option<(u32,u32)>', - priority: 'u8', - call: 'Call', - }, - cancel_named: { - id: '[u8;32]', - }, - schedule_after: { - after: 'u32', - maybePeriodic: 'Option<(u32,u32)>', - priority: 'u8', - call: 'Call', - }, - schedule_named_after: { - id: '[u8;32]', - after: 'u32', - maybePeriodic: 'Option<(u32,u32)>', - priority: 'u8', - call: 'Call' - } - } - }, - /** - * Lookup223: cumulus_pallet_xcmp_queue::pallet::Call - **/ - CumulusPalletXcmpQueueCall: { - _enum: { - service_overweight: { - index: 'u64', - weightLimit: 'SpWeightsWeightV2Weight', - }, - suspend_xcm_execution: 'Null', - resume_xcm_execution: 'Null', - update_suspend_threshold: { - _alias: { - new_: 'new', - }, - new_: 'u32', - }, - update_drop_threshold: { - _alias: { - new_: 'new', - }, - new_: 'u32', - }, - update_resume_threshold: { - _alias: { - new_: 'new', - }, - new_: 'u32', - }, - update_threshold_weight: { - _alias: { - new_: 'new', - }, - new_: 'SpWeightsWeightV2Weight', - }, - update_weight_restrict_decay: { - _alias: { - new_: 'new', - }, - new_: 'SpWeightsWeightV2Weight', - }, - update_xcmp_max_individual_weight: { - _alias: { - new_: 'new', - }, - new_: 'SpWeightsWeightV2Weight' - } - } - }, - /** - * Lookup224: pallet_xcm::pallet::Call - **/ - PalletXcmCall: { - _enum: { - send: { - dest: 'StagingXcmVersionedMultiLocation', - message: 'StagingXcmVersionedXcm', - }, - teleport_assets: { - dest: 'StagingXcmVersionedMultiLocation', - beneficiary: 'StagingXcmVersionedMultiLocation', - assets: 'StagingXcmVersionedMultiAssets', - feeAssetItem: 'u32', - }, - reserve_transfer_assets: { - dest: 'StagingXcmVersionedMultiLocation', - beneficiary: 'StagingXcmVersionedMultiLocation', - assets: 'StagingXcmVersionedMultiAssets', - feeAssetItem: 'u32', - }, - execute: { - message: 'StagingXcmVersionedXcm', - maxWeight: 'SpWeightsWeightV2Weight', - }, - force_xcm_version: { - location: 'StagingXcmV3MultiLocation', - version: 'u32', - }, - force_default_xcm_version: { - maybeXcmVersion: 'Option', - }, - force_subscribe_version_notify: { - location: 'StagingXcmVersionedMultiLocation', - }, - force_unsubscribe_version_notify: { - location: 'StagingXcmVersionedMultiLocation', - }, - limited_reserve_transfer_assets: { - dest: 'StagingXcmVersionedMultiLocation', - beneficiary: 'StagingXcmVersionedMultiLocation', - assets: 'StagingXcmVersionedMultiAssets', - feeAssetItem: 'u32', - weightLimit: 'StagingXcmV3WeightLimit', - }, - limited_teleport_assets: { - dest: 'StagingXcmVersionedMultiLocation', - beneficiary: 'StagingXcmVersionedMultiLocation', - assets: 'StagingXcmVersionedMultiAssets', - feeAssetItem: 'u32', - weightLimit: 'StagingXcmV3WeightLimit', - }, - force_suspension: { - suspended: 'bool' - } - } - }, - /** - * Lookup225: staging_xcm::VersionedXcm - **/ - StagingXcmVersionedXcm: { - _enum: { - __Unused0: 'Null', - __Unused1: 'Null', - V2: 'StagingXcmV2Xcm', - V3: 'StagingXcmV3Xcm' - } - }, - /** - * Lookup226: staging_xcm::v2::Xcm - **/ - StagingXcmV2Xcm: 'Vec', - /** - * Lookup228: staging_xcm::v2::Instruction - **/ - StagingXcmV2Instruction: { - _enum: { - WithdrawAsset: 'StagingXcmV2MultiassetMultiAssets', - ReserveAssetDeposited: 'StagingXcmV2MultiassetMultiAssets', - ReceiveTeleportedAsset: 'StagingXcmV2MultiassetMultiAssets', - QueryResponse: { - queryId: 'Compact', - response: 'StagingXcmV2Response', - maxWeight: 'Compact', - }, - TransferAsset: { - assets: 'StagingXcmV2MultiassetMultiAssets', - beneficiary: 'StagingXcmV2MultiLocation', - }, - TransferReserveAsset: { - assets: 'StagingXcmV2MultiassetMultiAssets', - dest: 'StagingXcmV2MultiLocation', - xcm: 'StagingXcmV2Xcm', - }, - Transact: { - originType: 'StagingXcmV2OriginKind', - requireWeightAtMost: 'Compact', - call: 'StagingXcmDoubleEncoded', - }, - HrmpNewChannelOpenRequest: { - sender: 'Compact', - maxMessageSize: 'Compact', - maxCapacity: 'Compact', - }, - HrmpChannelAccepted: { - recipient: 'Compact', - }, - HrmpChannelClosing: { - initiator: 'Compact', - sender: 'Compact', - recipient: 'Compact', - }, - ClearOrigin: 'Null', - DescendOrigin: 'StagingXcmV2MultilocationJunctions', - ReportError: { - queryId: 'Compact', - dest: 'StagingXcmV2MultiLocation', - maxResponseWeight: 'Compact', - }, - DepositAsset: { - assets: 'StagingXcmV2MultiassetMultiAssetFilter', - maxAssets: 'Compact', - beneficiary: 'StagingXcmV2MultiLocation', - }, - DepositReserveAsset: { - assets: 'StagingXcmV2MultiassetMultiAssetFilter', - maxAssets: 'Compact', - dest: 'StagingXcmV2MultiLocation', - xcm: 'StagingXcmV2Xcm', - }, - ExchangeAsset: { - give: 'StagingXcmV2MultiassetMultiAssetFilter', - receive: 'StagingXcmV2MultiassetMultiAssets', - }, - InitiateReserveWithdraw: { - assets: 'StagingXcmV2MultiassetMultiAssetFilter', - reserve: 'StagingXcmV2MultiLocation', - xcm: 'StagingXcmV2Xcm', - }, - InitiateTeleport: { - assets: 'StagingXcmV2MultiassetMultiAssetFilter', - dest: 'StagingXcmV2MultiLocation', - xcm: 'StagingXcmV2Xcm', - }, - QueryHolding: { - queryId: 'Compact', - dest: 'StagingXcmV2MultiLocation', - assets: 'StagingXcmV2MultiassetMultiAssetFilter', - maxResponseWeight: 'Compact', - }, - BuyExecution: { - fees: 'StagingXcmV2MultiAsset', - weightLimit: 'StagingXcmV2WeightLimit', - }, - RefundSurplus: 'Null', - SetErrorHandler: 'StagingXcmV2Xcm', - SetAppendix: 'StagingXcmV2Xcm', - ClearError: 'Null', - ClaimAsset: { - assets: 'StagingXcmV2MultiassetMultiAssets', - ticket: 'StagingXcmV2MultiLocation', - }, - Trap: 'Compact', - SubscribeVersion: { - queryId: 'Compact', - maxResponseWeight: 'Compact', - }, - UnsubscribeVersion: 'Null' - } - }, - /** - * Lookup229: staging_xcm::v2::Response - **/ - StagingXcmV2Response: { - _enum: { - Null: 'Null', - Assets: 'StagingXcmV2MultiassetMultiAssets', - ExecutionResult: 'Option<(u32,StagingXcmV2TraitsError)>', - Version: 'u32' - } - }, - /** - * Lookup232: staging_xcm::v2::traits::Error - **/ - StagingXcmV2TraitsError: { - _enum: { - Overflow: 'Null', - Unimplemented: 'Null', - UntrustedReserveLocation: 'Null', - UntrustedTeleportLocation: 'Null', - MultiLocationFull: 'Null', - MultiLocationNotInvertible: 'Null', - BadOrigin: 'Null', - InvalidLocation: 'Null', - AssetNotFound: 'Null', - FailedToTransactAsset: 'Null', - NotWithdrawable: 'Null', - LocationCannotHold: 'Null', - ExceedsMaxMessageSize: 'Null', - DestinationUnsupported: 'Null', - Transport: 'Null', - Unroutable: 'Null', - UnknownClaim: 'Null', - FailedToDecode: 'Null', - MaxWeightInvalid: 'Null', - NotHoldingFees: 'Null', - TooExpensive: 'Null', - Trap: 'u64', - UnhandledXcmVersion: 'Null', - WeightLimitReached: 'u64', - Barrier: 'Null', - WeightNotComputable: 'Null' - } - }, - /** - * Lookup233: staging_xcm::v2::OriginKind - **/ - StagingXcmV2OriginKind: { - _enum: ['Native', 'SovereignAccount', 'Superuser', 'Xcm'] - }, - /** - * Lookup234: staging_xcm::double_encoded::DoubleEncoded - **/ - StagingXcmDoubleEncoded: { - encoded: 'Bytes' - }, - /** - * Lookup235: staging_xcm::v2::multiasset::MultiAssetFilter - **/ - StagingXcmV2MultiassetMultiAssetFilter: { - _enum: { - Definite: 'StagingXcmV2MultiassetMultiAssets', - Wild: 'StagingXcmV2MultiassetWildMultiAsset' - } - }, - /** - * Lookup236: staging_xcm::v2::multiasset::WildMultiAsset - **/ - StagingXcmV2MultiassetWildMultiAsset: { - _enum: { - All: 'Null', - AllOf: { - id: 'StagingXcmV2MultiassetAssetId', - fun: 'StagingXcmV2MultiassetWildFungibility' - } - } - }, - /** - * Lookup237: staging_xcm::v2::multiasset::WildFungibility - **/ - StagingXcmV2MultiassetWildFungibility: { - _enum: ['Fungible', 'NonFungible'] - }, - /** - * Lookup238: staging_xcm::v2::WeightLimit - **/ - StagingXcmV2WeightLimit: { - _enum: { - Unlimited: 'Null', - Limited: 'Compact' - } - }, - /** - * Lookup239: staging_xcm::v3::Xcm - **/ - StagingXcmV3Xcm: 'Vec', - /** - * Lookup241: staging_xcm::v3::Instruction - **/ - StagingXcmV3Instruction: { - _enum: { - WithdrawAsset: 'StagingXcmV3MultiassetMultiAssets', - ReserveAssetDeposited: 'StagingXcmV3MultiassetMultiAssets', - ReceiveTeleportedAsset: 'StagingXcmV3MultiassetMultiAssets', - QueryResponse: { - queryId: 'Compact', - response: 'StagingXcmV3Response', - maxWeight: 'SpWeightsWeightV2Weight', - querier: 'Option', - }, - TransferAsset: { - assets: 'StagingXcmV3MultiassetMultiAssets', - beneficiary: 'StagingXcmV3MultiLocation', - }, - TransferReserveAsset: { - assets: 'StagingXcmV3MultiassetMultiAssets', - dest: 'StagingXcmV3MultiLocation', - xcm: 'StagingXcmV3Xcm', - }, - Transact: { - originKind: 'StagingXcmV2OriginKind', - requireWeightAtMost: 'SpWeightsWeightV2Weight', - call: 'StagingXcmDoubleEncoded', - }, - HrmpNewChannelOpenRequest: { - sender: 'Compact', - maxMessageSize: 'Compact', - maxCapacity: 'Compact', - }, - HrmpChannelAccepted: { - recipient: 'Compact', - }, - HrmpChannelClosing: { - initiator: 'Compact', - sender: 'Compact', - recipient: 'Compact', - }, - ClearOrigin: 'Null', - DescendOrigin: 'StagingXcmV3Junctions', - ReportError: 'StagingXcmV3QueryResponseInfo', - DepositAsset: { - assets: 'StagingXcmV3MultiassetMultiAssetFilter', - beneficiary: 'StagingXcmV3MultiLocation', - }, - DepositReserveAsset: { - assets: 'StagingXcmV3MultiassetMultiAssetFilter', - dest: 'StagingXcmV3MultiLocation', - xcm: 'StagingXcmV3Xcm', - }, - ExchangeAsset: { - give: 'StagingXcmV3MultiassetMultiAssetFilter', - want: 'StagingXcmV3MultiassetMultiAssets', - maximal: 'bool', - }, - InitiateReserveWithdraw: { - assets: 'StagingXcmV3MultiassetMultiAssetFilter', - reserve: 'StagingXcmV3MultiLocation', - xcm: 'StagingXcmV3Xcm', - }, - InitiateTeleport: { - assets: 'StagingXcmV3MultiassetMultiAssetFilter', - dest: 'StagingXcmV3MultiLocation', - xcm: 'StagingXcmV3Xcm', - }, - ReportHolding: { - responseInfo: 'StagingXcmV3QueryResponseInfo', - assets: 'StagingXcmV3MultiassetMultiAssetFilter', - }, - BuyExecution: { - fees: 'StagingXcmV3MultiAsset', - weightLimit: 'StagingXcmV3WeightLimit', - }, - RefundSurplus: 'Null', - SetErrorHandler: 'StagingXcmV3Xcm', - SetAppendix: 'StagingXcmV3Xcm', - ClearError: 'Null', - ClaimAsset: { - assets: 'StagingXcmV3MultiassetMultiAssets', - ticket: 'StagingXcmV3MultiLocation', - }, - Trap: 'Compact', - SubscribeVersion: { - queryId: 'Compact', - maxResponseWeight: 'SpWeightsWeightV2Weight', - }, - UnsubscribeVersion: 'Null', - BurnAsset: 'StagingXcmV3MultiassetMultiAssets', - ExpectAsset: 'StagingXcmV3MultiassetMultiAssets', - ExpectOrigin: 'Option', - ExpectError: 'Option<(u32,StagingXcmV3TraitsError)>', - ExpectTransactStatus: 'StagingXcmV3MaybeErrorCode', - QueryPallet: { - moduleName: 'Bytes', - responseInfo: 'StagingXcmV3QueryResponseInfo', - }, - ExpectPallet: { - index: 'Compact', - name: 'Bytes', - moduleName: 'Bytes', - crateMajor: 'Compact', - minCrateMinor: 'Compact', - }, - ReportTransactStatus: 'StagingXcmV3QueryResponseInfo', - ClearTransactStatus: 'Null', - UniversalOrigin: 'StagingXcmV3Junction', - ExportMessage: { - network: 'StagingXcmV3JunctionNetworkId', - destination: 'StagingXcmV3Junctions', - xcm: 'StagingXcmV3Xcm', - }, - LockAsset: { - asset: 'StagingXcmV3MultiAsset', - unlocker: 'StagingXcmV3MultiLocation', - }, - UnlockAsset: { - asset: 'StagingXcmV3MultiAsset', - target: 'StagingXcmV3MultiLocation', - }, - NoteUnlockable: { - asset: 'StagingXcmV3MultiAsset', - owner: 'StagingXcmV3MultiLocation', - }, - RequestUnlock: { - asset: 'StagingXcmV3MultiAsset', - locker: 'StagingXcmV3MultiLocation', - }, - SetFeesMode: { - jitWithdraw: 'bool', - }, - SetTopic: '[u8;32]', - ClearTopic: 'Null', - AliasOrigin: 'StagingXcmV3MultiLocation', - UnpaidExecution: { - weightLimit: 'StagingXcmV3WeightLimit', - checkOrigin: 'Option' - } - } - }, - /** - * Lookup242: staging_xcm::v3::Response - **/ - StagingXcmV3Response: { - _enum: { - Null: 'Null', - Assets: 'StagingXcmV3MultiassetMultiAssets', - ExecutionResult: 'Option<(u32,StagingXcmV3TraitsError)>', - Version: 'u32', - PalletsInfo: 'Vec', - DispatchResult: 'StagingXcmV3MaybeErrorCode' - } - }, - /** - * Lookup245: staging_xcm::v3::traits::Error - **/ - StagingXcmV3TraitsError: { - _enum: { - Overflow: 'Null', - Unimplemented: 'Null', - UntrustedReserveLocation: 'Null', - UntrustedTeleportLocation: 'Null', - LocationFull: 'Null', - LocationNotInvertible: 'Null', - BadOrigin: 'Null', - InvalidLocation: 'Null', - AssetNotFound: 'Null', - FailedToTransactAsset: 'Null', - NotWithdrawable: 'Null', - LocationCannotHold: 'Null', - ExceedsMaxMessageSize: 'Null', - DestinationUnsupported: 'Null', - Transport: 'Null', - Unroutable: 'Null', - UnknownClaim: 'Null', - FailedToDecode: 'Null', - MaxWeightInvalid: 'Null', - NotHoldingFees: 'Null', - TooExpensive: 'Null', - Trap: 'u64', - ExpectationFalse: 'Null', - PalletNotFound: 'Null', - NameMismatch: 'Null', - VersionIncompatible: 'Null', - HoldingWouldOverflow: 'Null', - ExportError: 'Null', - ReanchorFailed: 'Null', - NoDeal: 'Null', - FeesNotMet: 'Null', - LockError: 'Null', - NoPermission: 'Null', - Unanchored: 'Null', - NotDepositable: 'Null', - UnhandledXcmVersion: 'Null', - WeightLimitReached: 'SpWeightsWeightV2Weight', - Barrier: 'Null', - WeightNotComputable: 'Null', - ExceedsStackLimit: 'Null' - } - }, - /** - * Lookup247: staging_xcm::v3::PalletInfo - **/ - StagingXcmV3PalletInfo: { - index: 'Compact', - name: 'Bytes', - moduleName: 'Bytes', - major: 'Compact', - minor: 'Compact', - patch: 'Compact' - }, - /** - * Lookup250: staging_xcm::v3::MaybeErrorCode - **/ - StagingXcmV3MaybeErrorCode: { - _enum: { - Success: 'Null', - Error: 'Bytes', - TruncatedError: 'Bytes' - } - }, - /** - * Lookup253: staging_xcm::v3::QueryResponseInfo - **/ - StagingXcmV3QueryResponseInfo: { - destination: 'StagingXcmV3MultiLocation', - queryId: 'Compact', - maxWeight: 'SpWeightsWeightV2Weight' - }, - /** - * Lookup254: staging_xcm::v3::multiasset::MultiAssetFilter - **/ - StagingXcmV3MultiassetMultiAssetFilter: { - _enum: { - Definite: 'StagingXcmV3MultiassetMultiAssets', - Wild: 'StagingXcmV3MultiassetWildMultiAsset' - } - }, - /** - * Lookup255: staging_xcm::v3::multiasset::WildMultiAsset - **/ - StagingXcmV3MultiassetWildMultiAsset: { - _enum: { - All: 'Null', - AllOf: { - id: 'StagingXcmV3MultiassetAssetId', - fun: 'StagingXcmV3MultiassetWildFungibility', - }, - AllCounted: 'Compact', - AllOfCounted: { - id: 'StagingXcmV3MultiassetAssetId', - fun: 'StagingXcmV3MultiassetWildFungibility', - count: 'Compact' - } - } - }, - /** - * Lookup256: staging_xcm::v3::multiasset::WildFungibility - **/ - StagingXcmV3MultiassetWildFungibility: { - _enum: ['Fungible', 'NonFungible'] - }, - /** - * Lookup265: cumulus_pallet_xcm::pallet::Call - **/ - CumulusPalletXcmCall: 'Null', - /** - * Lookup266: cumulus_pallet_dmp_queue::pallet::Call - **/ - CumulusPalletDmpQueueCall: { - _enum: { - service_overweight: { - index: 'u64', - weightLimit: 'SpWeightsWeightV2Weight' - } - } - }, - /** - * Lookup267: pallet_inflation::pallet::Call - **/ - PalletInflationCall: { - _enum: { - start_inflation: { - inflationStartRelayBlock: 'u32' - } - } - }, - /** - * Lookup268: pallet_unique::pallet::Call - **/ - PalletUniqueCall: { - _enum: { - create_collection: { - collectionName: 'Vec', - collectionDescription: 'Vec', - tokenPrefix: 'Bytes', - mode: 'UpDataStructsCollectionMode', - }, - create_collection_ex: { - data: 'UpDataStructsCreateCollectionData', - }, - destroy_collection: { - collectionId: 'u32', - }, - add_to_allow_list: { - collectionId: 'u32', - address: 'PalletEvmAccountBasicCrossAccountIdRepr', - }, - remove_from_allow_list: { - collectionId: 'u32', - address: 'PalletEvmAccountBasicCrossAccountIdRepr', - }, - change_collection_owner: { - collectionId: 'u32', - newOwner: 'AccountId32', - }, - add_collection_admin: { - collectionId: 'u32', - newAdminId: 'PalletEvmAccountBasicCrossAccountIdRepr', - }, - remove_collection_admin: { - collectionId: 'u32', - accountId: 'PalletEvmAccountBasicCrossAccountIdRepr', - }, - set_collection_sponsor: { - collectionId: 'u32', - newSponsor: 'AccountId32', - }, - confirm_sponsorship: { - collectionId: 'u32', - }, - remove_collection_sponsor: { - collectionId: 'u32', - }, - create_item: { - collectionId: 'u32', - owner: 'PalletEvmAccountBasicCrossAccountIdRepr', - data: 'UpDataStructsCreateItemData', - }, - create_multiple_items: { - collectionId: 'u32', - owner: 'PalletEvmAccountBasicCrossAccountIdRepr', - itemsData: 'Vec', - }, - set_collection_properties: { - collectionId: 'u32', - properties: 'Vec', - }, - delete_collection_properties: { - collectionId: 'u32', - propertyKeys: 'Vec', - }, - set_token_properties: { - collectionId: 'u32', - tokenId: 'u32', - properties: 'Vec', - }, - delete_token_properties: { - collectionId: 'u32', - tokenId: 'u32', - propertyKeys: 'Vec', - }, - set_token_property_permissions: { - collectionId: 'u32', - propertyPermissions: 'Vec', - }, - create_multiple_items_ex: { - collectionId: 'u32', - data: 'UpDataStructsCreateItemExData', - }, - set_transfers_enabled_flag: { - collectionId: 'u32', - value: 'bool', - }, - burn_item: { - collectionId: 'u32', - itemId: 'u32', - value: 'u128', - }, - burn_from: { - collectionId: 'u32', - from: 'PalletEvmAccountBasicCrossAccountIdRepr', - itemId: 'u32', - value: 'u128', - }, - transfer: { - recipient: 'PalletEvmAccountBasicCrossAccountIdRepr', - collectionId: 'u32', - itemId: 'u32', - value: 'u128', - }, - approve: { - spender: 'PalletEvmAccountBasicCrossAccountIdRepr', - collectionId: 'u32', - itemId: 'u32', - amount: 'u128', - }, - approve_from: { - from: 'PalletEvmAccountBasicCrossAccountIdRepr', - to: 'PalletEvmAccountBasicCrossAccountIdRepr', - collectionId: 'u32', - itemId: 'u32', - amount: 'u128', - }, - transfer_from: { - from: 'PalletEvmAccountBasicCrossAccountIdRepr', - recipient: 'PalletEvmAccountBasicCrossAccountIdRepr', - collectionId: 'u32', - itemId: 'u32', - value: 'u128', - }, - set_collection_limits: { - collectionId: 'u32', - newLimit: 'UpDataStructsCollectionLimits', - }, - set_collection_permissions: { - collectionId: 'u32', - newPermission: 'UpDataStructsCollectionPermissions', - }, - repartition: { - collectionId: 'u32', - tokenId: 'u32', - amount: 'u128', - }, - set_allowance_for_all: { - collectionId: 'u32', - operator: 'PalletEvmAccountBasicCrossAccountIdRepr', - approve: 'bool', - }, - force_repair_collection: { - collectionId: 'u32', - }, - force_repair_item: { - collectionId: 'u32', - itemId: 'u32' - } - } - }, - /** - * Lookup273: up_data_structs::CollectionMode - **/ - UpDataStructsCollectionMode: { - _enum: { - NFT: 'Null', - Fungible: 'u8', - ReFungible: 'Null' - } - }, - /** - * Lookup274: up_data_structs::CreateCollectionData> - **/ - UpDataStructsCreateCollectionData: { - mode: 'UpDataStructsCollectionMode', - access: 'Option', - name: 'Vec', - description: 'Vec', - tokenPrefix: 'Bytes', - limits: 'Option', - permissions: 'Option', - tokenPropertyPermissions: 'Vec', - properties: 'Vec', - adminList: 'Vec', - pendingSponsor: 'Option', - flags: '[u8;1]' - }, - /** - * Lookup275: pallet_evm::account::BasicCrossAccountIdRepr - **/ - PalletEvmAccountBasicCrossAccountIdRepr: { - _enum: { - Substrate: 'AccountId32', - Ethereum: 'H160' - } - }, - /** - * Lookup277: up_data_structs::AccessMode - **/ - UpDataStructsAccessMode: { - _enum: ['Normal', 'AllowList'] - }, - /** - * Lookup279: up_data_structs::CollectionLimits - **/ - UpDataStructsCollectionLimits: { - accountTokenOwnershipLimit: 'Option', - sponsoredDataSize: 'Option', - sponsoredDataRateLimit: 'Option', - tokenLimit: 'Option', - sponsorTransferTimeout: 'Option', - sponsorApproveTimeout: 'Option', - ownerCanTransfer: 'Option', - ownerCanDestroy: 'Option', - transfersEnabled: 'Option' - }, - /** - * Lookup281: up_data_structs::SponsoringRateLimit - **/ - UpDataStructsSponsoringRateLimit: { - _enum: { - SponsoringDisabled: 'Null', - Blocks: 'u32' - } - }, - /** - * Lookup284: up_data_structs::CollectionPermissions - **/ - UpDataStructsCollectionPermissions: { - access: 'Option', - mintMode: 'Option', - nesting: 'Option' - }, - /** - * Lookup286: up_data_structs::NestingPermissions - **/ - UpDataStructsNestingPermissions: { - tokenOwner: 'bool', - collectionAdmin: 'bool', - restricted: 'Option' - }, - /** - * Lookup288: up_data_structs::OwnerRestrictedSet - **/ - UpDataStructsOwnerRestrictedSet: 'BTreeSet', - /** - * Lookup294: up_data_structs::PropertyKeyPermission - **/ - UpDataStructsPropertyKeyPermission: { - key: 'Bytes', - permission: 'UpDataStructsPropertyPermission' - }, - /** - * Lookup296: up_data_structs::PropertyPermission - **/ - UpDataStructsPropertyPermission: { - mutable: 'bool', - collectionAdmin: 'bool', - tokenOwner: 'bool' - }, - /** - * Lookup299: up_data_structs::Property - **/ - UpDataStructsProperty: { - key: 'Bytes', - value: 'Bytes' - }, - /** - * Lookup304: up_data_structs::CreateItemData - **/ - UpDataStructsCreateItemData: { - _enum: { - NFT: 'UpDataStructsCreateNftData', - Fungible: 'UpDataStructsCreateFungibleData', - ReFungible: 'UpDataStructsCreateReFungibleData' - } - }, - /** - * Lookup305: up_data_structs::CreateNftData - **/ - UpDataStructsCreateNftData: { - properties: 'Vec' - }, - /** - * Lookup306: up_data_structs::CreateFungibleData - **/ - UpDataStructsCreateFungibleData: { - value: 'u128' - }, - /** - * Lookup307: up_data_structs::CreateReFungibleData - **/ - UpDataStructsCreateReFungibleData: { - pieces: 'u128', - properties: 'Vec' - }, - /** - * Lookup311: up_data_structs::CreateItemExData> - **/ - UpDataStructsCreateItemExData: { - _enum: { - NFT: 'Vec', - Fungible: 'BTreeMap', - RefungibleMultipleItems: 'Vec', - RefungibleMultipleOwners: 'UpDataStructsCreateRefungibleExMultipleOwners' - } - }, - /** - * Lookup313: up_data_structs::CreateNftExData> - **/ - UpDataStructsCreateNftExData: { - properties: 'Vec', - owner: 'PalletEvmAccountBasicCrossAccountIdRepr' - }, - /** - * Lookup320: up_data_structs::CreateRefungibleExSingleOwner> - **/ - UpDataStructsCreateRefungibleExSingleOwner: { - user: 'PalletEvmAccountBasicCrossAccountIdRepr', - pieces: 'u128', - properties: 'Vec' - }, - /** - * Lookup322: up_data_structs::CreateRefungibleExMultipleOwners> - **/ - UpDataStructsCreateRefungibleExMultipleOwners: { - users: 'BTreeMap', - properties: 'Vec' - }, - /** - * Lookup323: pallet_configuration::pallet::Call - **/ - PalletConfigurationCall: { - _enum: { - set_weight_to_fee_coefficient_override: { - coeff: 'Option', - }, - set_min_gas_price_override: { - coeff: 'Option', - }, - __Unused2: 'Null', - set_app_promotion_configuration_override: { - configuration: 'PalletConfigurationAppPromotionConfiguration', - }, - set_collator_selection_desired_collators: { - max: 'Option', - }, - set_collator_selection_license_bond: { - amount: 'Option', - }, - set_collator_selection_kick_threshold: { - threshold: 'Option' - } - } - }, - /** - * Lookup325: pallet_configuration::AppPromotionConfiguration - **/ - PalletConfigurationAppPromotionConfiguration: { - recalculationInterval: 'Option', - pendingInterval: 'Option', - intervalIncome: 'Option', - maxStakersPerCalculation: 'Option' - }, - /** - * Lookup330: pallet_structure::pallet::Call - **/ - PalletStructureCall: 'Null', - /** - * Lookup331: pallet_app_promotion::pallet::Call - **/ - PalletAppPromotionCall: { - _enum: { - set_admin_address: { - admin: 'PalletEvmAccountBasicCrossAccountIdRepr', - }, - stake: { - amount: 'u128', - }, - unstake_all: 'Null', - sponsor_collection: { - collectionId: 'u32', - }, - stop_sponsoring_collection: { - collectionId: 'u32', - }, - sponsor_contract: { - contractId: 'H160', - }, - stop_sponsoring_contract: { - contractId: 'H160', - }, - payout_stakers: { - stakersNumber: 'Option', - }, - unstake_partial: { - amount: 'u128', - }, - force_unstake: { - pendingBlocks: 'Vec' - } - } - }, - /** - * Lookup333: pallet_foreign_assets::module::Call - **/ - PalletForeignAssetsModuleCall: { - _enum: { - register_foreign_asset: { - owner: 'AccountId32', - location: 'StagingXcmVersionedMultiLocation', - metadata: 'PalletForeignAssetsModuleAssetMetadata', - }, - update_foreign_asset: { - foreignAssetId: 'u32', - location: 'StagingXcmVersionedMultiLocation', - metadata: 'PalletForeignAssetsModuleAssetMetadata' - } - } - }, - /** - * Lookup334: pallet_foreign_assets::module::AssetMetadata - **/ - PalletForeignAssetsModuleAssetMetadata: { - name: 'Bytes', - symbol: 'Bytes', - decimals: 'u8', - minimalBalance: 'u128' - }, - /** - * Lookup337: pallet_evm::pallet::Call - **/ - PalletEvmCall: { - _enum: { - withdraw: { - address: 'H160', - value: 'u128', - }, - call: { - source: 'H160', - target: 'H160', - input: 'Bytes', - value: 'U256', - gasLimit: 'u64', - maxFeePerGas: 'U256', - maxPriorityFeePerGas: 'Option', - nonce: 'Option', - accessList: 'Vec<(H160,Vec)>', - }, - create: { - source: 'H160', - init: 'Bytes', - value: 'U256', - gasLimit: 'u64', - maxFeePerGas: 'U256', - maxPriorityFeePerGas: 'Option', - nonce: 'Option', - accessList: 'Vec<(H160,Vec)>', - }, - create2: { - source: 'H160', - init: 'Bytes', - salt: 'H256', - value: 'U256', - gasLimit: 'u64', - maxFeePerGas: 'U256', - maxPriorityFeePerGas: 'Option', - nonce: 'Option', - accessList: 'Vec<(H160,Vec)>' - } - } - }, - /** - * Lookup344: pallet_ethereum::pallet::Call - **/ - PalletEthereumCall: { - _enum: { - transact: { - transaction: 'EthereumTransactionTransactionV2' - } - } - }, - /** - * Lookup345: ethereum::transaction::TransactionV2 - **/ - EthereumTransactionTransactionV2: { - _enum: { - Legacy: 'EthereumTransactionLegacyTransaction', - EIP2930: 'EthereumTransactionEip2930Transaction', - EIP1559: 'EthereumTransactionEip1559Transaction' - } - }, - /** - * Lookup346: ethereum::transaction::LegacyTransaction - **/ - EthereumTransactionLegacyTransaction: { - nonce: 'U256', - gasPrice: 'U256', - gasLimit: 'U256', - action: 'EthereumTransactionTransactionAction', - value: 'U256', - input: 'Bytes', - signature: 'EthereumTransactionTransactionSignature' - }, - /** - * Lookup347: ethereum::transaction::TransactionAction - **/ - EthereumTransactionTransactionAction: { - _enum: { - Call: 'H160', - Create: 'Null' - } - }, - /** - * Lookup348: ethereum::transaction::TransactionSignature - **/ - EthereumTransactionTransactionSignature: { - v: 'u64', - r: 'H256', - s: 'H256' - }, - /** - * Lookup350: ethereum::transaction::EIP2930Transaction - **/ - EthereumTransactionEip2930Transaction: { - chainId: 'u64', - nonce: 'U256', - gasPrice: 'U256', - gasLimit: 'U256', - action: 'EthereumTransactionTransactionAction', - value: 'U256', - input: 'Bytes', - accessList: 'Vec', - oddYParity: 'bool', - r: 'H256', - s: 'H256' - }, - /** - * Lookup352: ethereum::transaction::AccessListItem - **/ - EthereumTransactionAccessListItem: { - address: 'H160', - storageKeys: 'Vec' - }, - /** - * Lookup353: ethereum::transaction::EIP1559Transaction - **/ - EthereumTransactionEip1559Transaction: { - chainId: 'u64', - nonce: 'U256', - maxPriorityFeePerGas: 'U256', - maxFeePerGas: 'U256', - gasLimit: 'U256', - action: 'EthereumTransactionTransactionAction', - value: 'U256', - input: 'Bytes', - accessList: 'Vec', - oddYParity: 'bool', - r: 'H256', - s: 'H256' - }, - /** - * Lookup354: pallet_evm_contract_helpers::pallet::Call - **/ - PalletEvmContractHelpersCall: { - _enum: { - migrate_from_self_sponsoring: { - addresses: 'Vec' - } - } - }, - /** - * Lookup356: pallet_evm_migration::pallet::Call - **/ - PalletEvmMigrationCall: { - _enum: { - begin: { - address: 'H160', - }, - set_data: { - address: 'H160', - data: 'Vec<(H256,H256)>', - }, - finish: { - address: 'H160', - code: 'Bytes', - }, - insert_eth_logs: { - logs: 'Vec', - }, - insert_events: { - events: 'Vec', - }, - remove_rmrk_data: 'Null' - } - }, - /** - * Lookup360: ethereum::log::Log - **/ - EthereumLog: { - address: 'H160', - topics: 'Vec', - data: 'Bytes' - }, - /** - * Lookup361: pallet_maintenance::pallet::Call - **/ - PalletMaintenanceCall: { - _enum: ['enable', 'disable'] - }, - /** - * Lookup362: pallet_utility::pallet::Call - **/ - PalletUtilityCall: { - _enum: { - batch: { - calls: 'Vec', - }, - as_derivative: { - index: 'u16', - call: 'Call', - }, - batch_all: { - calls: 'Vec', - }, - dispatch_as: { - asOrigin: 'OpalRuntimeOriginCaller', - call: 'Call', - }, - force_batch: { - calls: 'Vec', - }, - with_weight: { - call: 'Call', - weight: 'SpWeightsWeightV2Weight' - } - } - }, - /** - * Lookup364: pallet_test_utils::pallet::Call - **/ - PalletTestUtilsCall: { - _enum: { - enable: 'Null', - set_test_value: { - value: 'u32', - }, - set_test_value_and_rollback: { - value: 'u32', - }, - inc_test_value: 'Null', - just_take_fee: 'Null', - batch_all: { - calls: 'Vec' - } - } - }, - /** - * Lookup366: pallet_scheduler::pallet::Event - **/ - PalletSchedulerEvent: { - _enum: { - Scheduled: { - when: 'u32', - index: 'u32', - }, - Canceled: { - when: 'u32', - index: 'u32', - }, - Dispatched: { - task: '(u32,u32)', - id: 'Option<[u8;32]>', - result: 'Result', - }, - CallUnavailable: { - task: '(u32,u32)', - id: 'Option<[u8;32]>', - }, - PeriodicFailed: { - task: '(u32,u32)', - id: 'Option<[u8;32]>', - }, - PermanentlyOverweight: { - task: '(u32,u32)', - id: 'Option<[u8;32]>' - } - } - }, - /** - * Lookup367: cumulus_pallet_xcmp_queue::pallet::Event - **/ - CumulusPalletXcmpQueueEvent: { - _enum: { - Success: { - messageHash: '[u8;32]', - messageId: '[u8;32]', - weight: 'SpWeightsWeightV2Weight', - }, - Fail: { - messageHash: '[u8;32]', - messageId: '[u8;32]', - error: 'StagingXcmV3TraitsError', - weight: 'SpWeightsWeightV2Weight', - }, - BadVersion: { - messageHash: '[u8;32]', - }, - BadFormat: { - messageHash: '[u8;32]', - }, - XcmpMessageSent: { - messageHash: '[u8;32]', - }, - OverweightEnqueued: { - sender: 'u32', - sentAt: 'u32', - index: 'u64', - required: 'SpWeightsWeightV2Weight', - }, - OverweightServiced: { - index: 'u64', - used: 'SpWeightsWeightV2Weight' - } - } - }, - /** - * Lookup368: pallet_xcm::pallet::Event - **/ - PalletXcmEvent: { - _enum: { - Attempted: { - outcome: 'StagingXcmV3TraitsOutcome', - }, - Sent: { - origin: 'StagingXcmV3MultiLocation', - destination: 'StagingXcmV3MultiLocation', - message: 'StagingXcmV3Xcm', - messageId: '[u8;32]', - }, - UnexpectedResponse: { - origin: 'StagingXcmV3MultiLocation', - queryId: 'u64', - }, - ResponseReady: { - queryId: 'u64', - response: 'StagingXcmV3Response', - }, - Notified: { - queryId: 'u64', - palletIndex: 'u8', - callIndex: 'u8', - }, - NotifyOverweight: { - queryId: 'u64', - palletIndex: 'u8', - callIndex: 'u8', - actualWeight: 'SpWeightsWeightV2Weight', - maxBudgetedWeight: 'SpWeightsWeightV2Weight', - }, - NotifyDispatchError: { - queryId: 'u64', - palletIndex: 'u8', - callIndex: 'u8', - }, - NotifyDecodeFailed: { - queryId: 'u64', - palletIndex: 'u8', - callIndex: 'u8', - }, - InvalidResponder: { - origin: 'StagingXcmV3MultiLocation', - queryId: 'u64', - expectedLocation: 'Option', - }, - InvalidResponderVersion: { - origin: 'StagingXcmV3MultiLocation', - queryId: 'u64', - }, - ResponseTaken: { - queryId: 'u64', - }, - AssetsTrapped: { - _alias: { - hash_: 'hash', - }, - hash_: 'H256', - origin: 'StagingXcmV3MultiLocation', - assets: 'StagingXcmVersionedMultiAssets', - }, - VersionChangeNotified: { - destination: 'StagingXcmV3MultiLocation', - result: 'u32', - cost: 'StagingXcmV3MultiassetMultiAssets', - messageId: '[u8;32]', - }, - SupportedVersionChanged: { - location: 'StagingXcmV3MultiLocation', - version: 'u32', - }, - NotifyTargetSendFail: { - location: 'StagingXcmV3MultiLocation', - queryId: 'u64', - error: 'StagingXcmV3TraitsError', - }, - NotifyTargetMigrationFail: { - location: 'StagingXcmVersionedMultiLocation', - queryId: 'u64', - }, - InvalidQuerierVersion: { - origin: 'StagingXcmV3MultiLocation', - queryId: 'u64', - }, - InvalidQuerier: { - origin: 'StagingXcmV3MultiLocation', - queryId: 'u64', - expectedQuerier: 'StagingXcmV3MultiLocation', - maybeActualQuerier: 'Option', - }, - VersionNotifyStarted: { - destination: 'StagingXcmV3MultiLocation', - cost: 'StagingXcmV3MultiassetMultiAssets', - messageId: '[u8;32]', - }, - VersionNotifyRequested: { - destination: 'StagingXcmV3MultiLocation', - cost: 'StagingXcmV3MultiassetMultiAssets', - messageId: '[u8;32]', - }, - VersionNotifyUnrequested: { - destination: 'StagingXcmV3MultiLocation', - cost: 'StagingXcmV3MultiassetMultiAssets', - messageId: '[u8;32]', - }, - FeesPaid: { - paying: 'StagingXcmV3MultiLocation', - fees: 'StagingXcmV3MultiassetMultiAssets', - }, - AssetsClaimed: { - _alias: { - hash_: 'hash', - }, - hash_: 'H256', - origin: 'StagingXcmV3MultiLocation', - assets: 'StagingXcmVersionedMultiAssets' - } - } - }, - /** - * Lookup369: staging_xcm::v3::traits::Outcome - **/ - StagingXcmV3TraitsOutcome: { - _enum: { - Complete: 'SpWeightsWeightV2Weight', - Incomplete: '(SpWeightsWeightV2Weight,StagingXcmV3TraitsError)', - Error: 'StagingXcmV3TraitsError' - } - }, - /** - * Lookup370: cumulus_pallet_xcm::pallet::Event - **/ - CumulusPalletXcmEvent: { - _enum: { - InvalidFormat: '[u8;32]', - UnsupportedVersion: '[u8;32]', - ExecutedDownward: '([u8;32],StagingXcmV3TraitsOutcome)' - } - }, - /** - * Lookup371: cumulus_pallet_dmp_queue::pallet::Event - **/ - CumulusPalletDmpQueueEvent: { - _enum: { - InvalidFormat: { - messageHash: '[u8;32]', - }, - UnsupportedVersion: { - messageHash: '[u8;32]', - }, - ExecutedDownward: { - messageHash: '[u8;32]', - messageId: '[u8;32]', - outcome: 'StagingXcmV3TraitsOutcome', - }, - WeightExhausted: { - messageHash: '[u8;32]', - messageId: '[u8;32]', - remainingWeight: 'SpWeightsWeightV2Weight', - requiredWeight: 'SpWeightsWeightV2Weight', - }, - OverweightEnqueued: { - messageHash: '[u8;32]', - messageId: '[u8;32]', - overweightIndex: 'u64', - requiredWeight: 'SpWeightsWeightV2Weight', - }, - OverweightServiced: { - overweightIndex: 'u64', - weightUsed: 'SpWeightsWeightV2Weight', - }, - MaxMessagesExhausted: { - messageHash: '[u8;32]' - } - } - }, - /** - * Lookup372: pallet_configuration::pallet::Event - **/ - PalletConfigurationEvent: { - _enum: { - NewDesiredCollators: { - desiredCollators: 'Option', - }, - NewCollatorLicenseBond: { - bondCost: 'Option', - }, - NewCollatorKickThreshold: { - lengthInBlocks: 'Option' - } - } - }, - /** - * Lookup373: pallet_common::pallet::Event - **/ - PalletCommonEvent: { - _enum: { - CollectionCreated: '(u32,u8,AccountId32)', - CollectionDestroyed: 'u32', - ItemCreated: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,u128)', - ItemDestroyed: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,u128)', - Transfer: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,u128)', - Approved: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,u128)', - ApprovedForAll: '(u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,bool)', - CollectionPropertySet: '(u32,Bytes)', - CollectionPropertyDeleted: '(u32,Bytes)', - TokenPropertySet: '(u32,u32,Bytes)', - TokenPropertyDeleted: '(u32,u32,Bytes)', - PropertyPermissionSet: '(u32,Bytes)', - AllowListAddressAdded: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)', - AllowListAddressRemoved: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)', - CollectionAdminAdded: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)', - CollectionAdminRemoved: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)', - CollectionLimitSet: 'u32', - CollectionOwnerChanged: '(u32,AccountId32)', - CollectionPermissionSet: 'u32', - CollectionSponsorSet: '(u32,AccountId32)', - SponsorshipConfirmed: '(u32,AccountId32)', - CollectionSponsorRemoved: 'u32' - } - }, - /** - * Lookup374: pallet_structure::pallet::Event - **/ - PalletStructureEvent: { - _enum: { - Executed: 'Result' - } - }, - /** - * Lookup375: pallet_app_promotion::pallet::Event - **/ - PalletAppPromotionEvent: { - _enum: { - StakingRecalculation: '(AccountId32,u128,u128)', - Stake: '(AccountId32,u128)', - Unstake: '(AccountId32,u128)', - SetAdmin: 'AccountId32' - } - }, - /** - * Lookup376: pallet_foreign_assets::module::Event - **/ - PalletForeignAssetsModuleEvent: { - _enum: { - ForeignAssetRegistered: { - assetId: 'u32', - assetAddress: 'StagingXcmV3MultiLocation', - metadata: 'PalletForeignAssetsModuleAssetMetadata', - }, - ForeignAssetUpdated: { - assetId: 'u32', - assetAddress: 'StagingXcmV3MultiLocation', - metadata: 'PalletForeignAssetsModuleAssetMetadata', - }, - AssetRegistered: { - assetId: 'PalletForeignAssetsAssetId', - metadata: 'PalletForeignAssetsModuleAssetMetadata', - }, - AssetUpdated: { - assetId: 'PalletForeignAssetsAssetId', - metadata: 'PalletForeignAssetsModuleAssetMetadata' - } - } - }, - /** - * Lookup377: pallet_evm::pallet::Event - **/ - PalletEvmEvent: { - _enum: { - Log: { - log: 'EthereumLog', - }, - Created: { - address: 'H160', - }, - CreatedFailed: { - address: 'H160', - }, - Executed: { - address: 'H160', - }, - ExecutedFailed: { - address: 'H160' - } - } - }, - /** - * Lookup378: pallet_ethereum::pallet::Event - **/ - PalletEthereumEvent: { - _enum: { - Executed: { - from: 'H160', - to: 'H160', - transactionHash: 'H256', - exitReason: 'EvmCoreErrorExitReason', - extraData: 'Bytes' - } - } - }, - /** - * Lookup379: evm_core::error::ExitReason - **/ - EvmCoreErrorExitReason: { - _enum: { - Succeed: 'EvmCoreErrorExitSucceed', - Error: 'EvmCoreErrorExitError', - Revert: 'EvmCoreErrorExitRevert', - Fatal: 'EvmCoreErrorExitFatal' - } - }, - /** - * Lookup380: evm_core::error::ExitSucceed - **/ - EvmCoreErrorExitSucceed: { - _enum: ['Stopped', 'Returned', 'Suicided'] - }, - /** - * Lookup381: evm_core::error::ExitError - **/ - EvmCoreErrorExitError: { - _enum: { - StackUnderflow: 'Null', - StackOverflow: 'Null', - InvalidJump: 'Null', - InvalidRange: 'Null', - DesignatedInvalid: 'Null', - CallTooDeep: 'Null', - CreateCollision: 'Null', - CreateContractLimit: 'Null', - OutOfOffset: 'Null', - OutOfGas: 'Null', - OutOfFund: 'Null', - PCUnderflow: 'Null', - CreateEmpty: 'Null', - Other: 'Text', - MaxNonce: 'Null', - InvalidCode: 'u8' - } - }, - /** - * Lookup385: evm_core::error::ExitRevert - **/ - EvmCoreErrorExitRevert: { - _enum: ['Reverted'] - }, - /** - * Lookup386: evm_core::error::ExitFatal - **/ - EvmCoreErrorExitFatal: { - _enum: { - NotSupported: 'Null', - UnhandledInterrupt: 'Null', - CallErrorAsFatal: 'EvmCoreErrorExitError', - Other: 'Text' - } - }, - /** - * Lookup387: pallet_evm_contract_helpers::pallet::Event - **/ - PalletEvmContractHelpersEvent: { - _enum: { - ContractSponsorSet: '(H160,AccountId32)', - ContractSponsorshipConfirmed: '(H160,AccountId32)', - ContractSponsorRemoved: 'H160' - } - }, - /** - * Lookup388: pallet_evm_migration::pallet::Event - **/ - PalletEvmMigrationEvent: { - _enum: ['TestEvent'] - }, - /** - * Lookup389: pallet_maintenance::pallet::Event - **/ - PalletMaintenanceEvent: { - _enum: ['MaintenanceEnabled', 'MaintenanceDisabled'] - }, - /** - * Lookup390: pallet_utility::pallet::Event - **/ - PalletUtilityEvent: { - _enum: { - BatchInterrupted: { - index: 'u32', - error: 'SpRuntimeDispatchError', - }, - BatchCompleted: 'Null', - BatchCompletedWithErrors: 'Null', - ItemCompleted: 'Null', - ItemFailed: { - error: 'SpRuntimeDispatchError', - }, - DispatchedAs: { - result: 'Result' - } - } - }, - /** - * Lookup391: pallet_test_utils::pallet::Event - **/ - PalletTestUtilsEvent: { - _enum: ['ValueIsSet', 'ShouldRollback', 'BatchCompleted'] - }, - /** - * Lookup392: frame_system::Phase - **/ - FrameSystemPhase: { - _enum: { - ApplyExtrinsic: 'u32', - Finalization: 'Null', - Initialization: 'Null' - } - }, - /** - * Lookup394: frame_system::LastRuntimeUpgradeInfo - **/ - FrameSystemLastRuntimeUpgradeInfo: { - specVersion: 'Compact', - specName: 'Text' - }, - /** - * Lookup395: frame_system::limits::BlockWeights - **/ - FrameSystemLimitsBlockWeights: { - baseBlock: 'SpWeightsWeightV2Weight', - maxBlock: 'SpWeightsWeightV2Weight', - perClass: 'FrameSupportDispatchPerDispatchClassWeightsPerClass' - }, - /** - * Lookup396: frame_support::dispatch::PerDispatchClass - **/ - FrameSupportDispatchPerDispatchClassWeightsPerClass: { - normal: 'FrameSystemLimitsWeightsPerClass', - operational: 'FrameSystemLimitsWeightsPerClass', - mandatory: 'FrameSystemLimitsWeightsPerClass' - }, - /** - * Lookup397: frame_system::limits::WeightsPerClass - **/ - FrameSystemLimitsWeightsPerClass: { - baseExtrinsic: 'SpWeightsWeightV2Weight', - maxExtrinsic: 'Option', - maxTotal: 'Option', - reserved: 'Option' - }, - /** - * Lookup399: frame_system::limits::BlockLength - **/ - FrameSystemLimitsBlockLength: { - max: 'FrameSupportDispatchPerDispatchClassU32' - }, - /** - * Lookup400: frame_support::dispatch::PerDispatchClass - **/ - FrameSupportDispatchPerDispatchClassU32: { - normal: 'u32', - operational: 'u32', - mandatory: 'u32' - }, - /** - * Lookup401: sp_weights::RuntimeDbWeight - **/ - SpWeightsRuntimeDbWeight: { - read: 'u64', - write: 'u64' - }, - /** - * Lookup402: sp_version::RuntimeVersion - **/ - SpVersionRuntimeVersion: { - specName: 'Text', - implName: 'Text', - authoringVersion: 'u32', - specVersion: 'u32', - implVersion: 'u32', - apis: 'Vec<([u8;8],u32)>', - transactionVersion: 'u32', - stateVersion: 'u8' - }, - /** - * Lookup406: frame_system::pallet::Error - **/ - FrameSystemError: { - _enum: ['InvalidSpecName', 'SpecVersionNeedsToIncrease', 'FailedToExtractRuntimeVersion', 'NonDefaultComposite', 'NonZeroRefCount', 'CallFiltered'] - }, - /** - * Lookup408: cumulus_pallet_parachain_system::unincluded_segment::Ancestor - **/ - CumulusPalletParachainSystemUnincludedSegmentAncestor: { - usedBandwidth: 'CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth', - paraHeadHash: 'Option', - consumedGoAheadSignal: 'Option' - }, - /** - * Lookup409: cumulus_pallet_parachain_system::unincluded_segment::UsedBandwidth - **/ - CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth: { - umpMsgCount: 'u32', - umpTotalBytes: 'u32', - hrmpOutgoing: 'BTreeMap' - }, - /** - * Lookup411: cumulus_pallet_parachain_system::unincluded_segment::HrmpChannelUpdate - **/ - CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate: { - msgCount: 'u32', - totalBytes: 'u32' - }, - /** - * Lookup415: polkadot_primitives::v5::UpgradeGoAhead - **/ - PolkadotPrimitivesV5UpgradeGoAhead: { - _enum: ['Abort', 'GoAhead'] - }, - /** - * Lookup416: cumulus_pallet_parachain_system::unincluded_segment::SegmentTracker - **/ - CumulusPalletParachainSystemUnincludedSegmentSegmentTracker: { - usedBandwidth: 'CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth', - hrmpWatermark: 'Option', - consumedGoAheadSignal: 'Option' - }, - /** - * Lookup418: polkadot_primitives::v5::UpgradeRestriction - **/ - PolkadotPrimitivesV5UpgradeRestriction: { - _enum: ['Present'] - }, - /** - * Lookup419: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot - **/ - CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: { - dmqMqcHead: 'H256', - relayDispatchQueueRemainingCapacity: 'CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity', - ingressChannels: 'Vec<(u32,PolkadotPrimitivesV5AbridgedHrmpChannel)>', - egressChannels: 'Vec<(u32,PolkadotPrimitivesV5AbridgedHrmpChannel)>' - }, - /** - * Lookup420: cumulus_pallet_parachain_system::relay_state_snapshot::RelayDispatchQueueRemainingCapacity - **/ - CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity: { - remainingCount: 'u32', - remainingSize: 'u32' - }, - /** - * Lookup423: polkadot_primitives::v5::AbridgedHrmpChannel - **/ - PolkadotPrimitivesV5AbridgedHrmpChannel: { - maxCapacity: 'u32', - maxTotalSize: 'u32', - maxMessageSize: 'u32', - msgCount: 'u32', - totalSize: 'u32', - mqcHead: 'Option' - }, - /** - * Lookup424: polkadot_primitives::v5::AbridgedHostConfiguration - **/ - PolkadotPrimitivesV5AbridgedHostConfiguration: { - maxCodeSize: 'u32', - maxHeadDataSize: 'u32', - maxUpwardQueueCount: 'u32', - maxUpwardQueueSize: 'u32', - maxUpwardMessageSize: 'u32', - maxUpwardMessageNumPerCandidate: 'u32', - hrmpMaxMessageNumPerCandidate: 'u32', - validationUpgradeCooldown: 'u32', - validationUpgradeDelay: 'u32', - asyncBackingParams: 'PolkadotPrimitivesVstagingAsyncBackingParams' - }, - /** - * Lookup425: polkadot_primitives::vstaging::AsyncBackingParams - **/ - PolkadotPrimitivesVstagingAsyncBackingParams: { - maxCandidateDepth: 'u32', - allowedAncestryLen: 'u32' - }, - /** - * Lookup431: polkadot_core_primitives::OutboundHrmpMessage - **/ - PolkadotCorePrimitivesOutboundHrmpMessage: { - recipient: 'u32', - data: 'Bytes' - }, - /** - * Lookup432: cumulus_pallet_parachain_system::CodeUpgradeAuthorization - **/ - CumulusPalletParachainSystemCodeUpgradeAuthorization: { - codeHash: 'H256', - checkVersion: 'bool' - }, - /** - * Lookup433: cumulus_pallet_parachain_system::pallet::Error - **/ - CumulusPalletParachainSystemError: { - _enum: ['OverlappingUpgrades', 'ProhibitedByPolkadot', 'TooBig', 'ValidationDataNotAvailable', 'HostConfigurationNotAvailable', 'NotScheduled', 'NothingAuthorized', 'Unauthorized'] - }, - /** - * Lookup435: pallet_collator_selection::pallet::Error - **/ - PalletCollatorSelectionError: { - _enum: ['TooManyCandidates', 'Unknown', 'Permission', 'AlreadyHoldingLicense', 'NoLicense', 'AlreadyCandidate', 'NotCandidate', 'TooManyInvulnerables', 'TooFewInvulnerables', 'AlreadyInvulnerable', 'NotInvulnerable', 'NoAssociatedValidatorId', 'ValidatorNotRegistered'] - }, - /** - * Lookup439: sp_core::crypto::KeyTypeId - **/ - SpCoreCryptoKeyTypeId: '[u8;4]', - /** - * Lookup440: pallet_session::pallet::Error - **/ - PalletSessionError: { - _enum: ['InvalidProof', 'NoAssociatedValidatorId', 'DuplicatedKey', 'NoKeys', 'NoAccount'] - }, - /** - * Lookup446: pallet_balances::types::BalanceLock - **/ - PalletBalancesBalanceLock: { - id: '[u8;8]', - amount: 'u128', - reasons: 'PalletBalancesReasons' - }, - /** - * Lookup447: pallet_balances::types::Reasons - **/ - PalletBalancesReasons: { - _enum: ['Fee', 'Misc', 'All'] - }, - /** - * Lookup450: pallet_balances::types::ReserveData - **/ - PalletBalancesReserveData: { - id: '[u8;16]', - amount: 'u128' - }, - /** - * Lookup454: opal_runtime::RuntimeHoldReason - **/ - OpalRuntimeRuntimeHoldReason: { - _enum: { - __Unused0: 'Null', - __Unused1: 'Null', - __Unused2: 'Null', - __Unused3: 'Null', - __Unused4: 'Null', - __Unused5: 'Null', - __Unused6: 'Null', - __Unused7: 'Null', - __Unused8: 'Null', - __Unused9: 'Null', - __Unused10: 'Null', - __Unused11: 'Null', - __Unused12: 'Null', - __Unused13: 'Null', - __Unused14: 'Null', - __Unused15: 'Null', - __Unused16: 'Null', - __Unused17: 'Null', - __Unused18: 'Null', - __Unused19: 'Null', - __Unused20: 'Null', - __Unused21: 'Null', - __Unused22: 'Null', - CollatorSelection: 'PalletCollatorSelectionHoldReason' - } - }, - /** - * Lookup455: pallet_collator_selection::pallet::HoldReason - **/ - PalletCollatorSelectionHoldReason: { - _enum: ['LicenseBond'] - }, - /** - * Lookup458: pallet_balances::types::IdAmount - **/ - PalletBalancesIdAmount: { - id: '[u8;16]', - amount: 'u128' - }, - /** - * Lookup460: pallet_balances::pallet::Error - **/ - PalletBalancesError: { - _enum: ['VestingBalance', 'LiquidityRestrictions', 'InsufficientBalance', 'ExistentialDeposit', 'Expendability', 'ExistingVestingSchedule', 'DeadAccount', 'TooManyReserves', 'TooManyHolds', 'TooManyFreezes'] - }, - /** - * Lookup462: pallet_transaction_payment::Releases - **/ - PalletTransactionPaymentReleases: { - _enum: ['V1Ancient', 'V2'] - }, - /** - * Lookup463: pallet_treasury::Proposal - **/ - PalletTreasuryProposal: { - proposer: 'AccountId32', - value: 'u128', - beneficiary: 'AccountId32', - bond: 'u128' - }, - /** - * Lookup466: frame_support::PalletId - **/ - FrameSupportPalletId: '[u8;8]', - /** - * Lookup467: pallet_treasury::pallet::Error - **/ - PalletTreasuryError: { - _enum: ['InsufficientProposersBalance', 'InvalidIndex', 'TooManyApprovals', 'InsufficientPermission', 'ProposalNotApproved'] - }, - /** - * Lookup468: pallet_sudo::pallet::Error - **/ - PalletSudoError: { - _enum: ['RequireSudo'] - }, - /** - * Lookup470: orml_vesting::module::Error - **/ - OrmlVestingModuleError: { - _enum: ['ZeroVestingPeriod', 'ZeroVestingPeriodCount', 'InsufficientBalanceToLock', 'TooManyVestingSchedules', 'AmountLow', 'MaxVestingSchedulesExceeded'] - }, - /** - * Lookup471: orml_xtokens::module::Error - **/ - OrmlXtokensModuleError: { - _enum: ['AssetHasNoReserve', 'NotCrossChainTransfer', 'InvalidDest', 'NotCrossChainTransferableCurrency', 'UnweighableMessage', 'XcmExecutionFailed', 'CannotReanchor', 'InvalidAncestry', 'InvalidAsset', 'DestinationNotInvertible', 'BadVersion', 'DistinctReserveForAssetAndFee', 'ZeroFee', 'ZeroAmount', 'TooManyAssetsBeingSent', 'AssetIndexNonExistent', 'FeeNotEnough', 'NotSupportedMultiLocation', 'MinXcmFeeNotDefined'] - }, - /** - * Lookup474: orml_tokens::BalanceLock - **/ - OrmlTokensBalanceLock: { - id: '[u8;8]', - amount: 'u128' - }, - /** - * Lookup476: orml_tokens::AccountData - **/ - OrmlTokensAccountData: { - free: 'u128', - reserved: 'u128', - frozen: 'u128' - }, - /** - * Lookup478: orml_tokens::ReserveData - **/ - OrmlTokensReserveData: { - id: 'Null', - amount: 'u128' - }, - /** - * Lookup480: orml_tokens::module::Error - **/ - OrmlTokensModuleError: { - _enum: ['BalanceTooLow', 'AmountIntoBalanceFailed', 'LiquidityRestrictions', 'MaxLocksExceeded', 'KeepAlive', 'ExistentialDeposit', 'DeadAccount', 'TooManyReserves'] - }, - /** - * Lookup485: pallet_identity::types::RegistrarInfo - **/ - PalletIdentityRegistrarInfo: { - account: 'AccountId32', - fee: 'u128', - fields: 'PalletIdentityBitFlags' - }, - /** - * Lookup487: pallet_identity::pallet::Error - **/ - PalletIdentityError: { - _enum: ['TooManySubAccounts', 'NotFound', 'NotNamed', 'EmptyIndex', 'FeeChanged', 'NoIdentity', 'StickyJudgement', 'JudgementGiven', 'InvalidJudgement', 'InvalidIndex', 'InvalidTarget', 'TooManyFields', 'TooManyRegistrars', 'AlreadyClaimed', 'NotSub', 'NotOwned', 'JudgementForDifferentIdentity', 'JudgementPaymentFailed'] - }, - /** - * Lookup488: pallet_preimage::RequestStatus - **/ - PalletPreimageRequestStatus: { - _enum: { - Unrequested: { - deposit: '(AccountId32,u128)', - len: 'u32', - }, - Requested: { - deposit: 'Option<(AccountId32,u128)>', - count: 'u32', - len: 'Option' - } - } - }, - /** - * Lookup493: pallet_preimage::pallet::Error - **/ - PalletPreimageError: { - _enum: ['TooBig', 'AlreadyNoted', 'NotAuthorized', 'NotNoted', 'Requested', 'NotRequested'] - }, - /** - * Lookup499: pallet_democracy::types::ReferendumInfo, Balance> - **/ - PalletDemocracyReferendumInfo: { - _enum: { - Ongoing: 'PalletDemocracyReferendumStatus', - Finished: { - approved: 'bool', - end: 'u32' - } - } - }, - /** - * Lookup500: pallet_democracy::types::ReferendumStatus, Balance> - **/ - PalletDemocracyReferendumStatus: { - end: 'u32', - proposal: 'FrameSupportPreimagesBounded', - threshold: 'PalletDemocracyVoteThreshold', - delay: 'u32', - tally: 'PalletDemocracyTally' - }, - /** - * Lookup501: pallet_democracy::types::Tally - **/ - PalletDemocracyTally: { - ayes: 'u128', - nays: 'u128', - turnout: 'u128' - }, - /** - * Lookup502: pallet_democracy::vote::Voting - **/ - PalletDemocracyVoteVoting: { - _enum: { - Direct: { - votes: 'Vec<(u32,PalletDemocracyVoteAccountVote)>', - delegations: 'PalletDemocracyDelegations', - prior: 'PalletDemocracyVotePriorLock', - }, - Delegating: { - balance: 'u128', - target: 'AccountId32', - conviction: 'PalletDemocracyConviction', - delegations: 'PalletDemocracyDelegations', - prior: 'PalletDemocracyVotePriorLock' - } - } - }, - /** - * Lookup506: pallet_democracy::types::Delegations - **/ - PalletDemocracyDelegations: { - votes: 'u128', - capital: 'u128' - }, - /** - * Lookup507: pallet_democracy::vote::PriorLock - **/ - PalletDemocracyVotePriorLock: '(u32,u128)', - /** - * Lookup510: pallet_democracy::pallet::Error - **/ - PalletDemocracyError: { - _enum: ['ValueLow', 'ProposalMissing', 'AlreadyCanceled', 'DuplicateProposal', 'ProposalBlacklisted', 'NotSimpleMajority', 'InvalidHash', 'NoProposal', 'AlreadyVetoed', 'ReferendumInvalid', 'NoneWaiting', 'NotVoter', 'NoPermission', 'AlreadyDelegating', 'InsufficientFunds', 'NotDelegating', 'VotesExist', 'InstantNotAllowed', 'Nonsense', 'WrongUpperBound', 'MaxVotesReached', 'TooMany', 'VotingPeriodLow', 'PreimageNotExist'] - }, - /** - * Lookup512: pallet_collective::Votes - **/ - PalletCollectiveVotes: { - index: 'u32', - threshold: 'u32', - ayes: 'Vec', - nays: 'Vec', - end: 'u32' - }, - /** - * Lookup513: pallet_collective::pallet::Error - **/ - PalletCollectiveError: { - _enum: ['NotMember', 'DuplicateProposal', 'ProposalMissing', 'WrongIndex', 'DuplicateVote', 'AlreadyInitialized', 'TooEarly', 'TooManyProposals', 'WrongProposalWeight', 'WrongProposalLength', 'PrimeAccountNotMember'] - }, - /** - * Lookup517: pallet_membership::pallet::Error - **/ - PalletMembershipError: { - _enum: ['AlreadyMember', 'NotMember', 'TooManyMembers'] - }, - /** - * Lookup520: pallet_ranked_collective::MemberRecord - **/ - PalletRankedCollectiveMemberRecord: { - rank: 'u16' - }, - /** - * Lookup525: pallet_ranked_collective::pallet::Error - **/ - PalletRankedCollectiveError: { - _enum: ['AlreadyMember', 'NotMember', 'NotPolling', 'Ongoing', 'NoneRemaining', 'Corruption', 'RankTooLow', 'InvalidWitness', 'NoPermission'] - }, - /** - * Lookup526: pallet_referenda::types::ReferendumInfo, Balance, pallet_ranked_collective::Tally, sp_core::crypto::AccountId32, ScheduleAddress> - **/ - PalletReferendaReferendumInfo: { - _enum: { - Ongoing: 'PalletReferendaReferendumStatus', - Approved: '(u32,Option,Option)', - Rejected: '(u32,Option,Option)', - Cancelled: '(u32,Option,Option)', - TimedOut: '(u32,Option,Option)', - Killed: 'u32' - } - }, - /** - * Lookup527: pallet_referenda::types::ReferendumStatus, Balance, pallet_ranked_collective::Tally, sp_core::crypto::AccountId32, ScheduleAddress> - **/ - PalletReferendaReferendumStatus: { - track: 'u16', - origin: 'OpalRuntimeOriginCaller', - proposal: 'FrameSupportPreimagesBounded', - enactment: 'FrameSupportScheduleDispatchTime', - submitted: 'u32', - submissionDeposit: 'PalletReferendaDeposit', - decisionDeposit: 'Option', - deciding: 'Option', - tally: 'PalletRankedCollectiveTally', - inQueue: 'bool', - alarm: 'Option<(u32,(u32,u32))>' - }, - /** - * Lookup528: pallet_referenda::types::Deposit - **/ - PalletReferendaDeposit: { - who: 'AccountId32', - amount: 'u128' - }, - /** - * Lookup531: pallet_referenda::types::DecidingStatus - **/ - PalletReferendaDecidingStatus: { - since: 'u32', - confirming: 'Option' - }, - /** - * Lookup537: pallet_referenda::types::TrackInfo - **/ - PalletReferendaTrackInfo: { - name: 'Text', - maxDeciding: 'u32', - decisionDeposit: 'u128', - preparePeriod: 'u32', - decisionPeriod: 'u32', - confirmPeriod: 'u32', - minEnactmentPeriod: 'u32', - minApproval: 'PalletReferendaCurve', - minSupport: 'PalletReferendaCurve' - }, - /** - * Lookup538: pallet_referenda::types::Curve - **/ - PalletReferendaCurve: { - _enum: { - LinearDecreasing: { - length: 'Perbill', - floor: 'Perbill', - ceil: 'Perbill', - }, - SteppedDecreasing: { - begin: 'Perbill', - end: 'Perbill', - step: 'Perbill', - period: 'Perbill', - }, - Reciprocal: { - factor: 'i64', - xOffset: 'i64', - yOffset: 'i64' - } - } - }, - /** - * Lookup541: pallet_referenda::pallet::Error - **/ - PalletReferendaError: { - _enum: ['NotOngoing', 'HasDeposit', 'BadTrack', 'Full', 'QueueEmpty', 'BadReferendum', 'NothingToDo', 'NoTrack', 'Unfinished', 'NoPermission', 'NoDeposit', 'BadStatus', 'PreimageNotExist'] - }, - /** - * Lookup544: pallet_scheduler::Scheduled, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32> - **/ - PalletSchedulerScheduled: { - maybeId: 'Option<[u8;32]>', - priority: 'u8', - call: 'FrameSupportPreimagesBounded', - maybePeriodic: 'Option<(u32,u32)>', - origin: 'OpalRuntimeOriginCaller' - }, - /** - * Lookup546: pallet_scheduler::pallet::Error - **/ - PalletSchedulerError: { - _enum: ['FailedToSchedule', 'NotFound', 'TargetBlockNumberInPast', 'RescheduleNoChange', 'Named'] - }, - /** - * Lookup548: cumulus_pallet_xcmp_queue::InboundChannelDetails - **/ - CumulusPalletXcmpQueueInboundChannelDetails: { - sender: 'u32', - state: 'CumulusPalletXcmpQueueInboundState', - messageMetadata: 'Vec<(u32,PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat)>' - }, - /** - * Lookup549: cumulus_pallet_xcmp_queue::InboundState - **/ - CumulusPalletXcmpQueueInboundState: { - _enum: ['Ok', 'Suspended'] - }, - /** - * Lookup552: polkadot_parachain_primitives::primitives::XcmpMessageFormat - **/ - PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat: { - _enum: ['ConcatenatedVersionedXcm', 'ConcatenatedEncodedBlob', 'Signals'] - }, - /** - * Lookup555: cumulus_pallet_xcmp_queue::OutboundChannelDetails - **/ - CumulusPalletXcmpQueueOutboundChannelDetails: { - recipient: 'u32', - state: 'CumulusPalletXcmpQueueOutboundState', - signalsExist: 'bool', - firstIndex: 'u16', - lastIndex: 'u16' - }, - /** - * Lookup556: cumulus_pallet_xcmp_queue::OutboundState - **/ - CumulusPalletXcmpQueueOutboundState: { - _enum: ['Ok', 'Suspended'] - }, - /** - * Lookup558: cumulus_pallet_xcmp_queue::QueueConfigData - **/ - CumulusPalletXcmpQueueQueueConfigData: { - suspendThreshold: 'u32', - dropThreshold: 'u32', - resumeThreshold: 'u32', - thresholdWeight: 'SpWeightsWeightV2Weight', - weightRestrictDecay: 'SpWeightsWeightV2Weight', - xcmpMaxIndividualWeight: 'SpWeightsWeightV2Weight' - }, - /** - * Lookup560: cumulus_pallet_xcmp_queue::pallet::Error - **/ - CumulusPalletXcmpQueueError: { - _enum: ['FailedToSend', 'BadXcmOrigin', 'BadXcm', 'BadOverweightIndex', 'WeightOverLimit'] - }, - /** - * Lookup561: pallet_xcm::pallet::QueryStatus - **/ - PalletXcmQueryStatus: { - _enum: { - Pending: { - responder: 'StagingXcmVersionedMultiLocation', - maybeMatchQuerier: 'Option', - maybeNotify: 'Option<(u8,u8)>', - timeout: 'u32', - }, - VersionNotifier: { - origin: 'StagingXcmVersionedMultiLocation', - isActive: 'bool', - }, - Ready: { - response: 'StagingXcmVersionedResponse', - at: 'u32' - } - } - }, - /** - * Lookup565: staging_xcm::VersionedResponse - **/ - StagingXcmVersionedResponse: { - _enum: { - __Unused0: 'Null', - __Unused1: 'Null', - V2: 'StagingXcmV2Response', - V3: 'StagingXcmV3Response' - } - }, - /** - * Lookup571: pallet_xcm::pallet::VersionMigrationStage - **/ - PalletXcmVersionMigrationStage: { - _enum: { - MigrateSupportedVersion: 'Null', - MigrateVersionNotifiers: 'Null', - NotifyCurrentTargets: 'Option', - MigrateAndNotifyOldTargets: 'Null' - } - }, - /** - * Lookup574: staging_xcm::VersionedAssetId - **/ - StagingXcmVersionedAssetId: { - _enum: { - __Unused0: 'Null', - __Unused1: 'Null', - __Unused2: 'Null', - V3: 'StagingXcmV3MultiassetAssetId' - } - }, - /** - * Lookup575: pallet_xcm::pallet::RemoteLockedFungibleRecord - **/ - PalletXcmRemoteLockedFungibleRecord: { - amount: 'u128', - owner: 'StagingXcmVersionedMultiLocation', - locker: 'StagingXcmVersionedMultiLocation', - consumers: 'Vec<(Null,u128)>' - }, - /** - * Lookup582: pallet_xcm::pallet::Error - **/ - PalletXcmError: { - _enum: ['Unreachable', 'SendFailure', 'Filtered', 'UnweighableMessage', 'DestinationNotInvertible', 'Empty', 'CannotReanchor', 'TooManyAssets', 'InvalidOrigin', 'BadVersion', 'BadLocation', 'NoSubscription', 'AlreadySubscribed', 'InvalidAsset', 'LowBalance', 'TooManyLocks', 'AccountNotSovereign', 'FeesNotMet', 'LockNotFound', 'InUse'] - }, - /** - * Lookup583: cumulus_pallet_xcm::pallet::Error - **/ - CumulusPalletXcmError: 'Null', - /** - * Lookup584: cumulus_pallet_dmp_queue::ConfigData - **/ - CumulusPalletDmpQueueConfigData: { - maxIndividual: 'SpWeightsWeightV2Weight' - }, - /** - * Lookup585: cumulus_pallet_dmp_queue::PageIndexData - **/ - CumulusPalletDmpQueuePageIndexData: { - beginUsed: 'u32', - endUsed: 'u32', - overweightCount: 'u64' - }, - /** - * Lookup588: cumulus_pallet_dmp_queue::pallet::Error - **/ - CumulusPalletDmpQueueError: { - _enum: ['Unknown', 'OverLimit'] - }, - /** - * Lookup592: pallet_unique::pallet::Error - **/ - PalletUniqueError: { - _enum: ['CollectionDecimalPointLimitExceeded', 'EmptyArgument', 'RepartitionCalledOnNonRefungibleCollection'] - }, - /** - * Lookup593: pallet_configuration::pallet::Error - **/ - PalletConfigurationError: { - _enum: ['InconsistentConfiguration'] - }, - /** - * Lookup594: up_data_structs::Collection - **/ - UpDataStructsCollection: { - owner: 'AccountId32', - mode: 'UpDataStructsCollectionMode', - name: 'Vec', - description: 'Vec', - tokenPrefix: 'Bytes', - sponsorship: 'UpDataStructsSponsorshipStateAccountId32', - limits: 'UpDataStructsCollectionLimits', - permissions: 'UpDataStructsCollectionPermissions', - flags: '[u8;1]' - }, - /** - * Lookup595: up_data_structs::SponsorshipState - **/ - UpDataStructsSponsorshipStateAccountId32: { - _enum: { - Disabled: 'Null', - Unconfirmed: 'AccountId32', - Confirmed: 'AccountId32' - } - }, - /** - * Lookup596: up_data_structs::Properties - **/ - UpDataStructsProperties: { - map: 'UpDataStructsPropertiesMapBoundedVec', - consumedSpace: 'u32', - reserved: 'u32' - }, - /** - * Lookup597: up_data_structs::PropertiesMap> - **/ - UpDataStructsPropertiesMapBoundedVec: 'BTreeMap', - /** - * Lookup602: up_data_structs::PropertiesMap - **/ - UpDataStructsPropertiesMapPropertyPermission: 'BTreeMap', - /** - * Lookup609: up_data_structs::CollectionStats - **/ - UpDataStructsCollectionStats: { - created: 'u32', - destroyed: 'u32', - alive: 'u32' - }, - /** - * Lookup610: up_data_structs::TokenChild - **/ - UpDataStructsTokenChild: { - token: 'u32', - collection: 'u32' - }, - /** - * Lookup611: PhantomType::up_data_structs - **/ - PhantomTypeUpDataStructs: '[(UpDataStructsTokenData,UpDataStructsRpcCollection,UpPovEstimateRpcPovInfo);0]', - /** - * Lookup613: up_data_structs::TokenData> - **/ - UpDataStructsTokenData: { - properties: 'Vec', - owner: 'Option', - pieces: 'u128' - }, - /** - * Lookup614: up_data_structs::RpcCollection - **/ - UpDataStructsRpcCollection: { - owner: 'AccountId32', - mode: 'UpDataStructsCollectionMode', - name: 'Vec', - description: 'Vec', - tokenPrefix: 'Bytes', - sponsorship: 'UpDataStructsSponsorshipStateAccountId32', - limits: 'UpDataStructsCollectionLimits', - permissions: 'UpDataStructsCollectionPermissions', - tokenPropertyPermissions: 'Vec', - properties: 'Vec', - readOnly: 'bool', - flags: 'UpDataStructsRpcCollectionFlags' - }, - /** - * Lookup615: up_data_structs::RpcCollectionFlags - **/ - UpDataStructsRpcCollectionFlags: { - foreign: 'bool', - erc721metadata: 'bool' - }, - /** - * Lookup616: up_pov_estimate_rpc::PovInfo - **/ - UpPovEstimateRpcPovInfo: { - proofSize: 'u64', - compactProofSize: 'u64', - compressedProofSize: 'u64', - results: 'Vec, SpRuntimeTransactionValidityTransactionValidityError>>', - keyValues: 'Vec' - }, - /** - * Lookup619: sp_runtime::transaction_validity::TransactionValidityError - **/ - SpRuntimeTransactionValidityTransactionValidityError: { - _enum: { - Invalid: 'SpRuntimeTransactionValidityInvalidTransaction', - Unknown: 'SpRuntimeTransactionValidityUnknownTransaction' - } - }, - /** - * Lookup620: sp_runtime::transaction_validity::InvalidTransaction - **/ - SpRuntimeTransactionValidityInvalidTransaction: { - _enum: { - Call: 'Null', - Payment: 'Null', - Future: 'Null', - Stale: 'Null', - BadProof: 'Null', - AncientBirthBlock: 'Null', - ExhaustsResources: 'Null', - Custom: 'u8', - BadMandatory: 'Null', - MandatoryValidation: 'Null', - BadSigner: 'Null' - } - }, - /** - * Lookup621: sp_runtime::transaction_validity::UnknownTransaction - **/ - SpRuntimeTransactionValidityUnknownTransaction: { - _enum: { - CannotLookup: 'Null', - NoUnsignedValidator: 'Null', - Custom: 'u8' - } - }, - /** - * Lookup623: up_pov_estimate_rpc::TrieKeyValue - **/ - UpPovEstimateRpcTrieKeyValue: { - key: 'Bytes', - value: 'Bytes' - }, - /** - * Lookup625: pallet_common::pallet::Error - **/ - PalletCommonError: { - _enum: ['CollectionNotFound', 'MustBeTokenOwner', 'NoPermission', 'CantDestroyNotEmptyCollection', 'PublicMintingNotAllowed', 'AddressNotInAllowlist', 'CollectionNameLimitExceeded', 'CollectionDescriptionLimitExceeded', 'CollectionTokenPrefixLimitExceeded', 'TotalCollectionsLimitExceeded', 'CollectionAdminCountExceeded', 'CollectionLimitBoundsExceeded', 'OwnerPermissionsCantBeReverted', 'TransferNotAllowed', 'AccountTokenLimitExceeded', 'CollectionTokenLimitExceeded', 'MetadataFlagFrozen', 'TokenNotFound', 'TokenValueTooLow', 'ApprovedValueTooLow', 'CantApproveMoreThanOwned', 'AddressIsNotEthMirror', 'AddressIsZero', 'UnsupportedOperation', 'NotSufficientFounds', 'UserIsNotAllowedToNest', 'SourceCollectionIsNotAllowedToNest', 'CollectionFieldSizeExceeded', 'NoSpaceForProperty', 'PropertyLimitReached', 'PropertyKeyIsTooLong', 'InvalidCharacterInPropertyKey', 'EmptyPropertyKey', 'CollectionIsExternal', 'CollectionIsInternal', 'ConfirmSponsorshipFail', 'UserIsNotCollectionAdmin', 'FungibleItemsHaveNoId'] - }, - /** - * Lookup627: pallet_fungible::pallet::Error - **/ - PalletFungibleError: { - _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsDontHaveData', 'FungibleDisallowsNesting', 'SettingPropertiesNotAllowed', 'SettingAllowanceForAllNotAllowed', 'FungibleTokensAreAlwaysValid'] - }, - /** - * Lookup632: pallet_refungible::pallet::Error - **/ - PalletRefungibleError: { - _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces', 'RepartitionWhileNotOwningAllPieces', 'RefungibleDisallowsNesting', 'SettingPropertiesNotAllowed'] - }, - /** - * Lookup633: pallet_nonfungible::ItemData> - **/ - PalletNonfungibleItemData: { - owner: 'PalletEvmAccountBasicCrossAccountIdRepr' - }, - /** - * Lookup635: up_data_structs::PropertyScope - **/ - UpDataStructsPropertyScope: { - _enum: ['None', 'Rmrk'] - }, - /** - * Lookup638: pallet_nonfungible::pallet::Error - **/ - PalletNonfungibleError: { - _enum: ['NotNonfungibleDataUsedToMintFungibleCollectionToken', 'NonfungibleItemsHaveNoAmount', 'CantBurnNftWithChildren'] - }, - /** - * Lookup639: pallet_structure::pallet::Error - **/ - PalletStructureError: { - _enum: ['OuroborosDetected', 'DepthLimit', 'BreadthLimit', 'TokenNotFound', 'CantNestTokenUnderCollection'] - }, - /** - * Lookup644: pallet_app_promotion::pallet::Error - **/ - PalletAppPromotionError: { - _enum: ['AdminNotSet', 'NoPermission', 'NotSufficientFunds', 'PendingForBlockOverflow', 'SponsorNotSet', 'InsufficientStakedBalance', 'InconsistencyState'] - }, - /** - * Lookup645: pallet_foreign_assets::module::Error - **/ - PalletForeignAssetsModuleError: { - _enum: ['BadLocation', 'MultiLocationExisted', 'AssetIdNotExists', 'AssetIdExisted'] - }, - /** - * Lookup646: pallet_evm::CodeMetadata - **/ - PalletEvmCodeMetadata: { - _alias: { - size_: 'size', - hash_: 'hash' - }, - size_: 'u64', - hash_: 'H256' - }, - /** - * Lookup648: pallet_evm::pallet::Error - **/ - PalletEvmError: { - _enum: ['BalanceLow', 'FeeOverflow', 'PaymentOverflow', 'WithdrawFailed', 'GasPriceTooLow', 'InvalidNonce', 'GasLimitTooLow', 'GasLimitTooHigh', 'Undefined', 'Reentrancy', 'TransactionMustComeFromEOA'] - }, - /** - * Lookup651: fp_rpc::TransactionStatus - **/ - FpRpcTransactionStatus: { - transactionHash: 'H256', - transactionIndex: 'u32', - from: 'H160', - to: 'Option', - contractAddress: 'Option', - logs: 'Vec', - logsBloom: 'EthbloomBloom' - }, - /** - * Lookup653: ethbloom::Bloom - **/ - EthbloomBloom: '[u8;256]', - /** - * Lookup655: ethereum::receipt::ReceiptV3 - **/ - EthereumReceiptReceiptV3: { - _enum: { - Legacy: 'EthereumReceiptEip658ReceiptData', - EIP2930: 'EthereumReceiptEip658ReceiptData', - EIP1559: 'EthereumReceiptEip658ReceiptData' - } - }, - /** - * Lookup656: ethereum::receipt::EIP658ReceiptData - **/ - EthereumReceiptEip658ReceiptData: { - statusCode: 'u8', - usedGas: 'U256', - logsBloom: 'EthbloomBloom', - logs: 'Vec' - }, - /** - * Lookup657: ethereum::block::Block - **/ - EthereumBlock: { - header: 'EthereumHeader', - transactions: 'Vec', - ommers: 'Vec' - }, - /** - * Lookup658: ethereum::header::Header - **/ - EthereumHeader: { - parentHash: 'H256', - ommersHash: 'H256', - beneficiary: 'H160', - stateRoot: 'H256', - transactionsRoot: 'H256', - receiptsRoot: 'H256', - logsBloom: 'EthbloomBloom', - difficulty: 'U256', - number: 'U256', - gasLimit: 'U256', - gasUsed: 'U256', - timestamp: 'u64', - extraData: 'Bytes', - mixHash: 'H256', - nonce: 'EthereumTypesHashH64' - }, - /** - * Lookup659: ethereum_types::hash::H64 - **/ - EthereumTypesHashH64: '[u8;8]', - /** - * Lookup664: pallet_ethereum::pallet::Error - **/ - PalletEthereumError: { - _enum: ['InvalidSignature', 'PreLogExists'] - }, - /** - * Lookup665: pallet_evm_coder_substrate::pallet::Error - **/ - PalletEvmCoderSubstrateError: { - _enum: ['OutOfGas', 'OutOfFund'] - }, - /** - * Lookup666: up_data_structs::SponsorshipState> - **/ - UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: { - _enum: { - Disabled: 'Null', - Unconfirmed: 'PalletEvmAccountBasicCrossAccountIdRepr', - Confirmed: 'PalletEvmAccountBasicCrossAccountIdRepr' - } - }, - /** - * Lookup667: pallet_evm_contract_helpers::SponsoringModeT - **/ - PalletEvmContractHelpersSponsoringModeT: { - _enum: ['Disabled', 'Allowlisted', 'Generous'] - }, - /** - * Lookup673: pallet_evm_contract_helpers::pallet::Error - **/ - PalletEvmContractHelpersError: { - _enum: ['NoPermission', 'NoPendingSponsor', 'TooManyMethodsHaveSponsoredLimit'] - }, - /** - * Lookup674: pallet_evm_migration::pallet::Error - **/ - PalletEvmMigrationError: { - _enum: ['AccountNotEmpty', 'AccountIsNotMigrating', 'BadEvent'] - }, - /** - * Lookup675: pallet_maintenance::pallet::Error - **/ - PalletMaintenanceError: 'Null', - /** - * Lookup676: pallet_utility::pallet::Error - **/ - PalletUtilityError: { - _enum: ['TooManyCalls'] - }, - /** - * Lookup677: pallet_test_utils::pallet::Error - **/ - PalletTestUtilsError: { - _enum: ['TestPalletDisabled', 'TriggerRollback'] - }, - /** - * Lookup679: sp_runtime::MultiSignature - **/ - SpRuntimeMultiSignature: { - _enum: { - Ed25519: 'SpCoreEd25519Signature', - Sr25519: 'SpCoreSr25519Signature', - Ecdsa: 'SpCoreEcdsaSignature' - } - }, - /** - * Lookup680: sp_core::ed25519::Signature - **/ - SpCoreEd25519Signature: '[u8;64]', - /** - * Lookup682: sp_core::sr25519::Signature - **/ - SpCoreSr25519Signature: '[u8;64]', - /** - * Lookup683: sp_core::ecdsa::Signature - **/ - SpCoreEcdsaSignature: '[u8;65]', - /** - * Lookup686: frame_system::extensions::check_spec_version::CheckSpecVersion - **/ - FrameSystemExtensionsCheckSpecVersion: 'Null', - /** - * Lookup687: frame_system::extensions::check_tx_version::CheckTxVersion - **/ - FrameSystemExtensionsCheckTxVersion: 'Null', - /** - * Lookup688: frame_system::extensions::check_genesis::CheckGenesis - **/ - FrameSystemExtensionsCheckGenesis: 'Null', - /** - * Lookup691: frame_system::extensions::check_nonce::CheckNonce - **/ - FrameSystemExtensionsCheckNonce: 'Compact', - /** - * Lookup692: frame_system::extensions::check_weight::CheckWeight - **/ - FrameSystemExtensionsCheckWeight: 'Null', - /** - * Lookup693: opal_runtime::runtime_common::maintenance::CheckMaintenance - **/ - OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: 'Null', - /** - * Lookup694: opal_runtime::runtime_common::identity::DisableIdentityCalls - **/ - OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls: 'Null', - /** - * Lookup695: pallet_template_transaction_payment::ChargeTransactionPayment - **/ - PalletTemplateTransactionPaymentChargeTransactionPayment: 'Compact', - /** - * Lookup696: opal_runtime::Runtime - **/ - OpalRuntimeRuntime: 'Null', - /** - * Lookup697: pallet_ethereum::FakeTransactionFinalizer - **/ - PalletEthereumFakeTransactionFinalizer: 'Null' -}; --- a/js-packages/types/src/povinfo/definitions.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -type RpcParam = { - name: string; - type: string; - isOptional?: true; -}; - -const atParam = {name: 'at', type: 'Hash', isOptional: true}; - -const fun = (description: string, params: RpcParam[], type: string) => ({ - description, - params: [...params, atParam], - type, -}); - -export default { - types: {}, - rpc: { - estimateExtrinsicPoV: fun( - 'Estimate PoV size of encoded signed extrinsics', - [{name: 'encodedXt', type: 'Vec'}], - 'UpPovEstimateRpcPovInfo', - ), - }, -}; --- a/js-packages/types/src/povinfo/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -export * from './types.js'; --- a/js-packages/types/src/povinfo/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -export type PHANTOM_POVINFO = 'povinfo'; --- a/js-packages/types/src/registry.ts +++ /dev/null @@ -1,370 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -// import type lookup before we augment - in some environments -// this is required to allow for ambient/previous definitions -import '@polkadot/types/types/registry'; - -import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemCodeUpgradeAuthorization, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity, CumulusPalletParachainSystemUnincludedSegmentAncestor, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate, CumulusPalletParachainSystemUnincludedSegmentSegmentTracker, CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OpalRuntimeRuntimeCommonSessionKeys, OpalRuntimeRuntimeHoldReason, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesIdAmount, PalletBalancesReasons, PalletBalancesReserveData, PalletCollatorSelectionCall, PalletCollatorSelectionError, PalletCollatorSelectionEvent, PalletCollatorSelectionHoldReason, PalletCollectiveCall, PalletCollectiveError, PalletCollectiveEvent, PalletCollectiveRawOrigin, PalletCollectiveVotes, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletConfigurationEvent, PalletDemocracyCall, PalletDemocracyConviction, PalletDemocracyDelegations, PalletDemocracyError, PalletDemocracyEvent, PalletDemocracyMetadataOwner, PalletDemocracyReferendumInfo, PalletDemocracyReferendumStatus, PalletDemocracyTally, PalletDemocracyVoteAccountVote, PalletDemocracyVotePriorLock, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCodeMetadata, PalletEvmCoderSubstrateError, PalletEvmContractHelpersCall, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletGovOriginsOrigin, PalletIdentityBitFlags, PalletIdentityCall, PalletIdentityError, PalletIdentityEvent, PalletIdentityIdentityField, PalletIdentityIdentityInfo, PalletIdentityJudgement, PalletIdentityRegistrarInfo, PalletIdentityRegistration, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletMembershipCall, PalletMembershipError, PalletMembershipEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletPreimageCall, PalletPreimageError, PalletPreimageEvent, PalletPreimageRequestStatus, PalletRankedCollectiveCall, PalletRankedCollectiveError, PalletRankedCollectiveEvent, PalletRankedCollectiveMemberRecord, PalletRankedCollectiveTally, PalletRankedCollectiveVoteRecord, PalletReferendaCall, PalletReferendaCurve, PalletReferendaDecidingStatus, PalletReferendaDeposit, PalletReferendaError, PalletReferendaEvent, PalletReferendaReferendumInfo, PalletReferendaReferendumStatus, PalletReferendaTrackInfo, PalletRefungibleError, PalletSchedulerCall, PalletSchedulerError, PalletSchedulerEvent, PalletSchedulerScheduled, PalletSessionCall, PalletSessionError, PalletSessionEvent, PalletStateTrieMigrationCall, PalletStateTrieMigrationError, PalletStateTrieMigrationEvent, PalletStateTrieMigrationMigrationCompute, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletStateTrieMigrationProgress, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUtilityCall, PalletUtilityError, PalletUtilityEvent, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PalletXcmQueryStatus, PalletXcmRemoteLockedFungibleRecord, PalletXcmVersionMigrationStage, ParachainInfoCall, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat, PolkadotPrimitivesV5AbridgedHostConfiguration, PolkadotPrimitivesV5AbridgedHrmpChannel, PolkadotPrimitivesV5PersistedValidationData, PolkadotPrimitivesV5UpgradeGoAhead, PolkadotPrimitivesV5UpgradeRestriction, PolkadotPrimitivesVstagingAsyncBackingParams, SpArithmeticArithmeticError, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Public, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionValidityInvalidTransaction, SpRuntimeTransactionValidityTransactionValidityError, SpRuntimeTransactionValidityUnknownTransaction, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, StagingXcmDoubleEncoded, StagingXcmV2BodyId, StagingXcmV2BodyPart, StagingXcmV2Instruction, StagingXcmV2Junction, StagingXcmV2MultiAsset, StagingXcmV2MultiLocation, StagingXcmV2MultiassetAssetId, StagingXcmV2MultiassetAssetInstance, StagingXcmV2MultiassetFungibility, StagingXcmV2MultiassetMultiAssetFilter, StagingXcmV2MultiassetMultiAssets, StagingXcmV2MultiassetWildFungibility, StagingXcmV2MultiassetWildMultiAsset, StagingXcmV2MultilocationJunctions, StagingXcmV2NetworkId, StagingXcmV2OriginKind, StagingXcmV2Response, StagingXcmV2TraitsError, StagingXcmV2WeightLimit, StagingXcmV2Xcm, StagingXcmV3Instruction, StagingXcmV3Junction, StagingXcmV3JunctionBodyId, StagingXcmV3JunctionBodyPart, StagingXcmV3JunctionNetworkId, StagingXcmV3Junctions, StagingXcmV3MaybeErrorCode, StagingXcmV3MultiAsset, StagingXcmV3MultiLocation, StagingXcmV3MultiassetAssetId, StagingXcmV3MultiassetAssetInstance, StagingXcmV3MultiassetFungibility, StagingXcmV3MultiassetMultiAssetFilter, StagingXcmV3MultiassetMultiAssets, StagingXcmV3MultiassetWildFungibility, StagingXcmV3MultiassetWildMultiAsset, StagingXcmV3PalletInfo, StagingXcmV3QueryResponseInfo, StagingXcmV3Response, StagingXcmV3TraitsError, StagingXcmV3TraitsOutcome, StagingXcmV3WeightLimit, StagingXcmV3Xcm, StagingXcmVersionedAssetId, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation, StagingXcmVersionedResponse, StagingXcmVersionedXcm, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, - UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo, UpPovEstimateRpcTrieKeyValue } from '@polkadot/types/lookup'; - -declare module '@polkadot/types/types/registry' { - interface InterfaceTypes { - CumulusPalletDmpQueueCall: CumulusPalletDmpQueueCall; - CumulusPalletDmpQueueConfigData: CumulusPalletDmpQueueConfigData; - CumulusPalletDmpQueueError: CumulusPalletDmpQueueError; - CumulusPalletDmpQueueEvent: CumulusPalletDmpQueueEvent; - CumulusPalletDmpQueuePageIndexData: CumulusPalletDmpQueuePageIndexData; - CumulusPalletParachainSystemCall: CumulusPalletParachainSystemCall; - CumulusPalletParachainSystemCodeUpgradeAuthorization: CumulusPalletParachainSystemCodeUpgradeAuthorization; - CumulusPalletParachainSystemError: CumulusPalletParachainSystemError; - CumulusPalletParachainSystemEvent: CumulusPalletParachainSystemEvent; - CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot; - CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity; - CumulusPalletParachainSystemUnincludedSegmentAncestor: CumulusPalletParachainSystemUnincludedSegmentAncestor; - CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate: CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate; - CumulusPalletParachainSystemUnincludedSegmentSegmentTracker: CumulusPalletParachainSystemUnincludedSegmentSegmentTracker; - CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth; - CumulusPalletXcmCall: CumulusPalletXcmCall; - CumulusPalletXcmError: CumulusPalletXcmError; - CumulusPalletXcmEvent: CumulusPalletXcmEvent; - CumulusPalletXcmOrigin: CumulusPalletXcmOrigin; - CumulusPalletXcmpQueueCall: CumulusPalletXcmpQueueCall; - CumulusPalletXcmpQueueError: CumulusPalletXcmpQueueError; - CumulusPalletXcmpQueueEvent: CumulusPalletXcmpQueueEvent; - CumulusPalletXcmpQueueInboundChannelDetails: CumulusPalletXcmpQueueInboundChannelDetails; - CumulusPalletXcmpQueueInboundState: CumulusPalletXcmpQueueInboundState; - CumulusPalletXcmpQueueOutboundChannelDetails: CumulusPalletXcmpQueueOutboundChannelDetails; - CumulusPalletXcmpQueueOutboundState: CumulusPalletXcmpQueueOutboundState; - CumulusPalletXcmpQueueQueueConfigData: CumulusPalletXcmpQueueQueueConfigData; - CumulusPrimitivesParachainInherentParachainInherentData: CumulusPrimitivesParachainInherentParachainInherentData; - EthbloomBloom: EthbloomBloom; - EthereumBlock: EthereumBlock; - EthereumHeader: EthereumHeader; - EthereumLog: EthereumLog; - EthereumReceiptEip658ReceiptData: EthereumReceiptEip658ReceiptData; - EthereumReceiptReceiptV3: EthereumReceiptReceiptV3; - EthereumTransactionAccessListItem: EthereumTransactionAccessListItem; - EthereumTransactionEip1559Transaction: EthereumTransactionEip1559Transaction; - EthereumTransactionEip2930Transaction: EthereumTransactionEip2930Transaction; - EthereumTransactionLegacyTransaction: EthereumTransactionLegacyTransaction; - EthereumTransactionTransactionAction: EthereumTransactionTransactionAction; - EthereumTransactionTransactionSignature: EthereumTransactionTransactionSignature; - EthereumTransactionTransactionV2: EthereumTransactionTransactionV2; - EthereumTypesHashH64: EthereumTypesHashH64; - EvmCoreErrorExitError: EvmCoreErrorExitError; - EvmCoreErrorExitFatal: EvmCoreErrorExitFatal; - EvmCoreErrorExitReason: EvmCoreErrorExitReason; - EvmCoreErrorExitRevert: EvmCoreErrorExitRevert; - EvmCoreErrorExitSucceed: EvmCoreErrorExitSucceed; - FpRpcTransactionStatus: FpRpcTransactionStatus; - FrameSupportDispatchDispatchClass: FrameSupportDispatchDispatchClass; - FrameSupportDispatchDispatchInfo: FrameSupportDispatchDispatchInfo; - FrameSupportDispatchPays: FrameSupportDispatchPays; - FrameSupportDispatchPerDispatchClassU32: FrameSupportDispatchPerDispatchClassU32; - FrameSupportDispatchPerDispatchClassWeight: FrameSupportDispatchPerDispatchClassWeight; - FrameSupportDispatchPerDispatchClassWeightsPerClass: FrameSupportDispatchPerDispatchClassWeightsPerClass; - FrameSupportDispatchRawOrigin: FrameSupportDispatchRawOrigin; - FrameSupportPalletId: FrameSupportPalletId; - FrameSupportPreimagesBounded: FrameSupportPreimagesBounded; - FrameSupportScheduleDispatchTime: FrameSupportScheduleDispatchTime; - FrameSupportTokensMiscBalanceStatus: FrameSupportTokensMiscBalanceStatus; - FrameSystemAccountInfo: FrameSystemAccountInfo; - FrameSystemCall: FrameSystemCall; - FrameSystemError: FrameSystemError; - FrameSystemEvent: FrameSystemEvent; - FrameSystemEventRecord: FrameSystemEventRecord; - FrameSystemExtensionsCheckGenesis: FrameSystemExtensionsCheckGenesis; - FrameSystemExtensionsCheckNonce: FrameSystemExtensionsCheckNonce; - FrameSystemExtensionsCheckSpecVersion: FrameSystemExtensionsCheckSpecVersion; - FrameSystemExtensionsCheckTxVersion: FrameSystemExtensionsCheckTxVersion; - FrameSystemExtensionsCheckWeight: FrameSystemExtensionsCheckWeight; - FrameSystemLastRuntimeUpgradeInfo: FrameSystemLastRuntimeUpgradeInfo; - FrameSystemLimitsBlockLength: FrameSystemLimitsBlockLength; - FrameSystemLimitsBlockWeights: FrameSystemLimitsBlockWeights; - FrameSystemLimitsWeightsPerClass: FrameSystemLimitsWeightsPerClass; - FrameSystemPhase: FrameSystemPhase; - OpalRuntimeOriginCaller: OpalRuntimeOriginCaller; - OpalRuntimeRuntime: OpalRuntimeRuntime; - OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls: OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls; - OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance; - OpalRuntimeRuntimeCommonSessionKeys: OpalRuntimeRuntimeCommonSessionKeys; - OpalRuntimeRuntimeHoldReason: OpalRuntimeRuntimeHoldReason; - OrmlTokensAccountData: OrmlTokensAccountData; - OrmlTokensBalanceLock: OrmlTokensBalanceLock; - OrmlTokensModuleCall: OrmlTokensModuleCall; - OrmlTokensModuleError: OrmlTokensModuleError; - OrmlTokensModuleEvent: OrmlTokensModuleEvent; - OrmlTokensReserveData: OrmlTokensReserveData; - OrmlVestingModuleCall: OrmlVestingModuleCall; - OrmlVestingModuleError: OrmlVestingModuleError; - OrmlVestingModuleEvent: OrmlVestingModuleEvent; - OrmlVestingVestingSchedule: OrmlVestingVestingSchedule; - OrmlXtokensModuleCall: OrmlXtokensModuleCall; - OrmlXtokensModuleError: OrmlXtokensModuleError; - OrmlXtokensModuleEvent: OrmlXtokensModuleEvent; - PalletAppPromotionCall: PalletAppPromotionCall; - PalletAppPromotionError: PalletAppPromotionError; - PalletAppPromotionEvent: PalletAppPromotionEvent; - PalletBalancesAccountData: PalletBalancesAccountData; - PalletBalancesBalanceLock: PalletBalancesBalanceLock; - PalletBalancesCall: PalletBalancesCall; - PalletBalancesError: PalletBalancesError; - PalletBalancesEvent: PalletBalancesEvent; - PalletBalancesIdAmount: PalletBalancesIdAmount; - PalletBalancesReasons: PalletBalancesReasons; - PalletBalancesReserveData: PalletBalancesReserveData; - PalletCollatorSelectionCall: PalletCollatorSelectionCall; - PalletCollatorSelectionError: PalletCollatorSelectionError; - PalletCollatorSelectionEvent: PalletCollatorSelectionEvent; - PalletCollatorSelectionHoldReason: PalletCollatorSelectionHoldReason; - PalletCollectiveCall: PalletCollectiveCall; - PalletCollectiveError: PalletCollectiveError; - PalletCollectiveEvent: PalletCollectiveEvent; - PalletCollectiveRawOrigin: PalletCollectiveRawOrigin; - PalletCollectiveVotes: PalletCollectiveVotes; - PalletCommonError: PalletCommonError; - PalletCommonEvent: PalletCommonEvent; - PalletConfigurationAppPromotionConfiguration: PalletConfigurationAppPromotionConfiguration; - PalletConfigurationCall: PalletConfigurationCall; - PalletConfigurationError: PalletConfigurationError; - PalletConfigurationEvent: PalletConfigurationEvent; - PalletDemocracyCall: PalletDemocracyCall; - PalletDemocracyConviction: PalletDemocracyConviction; - PalletDemocracyDelegations: PalletDemocracyDelegations; - PalletDemocracyError: PalletDemocracyError; - PalletDemocracyEvent: PalletDemocracyEvent; - PalletDemocracyMetadataOwner: PalletDemocracyMetadataOwner; - PalletDemocracyReferendumInfo: PalletDemocracyReferendumInfo; - PalletDemocracyReferendumStatus: PalletDemocracyReferendumStatus; - PalletDemocracyTally: PalletDemocracyTally; - PalletDemocracyVoteAccountVote: PalletDemocracyVoteAccountVote; - PalletDemocracyVotePriorLock: PalletDemocracyVotePriorLock; - PalletDemocracyVoteThreshold: PalletDemocracyVoteThreshold; - PalletDemocracyVoteVoting: PalletDemocracyVoteVoting; - PalletEthereumCall: PalletEthereumCall; - PalletEthereumError: PalletEthereumError; - PalletEthereumEvent: PalletEthereumEvent; - PalletEthereumFakeTransactionFinalizer: PalletEthereumFakeTransactionFinalizer; - PalletEthereumRawOrigin: PalletEthereumRawOrigin; - PalletEvmAccountBasicCrossAccountIdRepr: PalletEvmAccountBasicCrossAccountIdRepr; - PalletEvmCall: PalletEvmCall; - PalletEvmCodeMetadata: PalletEvmCodeMetadata; - PalletEvmCoderSubstrateError: PalletEvmCoderSubstrateError; - PalletEvmContractHelpersCall: PalletEvmContractHelpersCall; - PalletEvmContractHelpersError: PalletEvmContractHelpersError; - PalletEvmContractHelpersEvent: PalletEvmContractHelpersEvent; - PalletEvmContractHelpersSponsoringModeT: PalletEvmContractHelpersSponsoringModeT; - PalletEvmError: PalletEvmError; - PalletEvmEvent: PalletEvmEvent; - PalletEvmMigrationCall: PalletEvmMigrationCall; - PalletEvmMigrationError: PalletEvmMigrationError; - PalletEvmMigrationEvent: PalletEvmMigrationEvent; - PalletForeignAssetsAssetId: PalletForeignAssetsAssetId; - PalletForeignAssetsModuleAssetMetadata: PalletForeignAssetsModuleAssetMetadata; - PalletForeignAssetsModuleCall: PalletForeignAssetsModuleCall; - PalletForeignAssetsModuleError: PalletForeignAssetsModuleError; - PalletForeignAssetsModuleEvent: PalletForeignAssetsModuleEvent; - PalletForeignAssetsNativeCurrency: PalletForeignAssetsNativeCurrency; - PalletFungibleError: PalletFungibleError; - PalletGovOriginsOrigin: PalletGovOriginsOrigin; - PalletIdentityBitFlags: PalletIdentityBitFlags; - PalletIdentityCall: PalletIdentityCall; - PalletIdentityError: PalletIdentityError; - PalletIdentityEvent: PalletIdentityEvent; - PalletIdentityIdentityField: PalletIdentityIdentityField; - PalletIdentityIdentityInfo: PalletIdentityIdentityInfo; - PalletIdentityJudgement: PalletIdentityJudgement; - PalletIdentityRegistrarInfo: PalletIdentityRegistrarInfo; - PalletIdentityRegistration: PalletIdentityRegistration; - PalletInflationCall: PalletInflationCall; - PalletMaintenanceCall: PalletMaintenanceCall; - PalletMaintenanceError: PalletMaintenanceError; - PalletMaintenanceEvent: PalletMaintenanceEvent; - PalletMembershipCall: PalletMembershipCall; - PalletMembershipError: PalletMembershipError; - PalletMembershipEvent: PalletMembershipEvent; - PalletNonfungibleError: PalletNonfungibleError; - PalletNonfungibleItemData: PalletNonfungibleItemData; - PalletPreimageCall: PalletPreimageCall; - PalletPreimageError: PalletPreimageError; - PalletPreimageEvent: PalletPreimageEvent; - PalletPreimageRequestStatus: PalletPreimageRequestStatus; - PalletRankedCollectiveCall: PalletRankedCollectiveCall; - PalletRankedCollectiveError: PalletRankedCollectiveError; - PalletRankedCollectiveEvent: PalletRankedCollectiveEvent; - PalletRankedCollectiveMemberRecord: PalletRankedCollectiveMemberRecord; - PalletRankedCollectiveTally: PalletRankedCollectiveTally; - PalletRankedCollectiveVoteRecord: PalletRankedCollectiveVoteRecord; - PalletReferendaCall: PalletReferendaCall; - PalletReferendaCurve: PalletReferendaCurve; - PalletReferendaDecidingStatus: PalletReferendaDecidingStatus; - PalletReferendaDeposit: PalletReferendaDeposit; - PalletReferendaError: PalletReferendaError; - PalletReferendaEvent: PalletReferendaEvent; - PalletReferendaReferendumInfo: PalletReferendaReferendumInfo; - PalletReferendaReferendumStatus: PalletReferendaReferendumStatus; - PalletReferendaTrackInfo: PalletReferendaTrackInfo; - PalletRefungibleError: PalletRefungibleError; - PalletSchedulerCall: PalletSchedulerCall; - PalletSchedulerError: PalletSchedulerError; - PalletSchedulerEvent: PalletSchedulerEvent; - PalletSchedulerScheduled: PalletSchedulerScheduled; - PalletSessionCall: PalletSessionCall; - PalletSessionError: PalletSessionError; - PalletSessionEvent: PalletSessionEvent; - PalletStateTrieMigrationCall: PalletStateTrieMigrationCall; - PalletStateTrieMigrationError: PalletStateTrieMigrationError; - PalletStateTrieMigrationEvent: PalletStateTrieMigrationEvent; - PalletStateTrieMigrationMigrationCompute: PalletStateTrieMigrationMigrationCompute; - PalletStateTrieMigrationMigrationLimits: PalletStateTrieMigrationMigrationLimits; - PalletStateTrieMigrationMigrationTask: PalletStateTrieMigrationMigrationTask; - PalletStateTrieMigrationProgress: PalletStateTrieMigrationProgress; - PalletStructureCall: PalletStructureCall; - PalletStructureError: PalletStructureError; - PalletStructureEvent: PalletStructureEvent; - PalletSudoCall: PalletSudoCall; - PalletSudoError: PalletSudoError; - PalletSudoEvent: PalletSudoEvent; - PalletTemplateTransactionPaymentChargeTransactionPayment: PalletTemplateTransactionPaymentChargeTransactionPayment; - PalletTestUtilsCall: PalletTestUtilsCall; - PalletTestUtilsError: PalletTestUtilsError; - PalletTestUtilsEvent: PalletTestUtilsEvent; - PalletTimestampCall: PalletTimestampCall; - PalletTransactionPaymentEvent: PalletTransactionPaymentEvent; - PalletTransactionPaymentReleases: PalletTransactionPaymentReleases; - PalletTreasuryCall: PalletTreasuryCall; - PalletTreasuryError: PalletTreasuryError; - PalletTreasuryEvent: PalletTreasuryEvent; - PalletTreasuryProposal: PalletTreasuryProposal; - PalletUniqueCall: PalletUniqueCall; - PalletUniqueError: PalletUniqueError; - PalletUtilityCall: PalletUtilityCall; - PalletUtilityError: PalletUtilityError; - PalletUtilityEvent: PalletUtilityEvent; - PalletXcmCall: PalletXcmCall; - PalletXcmError: PalletXcmError; - PalletXcmEvent: PalletXcmEvent; - PalletXcmOrigin: PalletXcmOrigin; - PalletXcmQueryStatus: PalletXcmQueryStatus; - PalletXcmRemoteLockedFungibleRecord: PalletXcmRemoteLockedFungibleRecord; - PalletXcmVersionMigrationStage: PalletXcmVersionMigrationStage; - ParachainInfoCall: ParachainInfoCall; - PhantomTypeUpDataStructs: PhantomTypeUpDataStructs; - PolkadotCorePrimitivesInboundDownwardMessage: PolkadotCorePrimitivesInboundDownwardMessage; - PolkadotCorePrimitivesInboundHrmpMessage: PolkadotCorePrimitivesInboundHrmpMessage; - PolkadotCorePrimitivesOutboundHrmpMessage: PolkadotCorePrimitivesOutboundHrmpMessage; - PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat: PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat; - PolkadotPrimitivesV5AbridgedHostConfiguration: PolkadotPrimitivesV5AbridgedHostConfiguration; - PolkadotPrimitivesV5AbridgedHrmpChannel: PolkadotPrimitivesV5AbridgedHrmpChannel; - PolkadotPrimitivesV5PersistedValidationData: PolkadotPrimitivesV5PersistedValidationData; - PolkadotPrimitivesV5UpgradeGoAhead: PolkadotPrimitivesV5UpgradeGoAhead; - PolkadotPrimitivesV5UpgradeRestriction: PolkadotPrimitivesV5UpgradeRestriction; - PolkadotPrimitivesVstagingAsyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams; - SpArithmeticArithmeticError: SpArithmeticArithmeticError; - SpConsensusAuraSr25519AppSr25519Public: SpConsensusAuraSr25519AppSr25519Public; - SpCoreCryptoKeyTypeId: SpCoreCryptoKeyTypeId; - SpCoreEcdsaSignature: SpCoreEcdsaSignature; - SpCoreEd25519Signature: SpCoreEd25519Signature; - SpCoreSr25519Public: SpCoreSr25519Public; - SpCoreSr25519Signature: SpCoreSr25519Signature; - SpCoreVoid: SpCoreVoid; - SpRuntimeDigest: SpRuntimeDigest; - SpRuntimeDigestDigestItem: SpRuntimeDigestDigestItem; - SpRuntimeDispatchError: SpRuntimeDispatchError; - SpRuntimeModuleError: SpRuntimeModuleError; - SpRuntimeMultiSignature: SpRuntimeMultiSignature; - SpRuntimeTokenError: SpRuntimeTokenError; - SpRuntimeTransactionValidityInvalidTransaction: SpRuntimeTransactionValidityInvalidTransaction; - SpRuntimeTransactionValidityTransactionValidityError: SpRuntimeTransactionValidityTransactionValidityError; - SpRuntimeTransactionValidityUnknownTransaction: SpRuntimeTransactionValidityUnknownTransaction; - SpRuntimeTransactionalError: SpRuntimeTransactionalError; - SpTrieStorageProof: SpTrieStorageProof; - SpVersionRuntimeVersion: SpVersionRuntimeVersion; - SpWeightsRuntimeDbWeight: SpWeightsRuntimeDbWeight; - SpWeightsWeightV2Weight: SpWeightsWeightV2Weight; - StagingXcmDoubleEncoded: StagingXcmDoubleEncoded; - StagingXcmV2BodyId: StagingXcmV2BodyId; - StagingXcmV2BodyPart: StagingXcmV2BodyPart; - StagingXcmV2Instruction: StagingXcmV2Instruction; - StagingXcmV2Junction: StagingXcmV2Junction; - StagingXcmV2MultiAsset: StagingXcmV2MultiAsset; - StagingXcmV2MultiLocation: StagingXcmV2MultiLocation; - StagingXcmV2MultiassetAssetId: StagingXcmV2MultiassetAssetId; - StagingXcmV2MultiassetAssetInstance: StagingXcmV2MultiassetAssetInstance; - StagingXcmV2MultiassetFungibility: StagingXcmV2MultiassetFungibility; - StagingXcmV2MultiassetMultiAssetFilter: StagingXcmV2MultiassetMultiAssetFilter; - StagingXcmV2MultiassetMultiAssets: StagingXcmV2MultiassetMultiAssets; - StagingXcmV2MultiassetWildFungibility: StagingXcmV2MultiassetWildFungibility; - StagingXcmV2MultiassetWildMultiAsset: StagingXcmV2MultiassetWildMultiAsset; - StagingXcmV2MultilocationJunctions: StagingXcmV2MultilocationJunctions; - StagingXcmV2NetworkId: StagingXcmV2NetworkId; - StagingXcmV2OriginKind: StagingXcmV2OriginKind; - StagingXcmV2Response: StagingXcmV2Response; - StagingXcmV2TraitsError: StagingXcmV2TraitsError; - StagingXcmV2WeightLimit: StagingXcmV2WeightLimit; - StagingXcmV2Xcm: StagingXcmV2Xcm; - StagingXcmV3Instruction: StagingXcmV3Instruction; - StagingXcmV3Junction: StagingXcmV3Junction; - StagingXcmV3JunctionBodyId: StagingXcmV3JunctionBodyId; - StagingXcmV3JunctionBodyPart: StagingXcmV3JunctionBodyPart; - StagingXcmV3JunctionNetworkId: StagingXcmV3JunctionNetworkId; - StagingXcmV3Junctions: StagingXcmV3Junctions; - StagingXcmV3MaybeErrorCode: StagingXcmV3MaybeErrorCode; - StagingXcmV3MultiAsset: StagingXcmV3MultiAsset; - StagingXcmV3MultiLocation: StagingXcmV3MultiLocation; - StagingXcmV3MultiassetAssetId: StagingXcmV3MultiassetAssetId; - StagingXcmV3MultiassetAssetInstance: StagingXcmV3MultiassetAssetInstance; - StagingXcmV3MultiassetFungibility: StagingXcmV3MultiassetFungibility; - StagingXcmV3MultiassetMultiAssetFilter: StagingXcmV3MultiassetMultiAssetFilter; - StagingXcmV3MultiassetMultiAssets: StagingXcmV3MultiassetMultiAssets; - StagingXcmV3MultiassetWildFungibility: StagingXcmV3MultiassetWildFungibility; - StagingXcmV3MultiassetWildMultiAsset: StagingXcmV3MultiassetWildMultiAsset; - StagingXcmV3PalletInfo: StagingXcmV3PalletInfo; - StagingXcmV3QueryResponseInfo: StagingXcmV3QueryResponseInfo; - StagingXcmV3Response: StagingXcmV3Response; - StagingXcmV3TraitsError: StagingXcmV3TraitsError; - StagingXcmV3TraitsOutcome: StagingXcmV3TraitsOutcome; - StagingXcmV3WeightLimit: StagingXcmV3WeightLimit; - StagingXcmV3Xcm: StagingXcmV3Xcm; - StagingXcmVersionedAssetId: StagingXcmVersionedAssetId; - StagingXcmVersionedMultiAsset: StagingXcmVersionedMultiAsset; - StagingXcmVersionedMultiAssets: StagingXcmVersionedMultiAssets; - StagingXcmVersionedMultiLocation: StagingXcmVersionedMultiLocation; - StagingXcmVersionedResponse: StagingXcmVersionedResponse; - StagingXcmVersionedXcm: StagingXcmVersionedXcm; - UpDataStructsAccessMode: UpDataStructsAccessMode; - UpDataStructsCollection: UpDataStructsCollection; - UpDataStructsCollectionLimits: UpDataStructsCollectionLimits; - UpDataStructsCollectionMode: UpDataStructsCollectionMode; - UpDataStructsCollectionPermissions: UpDataStructsCollectionPermissions; - UpDataStructsCollectionStats: UpDataStructsCollectionStats; - UpDataStructsCreateCollectionData: UpDataStructsCreateCollectionData; - UpDataStructsCreateFungibleData: UpDataStructsCreateFungibleData; - UpDataStructsCreateItemData: UpDataStructsCreateItemData; - UpDataStructsCreateItemExData: UpDataStructsCreateItemExData; - UpDataStructsCreateNftData: UpDataStructsCreateNftData; - UpDataStructsCreateNftExData: UpDataStructsCreateNftExData; - UpDataStructsCreateReFungibleData: UpDataStructsCreateReFungibleData; - UpDataStructsCreateRefungibleExMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners; - UpDataStructsCreateRefungibleExSingleOwner: UpDataStructsCreateRefungibleExSingleOwner; - UpDataStructsNestingPermissions: UpDataStructsNestingPermissions; - UpDataStructsOwnerRestrictedSet: UpDataStructsOwnerRestrictedSet; - UpDataStructsProperties: UpDataStructsProperties; - UpDataStructsPropertiesMapBoundedVec: UpDataStructsPropertiesMapBoundedVec; - UpDataStructsPropertiesMapPropertyPermission: UpDataStructsPropertiesMapPropertyPermission; - UpDataStructsProperty: UpDataStructsProperty; - UpDataStructsPropertyKeyPermission: UpDataStructsPropertyKeyPermission; - UpDataStructsPropertyPermission: UpDataStructsPropertyPermission; - UpDataStructsPropertyScope: UpDataStructsPropertyScope; - UpDataStructsRpcCollection: UpDataStructsRpcCollection; - UpDataStructsRpcCollectionFlags: UpDataStructsRpcCollectionFlags; - UpDataStructsSponsoringRateLimit: UpDataStructsSponsoringRateLimit; - UpDataStructsSponsorshipStateAccountId32: UpDataStructsSponsorshipStateAccountId32; - UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: UpDataStructsSponsorshipStateBasicCrossAccountIdRepr; - UpDataStructsTokenChild: UpDataStructsTokenChild; - UpDataStructsTokenData: UpDataStructsTokenData; - UpPovEstimateRpcPovInfo: UpPovEstimateRpcPovInfo; - UpPovEstimateRpcTrieKeyValue: UpPovEstimateRpcTrieKeyValue; - } // InterfaceTypes -} // declare module --- a/js-packages/types/src/types-lookup.ts +++ /dev/null @@ -1,5540 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -// import type lookup before we augment - in some environments -// this is required to allow for ambient/previous definitions -import '@polkadot/types/lookup'; - -import type { Data } from '@polkadot/types'; -import type { BTreeMap, BTreeSet, Bytes, Compact, Enum, Null, Option, Result, Set, Struct, Text, U256, U8aFixed, Vec, bool, i64, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; -import type { ITuple } from '@polkadot/types-codec/types'; -import type { Vote } from '@polkadot/types/interfaces/elections'; -import type { AccountId32, Call, H160, H256, MultiAddress, Perbill } from '@polkadot/types/interfaces/runtime'; -import type { Event } from '@polkadot/types/interfaces/system'; - -declare module '@polkadot/types/lookup' { - /** @name FrameSystemAccountInfo (3) */ - interface FrameSystemAccountInfo extends Struct { - readonly nonce: u32; - readonly consumers: u32; - readonly providers: u32; - readonly sufficients: u32; - readonly data: PalletBalancesAccountData; - } - - /** @name PalletBalancesAccountData (5) */ - interface PalletBalancesAccountData extends Struct { - readonly free: u128; - readonly reserved: u128; - readonly frozen: u128; - readonly flags: u128; - } - - /** @name FrameSupportDispatchPerDispatchClassWeight (8) */ - interface FrameSupportDispatchPerDispatchClassWeight extends Struct { - readonly normal: SpWeightsWeightV2Weight; - readonly operational: SpWeightsWeightV2Weight; - readonly mandatory: SpWeightsWeightV2Weight; - } - - /** @name SpWeightsWeightV2Weight (9) */ - interface SpWeightsWeightV2Weight extends Struct { - readonly refTime: Compact; - readonly proofSize: Compact; - } - - /** @name SpRuntimeDigest (14) */ - interface SpRuntimeDigest extends Struct { - readonly logs: Vec; - } - - /** @name SpRuntimeDigestDigestItem (16) */ - interface SpRuntimeDigestDigestItem extends Enum { - readonly isOther: boolean; - readonly asOther: Bytes; - readonly isConsensus: boolean; - readonly asConsensus: ITuple<[U8aFixed, Bytes]>; - readonly isSeal: boolean; - readonly asSeal: ITuple<[U8aFixed, Bytes]>; - readonly isPreRuntime: boolean; - readonly asPreRuntime: ITuple<[U8aFixed, Bytes]>; - readonly isRuntimeEnvironmentUpdated: boolean; - readonly type: 'Other' | 'Consensus' | 'Seal' | 'PreRuntime' | 'RuntimeEnvironmentUpdated'; - } - - /** @name FrameSystemEventRecord (19) */ - interface FrameSystemEventRecord extends Struct { - readonly phase: FrameSystemPhase; - readonly event: Event; - readonly topics: Vec; - } - - /** @name FrameSystemEvent (21) */ - interface FrameSystemEvent extends Enum { - readonly isExtrinsicSuccess: boolean; - readonly asExtrinsicSuccess: { - readonly dispatchInfo: FrameSupportDispatchDispatchInfo; - } & Struct; - readonly isExtrinsicFailed: boolean; - readonly asExtrinsicFailed: { - readonly dispatchError: SpRuntimeDispatchError; - readonly dispatchInfo: FrameSupportDispatchDispatchInfo; - } & Struct; - readonly isCodeUpdated: boolean; - readonly isNewAccount: boolean; - readonly asNewAccount: { - readonly account: AccountId32; - } & Struct; - readonly isKilledAccount: boolean; - readonly asKilledAccount: { - readonly account: AccountId32; - } & Struct; - readonly isRemarked: boolean; - readonly asRemarked: { - readonly sender: AccountId32; - readonly hash_: H256; - } & Struct; - readonly type: 'ExtrinsicSuccess' | 'ExtrinsicFailed' | 'CodeUpdated' | 'NewAccount' | 'KilledAccount' | 'Remarked'; - } - - /** @name FrameSupportDispatchDispatchInfo (22) */ - interface FrameSupportDispatchDispatchInfo extends Struct { - readonly weight: SpWeightsWeightV2Weight; - readonly class: FrameSupportDispatchDispatchClass; - readonly paysFee: FrameSupportDispatchPays; - } - - /** @name FrameSupportDispatchDispatchClass (23) */ - interface FrameSupportDispatchDispatchClass extends Enum { - readonly isNormal: boolean; - readonly isOperational: boolean; - readonly isMandatory: boolean; - readonly type: 'Normal' | 'Operational' | 'Mandatory'; - } - - /** @name FrameSupportDispatchPays (24) */ - interface FrameSupportDispatchPays extends Enum { - readonly isYes: boolean; - readonly isNo: boolean; - readonly type: 'Yes' | 'No'; - } - - /** @name SpRuntimeDispatchError (25) */ - interface SpRuntimeDispatchError extends Enum { - readonly isOther: boolean; - readonly isCannotLookup: boolean; - readonly isBadOrigin: boolean; - readonly isModule: boolean; - readonly asModule: SpRuntimeModuleError; - readonly isConsumerRemaining: boolean; - readonly isNoProviders: boolean; - readonly isTooManyConsumers: boolean; - readonly isToken: boolean; - readonly asToken: SpRuntimeTokenError; - readonly isArithmetic: boolean; - readonly asArithmetic: SpArithmeticArithmeticError; - readonly isTransactional: boolean; - readonly asTransactional: SpRuntimeTransactionalError; - readonly isExhausted: boolean; - readonly isCorruption: boolean; - readonly isUnavailable: boolean; - readonly isRootNotAllowed: boolean; - readonly type: 'Other' | 'CannotLookup' | 'BadOrigin' | 'Module' | 'ConsumerRemaining' | 'NoProviders' | 'TooManyConsumers' | 'Token' | 'Arithmetic' | 'Transactional' | 'Exhausted' | 'Corruption' | 'Unavailable' | 'RootNotAllowed'; - } - - /** @name SpRuntimeModuleError (26) */ - interface SpRuntimeModuleError extends Struct { - readonly index: u8; - readonly error: U8aFixed; - } - - /** @name SpRuntimeTokenError (27) */ - interface SpRuntimeTokenError extends Enum { - readonly isFundsUnavailable: boolean; - readonly isOnlyProvider: boolean; - readonly isBelowMinimum: boolean; - readonly isCannotCreate: boolean; - readonly isUnknownAsset: boolean; - readonly isFrozen: boolean; - readonly isUnsupported: boolean; - readonly isCannotCreateHold: boolean; - readonly isNotExpendable: boolean; - readonly isBlocked: boolean; - readonly type: 'FundsUnavailable' | 'OnlyProvider' | 'BelowMinimum' | 'CannotCreate' | 'UnknownAsset' | 'Frozen' | 'Unsupported' | 'CannotCreateHold' | 'NotExpendable' | 'Blocked'; - } - - /** @name SpArithmeticArithmeticError (28) */ - interface SpArithmeticArithmeticError extends Enum { - readonly isUnderflow: boolean; - readonly isOverflow: boolean; - readonly isDivisionByZero: boolean; - readonly type: 'Underflow' | 'Overflow' | 'DivisionByZero'; - } - - /** @name SpRuntimeTransactionalError (29) */ - interface SpRuntimeTransactionalError extends Enum { - readonly isLimitReached: boolean; - readonly isNoLayer: boolean; - readonly type: 'LimitReached' | 'NoLayer'; - } - - /** @name PalletStateTrieMigrationEvent (30) */ - interface PalletStateTrieMigrationEvent extends Enum { - readonly isMigrated: boolean; - readonly asMigrated: { - readonly top: u32; - readonly child: u32; - readonly compute: PalletStateTrieMigrationMigrationCompute; - } & Struct; - readonly isSlashed: boolean; - readonly asSlashed: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isAutoMigrationFinished: boolean; - readonly isHalted: boolean; - readonly asHalted: { - readonly error: PalletStateTrieMigrationError; - } & Struct; - readonly type: 'Migrated' | 'Slashed' | 'AutoMigrationFinished' | 'Halted'; - } - - /** @name PalletStateTrieMigrationMigrationCompute (31) */ - interface PalletStateTrieMigrationMigrationCompute extends Enum { - readonly isSigned: boolean; - readonly isAuto: boolean; - readonly type: 'Signed' | 'Auto'; - } - - /** @name PalletStateTrieMigrationError (32) */ - interface PalletStateTrieMigrationError extends Enum { - readonly isMaxSignedLimits: boolean; - readonly isKeyTooLong: boolean; - readonly isNotEnoughFunds: boolean; - readonly isBadWitness: boolean; - readonly isSignedMigrationNotAllowed: boolean; - readonly isBadChildRoot: boolean; - readonly type: 'MaxSignedLimits' | 'KeyTooLong' | 'NotEnoughFunds' | 'BadWitness' | 'SignedMigrationNotAllowed' | 'BadChildRoot'; - } - - /** @name CumulusPalletParachainSystemEvent (33) */ - interface CumulusPalletParachainSystemEvent extends Enum { - readonly isValidationFunctionStored: boolean; - readonly isValidationFunctionApplied: boolean; - readonly asValidationFunctionApplied: { - readonly relayChainBlockNum: u32; - } & Struct; - readonly isValidationFunctionDiscarded: boolean; - readonly isUpgradeAuthorized: boolean; - readonly asUpgradeAuthorized: { - readonly codeHash: H256; - } & Struct; - readonly isDownwardMessagesReceived: boolean; - readonly asDownwardMessagesReceived: { - readonly count: u32; - } & Struct; - readonly isDownwardMessagesProcessed: boolean; - readonly asDownwardMessagesProcessed: { - readonly weightUsed: SpWeightsWeightV2Weight; - readonly dmqHead: H256; - } & Struct; - readonly isUpwardMessageSent: boolean; - readonly asUpwardMessageSent: { - readonly messageHash: Option; - } & Struct; - readonly type: 'ValidationFunctionStored' | 'ValidationFunctionApplied' | 'ValidationFunctionDiscarded' | 'UpgradeAuthorized' | 'DownwardMessagesReceived' | 'DownwardMessagesProcessed' | 'UpwardMessageSent'; - } - - /** @name PalletCollatorSelectionEvent (35) */ - interface PalletCollatorSelectionEvent extends Enum { - readonly isInvulnerableAdded: boolean; - readonly asInvulnerableAdded: { - readonly invulnerable: AccountId32; - } & Struct; - readonly isInvulnerableRemoved: boolean; - readonly asInvulnerableRemoved: { - readonly invulnerable: AccountId32; - } & Struct; - readonly isLicenseObtained: boolean; - readonly asLicenseObtained: { - readonly accountId: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isLicenseReleased: boolean; - readonly asLicenseReleased: { - readonly accountId: AccountId32; - readonly depositReturned: u128; - } & Struct; - readonly isCandidateAdded: boolean; - readonly asCandidateAdded: { - readonly accountId: AccountId32; - } & Struct; - readonly isCandidateRemoved: boolean; - readonly asCandidateRemoved: { - readonly accountId: AccountId32; - } & Struct; - readonly type: 'InvulnerableAdded' | 'InvulnerableRemoved' | 'LicenseObtained' | 'LicenseReleased' | 'CandidateAdded' | 'CandidateRemoved'; - } - - /** @name PalletSessionEvent (36) */ - interface PalletSessionEvent extends Enum { - readonly isNewSession: boolean; - readonly asNewSession: { - readonly sessionIndex: u32; - } & Struct; - readonly type: 'NewSession'; - } - - /** @name PalletBalancesEvent (37) */ - interface PalletBalancesEvent extends Enum { - readonly isEndowed: boolean; - readonly asEndowed: { - readonly account: AccountId32; - readonly freeBalance: u128; - } & Struct; - readonly isDustLost: boolean; - readonly asDustLost: { - readonly account: AccountId32; - readonly amount: u128; - } & Struct; - readonly isTransfer: boolean; - readonly asTransfer: { - readonly from: AccountId32; - readonly to: AccountId32; - readonly amount: u128; - } & Struct; - readonly isBalanceSet: boolean; - readonly asBalanceSet: { - readonly who: AccountId32; - readonly free: u128; - } & Struct; - readonly isReserved: boolean; - readonly asReserved: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isUnreserved: boolean; - readonly asUnreserved: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isReserveRepatriated: boolean; - readonly asReserveRepatriated: { - readonly from: AccountId32; - readonly to: AccountId32; - readonly amount: u128; - readonly destinationStatus: FrameSupportTokensMiscBalanceStatus; - } & Struct; - readonly isDeposit: boolean; - readonly asDeposit: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isWithdraw: boolean; - readonly asWithdraw: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isSlashed: boolean; - readonly asSlashed: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isMinted: boolean; - readonly asMinted: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isBurned: boolean; - readonly asBurned: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isSuspended: boolean; - readonly asSuspended: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isRestored: boolean; - readonly asRestored: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isUpgraded: boolean; - readonly asUpgraded: { - readonly who: AccountId32; - } & Struct; - readonly isIssued: boolean; - readonly asIssued: { - readonly amount: u128; - } & Struct; - readonly isRescinded: boolean; - readonly asRescinded: { - readonly amount: u128; - } & Struct; - readonly isLocked: boolean; - readonly asLocked: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isUnlocked: boolean; - readonly asUnlocked: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isFrozen: boolean; - readonly asFrozen: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isThawed: boolean; - readonly asThawed: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'BalanceSet' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'Deposit' | 'Withdraw' | 'Slashed' | 'Minted' | 'Burned' | 'Suspended' | 'Restored' | 'Upgraded' | 'Issued' | 'Rescinded' | 'Locked' | 'Unlocked' | 'Frozen' | 'Thawed'; - } - - /** @name FrameSupportTokensMiscBalanceStatus (38) */ - interface FrameSupportTokensMiscBalanceStatus extends Enum { - readonly isFree: boolean; - readonly isReserved: boolean; - readonly type: 'Free' | 'Reserved'; - } - - /** @name PalletTransactionPaymentEvent (39) */ - interface PalletTransactionPaymentEvent extends Enum { - readonly isTransactionFeePaid: boolean; - readonly asTransactionFeePaid: { - readonly who: AccountId32; - readonly actualFee: u128; - readonly tip: u128; - } & Struct; - readonly type: 'TransactionFeePaid'; - } - - /** @name PalletTreasuryEvent (40) */ - interface PalletTreasuryEvent extends Enum { - readonly isProposed: boolean; - readonly asProposed: { - readonly proposalIndex: u32; - } & Struct; - readonly isSpending: boolean; - readonly asSpending: { - readonly budgetRemaining: u128; - } & Struct; - readonly isAwarded: boolean; - readonly asAwarded: { - readonly proposalIndex: u32; - readonly award: u128; - readonly account: AccountId32; - } & Struct; - readonly isRejected: boolean; - readonly asRejected: { - readonly proposalIndex: u32; - readonly slashed: u128; - } & Struct; - readonly isBurnt: boolean; - readonly asBurnt: { - readonly burntFunds: u128; - } & Struct; - readonly isRollover: boolean; - readonly asRollover: { - readonly rolloverBalance: u128; - } & Struct; - readonly isDeposit: boolean; - readonly asDeposit: { - readonly value: u128; - } & Struct; - readonly isSpendApproved: boolean; - readonly asSpendApproved: { - readonly proposalIndex: u32; - readonly amount: u128; - readonly beneficiary: AccountId32; - } & Struct; - readonly isUpdatedInactive: boolean; - readonly asUpdatedInactive: { - readonly reactivated: u128; - readonly deactivated: u128; - } & Struct; - readonly type: 'Proposed' | 'Spending' | 'Awarded' | 'Rejected' | 'Burnt' | 'Rollover' | 'Deposit' | 'SpendApproved' | 'UpdatedInactive'; - } - - /** @name PalletSudoEvent (41) */ - interface PalletSudoEvent extends Enum { - readonly isSudid: boolean; - readonly asSudid: { - readonly sudoResult: Result; - } & Struct; - readonly isKeyChanged: boolean; - readonly asKeyChanged: { - readonly oldSudoer: Option; - } & Struct; - readonly isSudoAsDone: boolean; - readonly asSudoAsDone: { - readonly sudoResult: Result; - } & Struct; - readonly type: 'Sudid' | 'KeyChanged' | 'SudoAsDone'; - } - - /** @name OrmlVestingModuleEvent (45) */ - interface OrmlVestingModuleEvent extends Enum { - readonly isVestingScheduleAdded: boolean; - readonly asVestingScheduleAdded: { - readonly from: AccountId32; - readonly to: AccountId32; - readonly vestingSchedule: OrmlVestingVestingSchedule; - } & Struct; - readonly isClaimed: boolean; - readonly asClaimed: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isVestingSchedulesUpdated: boolean; - readonly asVestingSchedulesUpdated: { - readonly who: AccountId32; - } & Struct; - readonly type: 'VestingScheduleAdded' | 'Claimed' | 'VestingSchedulesUpdated'; - } - - /** @name OrmlVestingVestingSchedule (46) */ - interface OrmlVestingVestingSchedule extends Struct { - readonly start: u32; - readonly period: u32; - readonly periodCount: u32; - readonly perPeriod: Compact; - } - - /** @name OrmlXtokensModuleEvent (48) */ - interface OrmlXtokensModuleEvent extends Enum { - readonly isTransferredMultiAssets: boolean; - readonly asTransferredMultiAssets: { - readonly sender: AccountId32; - readonly assets: StagingXcmV3MultiassetMultiAssets; - readonly fee: StagingXcmV3MultiAsset; - readonly dest: StagingXcmV3MultiLocation; - } & Struct; - readonly type: 'TransferredMultiAssets'; - } - - /** @name StagingXcmV3MultiassetMultiAssets (49) */ - interface StagingXcmV3MultiassetMultiAssets extends Vec {} - - /** @name StagingXcmV3MultiAsset (51) */ - interface StagingXcmV3MultiAsset extends Struct { - readonly id: StagingXcmV3MultiassetAssetId; - readonly fun: StagingXcmV3MultiassetFungibility; - } - - /** @name StagingXcmV3MultiassetAssetId (52) */ - interface StagingXcmV3MultiassetAssetId extends Enum { - readonly isConcrete: boolean; - readonly asConcrete: StagingXcmV3MultiLocation; - readonly isAbstract: boolean; - readonly asAbstract: U8aFixed; - readonly type: 'Concrete' | 'Abstract'; - } - - /** @name StagingXcmV3MultiLocation (53) */ - interface StagingXcmV3MultiLocation extends Struct { - readonly parents: u8; - readonly interior: StagingXcmV3Junctions; - } - - /** @name StagingXcmV3Junctions (54) */ - interface StagingXcmV3Junctions extends Enum { - readonly isHere: boolean; - readonly isX1: boolean; - readonly asX1: StagingXcmV3Junction; - readonly isX2: boolean; - readonly asX2: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX3: boolean; - readonly asX3: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX4: boolean; - readonly asX4: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX5: boolean; - readonly asX5: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX6: boolean; - readonly asX6: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX7: boolean; - readonly asX7: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly isX8: boolean; - readonly asX8: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; - readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; - } - - /** @name StagingXcmV3Junction (55) */ - interface StagingXcmV3Junction extends Enum { - readonly isParachain: boolean; - readonly asParachain: Compact; - readonly isAccountId32: boolean; - readonly asAccountId32: { - readonly network: Option; - readonly id: U8aFixed; - } & Struct; - readonly isAccountIndex64: boolean; - readonly asAccountIndex64: { - readonly network: Option; - readonly index: Compact; - } & Struct; - readonly isAccountKey20: boolean; - readonly asAccountKey20: { - readonly network: Option; - readonly key: U8aFixed; - } & Struct; - readonly isPalletInstance: boolean; - readonly asPalletInstance: u8; - readonly isGeneralIndex: boolean; - readonly asGeneralIndex: Compact; - readonly isGeneralKey: boolean; - readonly asGeneralKey: { - readonly length: u8; - readonly data: U8aFixed; - } & Struct; - readonly isOnlyChild: boolean; - readonly isPlurality: boolean; - readonly asPlurality: { - readonly id: StagingXcmV3JunctionBodyId; - readonly part: StagingXcmV3JunctionBodyPart; - } & Struct; - readonly isGlobalConsensus: boolean; - readonly asGlobalConsensus: StagingXcmV3JunctionNetworkId; - readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality' | 'GlobalConsensus'; - } - - /** @name StagingXcmV3JunctionNetworkId (58) */ - interface StagingXcmV3JunctionNetworkId extends Enum { - readonly isByGenesis: boolean; - readonly asByGenesis: U8aFixed; - readonly isByFork: boolean; - readonly asByFork: { - readonly blockNumber: u64; - readonly blockHash: U8aFixed; - } & Struct; - readonly isPolkadot: boolean; - readonly isKusama: boolean; - readonly isWestend: boolean; - readonly isRococo: boolean; - readonly isWococo: boolean; - readonly isEthereum: boolean; - readonly asEthereum: { - readonly chainId: Compact; - } & Struct; - readonly isBitcoinCore: boolean; - readonly isBitcoinCash: boolean; - readonly type: 'ByGenesis' | 'ByFork' | 'Polkadot' | 'Kusama' | 'Westend' | 'Rococo' | 'Wococo' | 'Ethereum' | 'BitcoinCore' | 'BitcoinCash'; - } - - /** @name StagingXcmV3JunctionBodyId (60) */ - interface StagingXcmV3JunctionBodyId extends Enum { - readonly isUnit: boolean; - readonly isMoniker: boolean; - readonly asMoniker: U8aFixed; - readonly isIndex: boolean; - readonly asIndex: Compact; - readonly isExecutive: boolean; - readonly isTechnical: boolean; - readonly isLegislative: boolean; - readonly isJudicial: boolean; - readonly isDefense: boolean; - readonly isAdministration: boolean; - readonly isTreasury: boolean; - readonly type: 'Unit' | 'Moniker' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury'; - } - - /** @name StagingXcmV3JunctionBodyPart (61) */ - interface StagingXcmV3JunctionBodyPart extends Enum { - readonly isVoice: boolean; - readonly isMembers: boolean; - readonly asMembers: { - readonly count: Compact; - } & Struct; - readonly isFraction: boolean; - readonly asFraction: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly isAtLeastProportion: boolean; - readonly asAtLeastProportion: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly isMoreThanProportion: boolean; - readonly asMoreThanProportion: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion'; - } - - /** @name StagingXcmV3MultiassetFungibility (62) */ - interface StagingXcmV3MultiassetFungibility extends Enum { - readonly isFungible: boolean; - readonly asFungible: Compact; - readonly isNonFungible: boolean; - readonly asNonFungible: StagingXcmV3MultiassetAssetInstance; - readonly type: 'Fungible' | 'NonFungible'; - } - - /** @name StagingXcmV3MultiassetAssetInstance (63) */ - interface StagingXcmV3MultiassetAssetInstance extends Enum { - readonly isUndefined: boolean; - readonly isIndex: boolean; - readonly asIndex: Compact; - readonly isArray4: boolean; - readonly asArray4: U8aFixed; - readonly isArray8: boolean; - readonly asArray8: U8aFixed; - readonly isArray16: boolean; - readonly asArray16: U8aFixed; - readonly isArray32: boolean; - readonly asArray32: U8aFixed; - readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32'; - } - - /** @name OrmlTokensModuleEvent (66) */ - interface OrmlTokensModuleEvent extends Enum { - readonly isEndowed: boolean; - readonly asEndowed: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isDustLost: boolean; - readonly asDustLost: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isTransfer: boolean; - readonly asTransfer: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly from: AccountId32; - readonly to: AccountId32; - readonly amount: u128; - } & Struct; - readonly isReserved: boolean; - readonly asReserved: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isUnreserved: boolean; - readonly asUnreserved: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isReserveRepatriated: boolean; - readonly asReserveRepatriated: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly from: AccountId32; - readonly to: AccountId32; - readonly amount: u128; - readonly status: FrameSupportTokensMiscBalanceStatus; - } & Struct; - readonly isBalanceSet: boolean; - readonly asBalanceSet: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly free: u128; - readonly reserved: u128; - } & Struct; - readonly isTotalIssuanceSet: boolean; - readonly asTotalIssuanceSet: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: u128; - } & Struct; - readonly isWithdrawn: boolean; - readonly asWithdrawn: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isSlashed: boolean; - readonly asSlashed: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly freeAmount: u128; - readonly reservedAmount: u128; - } & Struct; - readonly isDeposited: boolean; - readonly asDeposited: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isLockSet: boolean; - readonly asLockSet: { - readonly lockId: U8aFixed; - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isLockRemoved: boolean; - readonly asLockRemoved: { - readonly lockId: U8aFixed; - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - } & Struct; - readonly isLocked: boolean; - readonly asLocked: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isUnlocked: boolean; - readonly asUnlocked: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isIssued: boolean; - readonly asIssued: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: u128; - } & Struct; - readonly isRescinded: boolean; - readonly asRescinded: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: u128; - } & Struct; - readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'BalanceSet' | 'TotalIssuanceSet' | 'Withdrawn' | 'Slashed' | 'Deposited' | 'LockSet' | 'LockRemoved' | 'Locked' | 'Unlocked' | 'Issued' | 'Rescinded'; - } - - /** @name PalletForeignAssetsAssetId (67) */ - interface PalletForeignAssetsAssetId extends Enum { - readonly isForeignAssetId: boolean; - readonly asForeignAssetId: u32; - readonly isNativeAssetId: boolean; - readonly asNativeAssetId: PalletForeignAssetsNativeCurrency; - readonly type: 'ForeignAssetId' | 'NativeAssetId'; - } - - /** @name PalletForeignAssetsNativeCurrency (68) */ - interface PalletForeignAssetsNativeCurrency extends Enum { - readonly isHere: boolean; - readonly isParent: boolean; - readonly type: 'Here' | 'Parent'; - } - - /** @name PalletIdentityEvent (69) */ - interface PalletIdentityEvent extends Enum { - readonly isIdentitySet: boolean; - readonly asIdentitySet: { - readonly who: AccountId32; - } & Struct; - readonly isIdentityCleared: boolean; - readonly asIdentityCleared: { - readonly who: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isIdentityKilled: boolean; - readonly asIdentityKilled: { - readonly who: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isIdentitiesInserted: boolean; - readonly asIdentitiesInserted: { - readonly amount: u32; - } & Struct; - readonly isIdentitiesRemoved: boolean; - readonly asIdentitiesRemoved: { - readonly amount: u32; - } & Struct; - readonly isJudgementRequested: boolean; - readonly asJudgementRequested: { - readonly who: AccountId32; - readonly registrarIndex: u32; - } & Struct; - readonly isJudgementUnrequested: boolean; - readonly asJudgementUnrequested: { - readonly who: AccountId32; - readonly registrarIndex: u32; - } & Struct; - readonly isJudgementGiven: boolean; - readonly asJudgementGiven: { - readonly target: AccountId32; - readonly registrarIndex: u32; - } & Struct; - readonly isRegistrarAdded: boolean; - readonly asRegistrarAdded: { - readonly registrarIndex: u32; - } & Struct; - readonly isSubIdentityAdded: boolean; - readonly asSubIdentityAdded: { - readonly sub: AccountId32; - readonly main: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isSubIdentityRemoved: boolean; - readonly asSubIdentityRemoved: { - readonly sub: AccountId32; - readonly main: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isSubIdentityRevoked: boolean; - readonly asSubIdentityRevoked: { - readonly sub: AccountId32; - readonly main: AccountId32; - readonly deposit: u128; - } & Struct; - readonly isSubIdentitiesInserted: boolean; - readonly asSubIdentitiesInserted: { - readonly amount: u32; - } & Struct; - readonly type: 'IdentitySet' | 'IdentityCleared' | 'IdentityKilled' | 'IdentitiesInserted' | 'IdentitiesRemoved' | 'JudgementRequested' | 'JudgementUnrequested' | 'JudgementGiven' | 'RegistrarAdded' | 'SubIdentityAdded' | 'SubIdentityRemoved' | 'SubIdentityRevoked' | 'SubIdentitiesInserted'; - } - - /** @name PalletPreimageEvent (70) */ - interface PalletPreimageEvent extends Enum { - readonly isNoted: boolean; - readonly asNoted: { - readonly hash_: H256; - } & Struct; - readonly isRequested: boolean; - readonly asRequested: { - readonly hash_: H256; - } & Struct; - readonly isCleared: boolean; - readonly asCleared: { - readonly hash_: H256; - } & Struct; - readonly type: 'Noted' | 'Requested' | 'Cleared'; - } - - /** @name PalletDemocracyEvent (71) */ - interface PalletDemocracyEvent extends Enum { - readonly isProposed: boolean; - readonly asProposed: { - readonly proposalIndex: u32; - readonly deposit: u128; - } & Struct; - readonly isTabled: boolean; - readonly asTabled: { - readonly proposalIndex: u32; - readonly deposit: u128; - } & Struct; - readonly isExternalTabled: boolean; - readonly isStarted: boolean; - readonly asStarted: { - readonly refIndex: u32; - readonly threshold: PalletDemocracyVoteThreshold; - } & Struct; - readonly isPassed: boolean; - readonly asPassed: { - readonly refIndex: u32; - } & Struct; - readonly isNotPassed: boolean; - readonly asNotPassed: { - readonly refIndex: u32; - } & Struct; - readonly isCancelled: boolean; - readonly asCancelled: { - readonly refIndex: u32; - } & Struct; - readonly isDelegated: boolean; - readonly asDelegated: { - readonly who: AccountId32; - readonly target: AccountId32; - } & Struct; - readonly isUndelegated: boolean; - readonly asUndelegated: { - readonly account: AccountId32; - } & Struct; - readonly isVetoed: boolean; - readonly asVetoed: { - readonly who: AccountId32; - readonly proposalHash: H256; - readonly until: u32; - } & Struct; - readonly isBlacklisted: boolean; - readonly asBlacklisted: { - readonly proposalHash: H256; - } & Struct; - readonly isVoted: boolean; - readonly asVoted: { - readonly voter: AccountId32; - readonly refIndex: u32; - readonly vote: PalletDemocracyVoteAccountVote; - } & Struct; - readonly isSeconded: boolean; - readonly asSeconded: { - readonly seconder: AccountId32; - readonly propIndex: u32; - } & Struct; - readonly isProposalCanceled: boolean; - readonly asProposalCanceled: { - readonly propIndex: u32; - } & Struct; - readonly isMetadataSet: boolean; - readonly asMetadataSet: { - readonly owner: PalletDemocracyMetadataOwner; - readonly hash_: H256; - } & Struct; - readonly isMetadataCleared: boolean; - readonly asMetadataCleared: { - readonly owner: PalletDemocracyMetadataOwner; - readonly hash_: H256; - } & Struct; - readonly isMetadataTransferred: boolean; - readonly asMetadataTransferred: { - readonly prevOwner: PalletDemocracyMetadataOwner; - readonly owner: PalletDemocracyMetadataOwner; - readonly hash_: H256; - } & Struct; - readonly type: 'Proposed' | 'Tabled' | 'ExternalTabled' | 'Started' | 'Passed' | 'NotPassed' | 'Cancelled' | 'Delegated' | 'Undelegated' | 'Vetoed' | 'Blacklisted' | 'Voted' | 'Seconded' | 'ProposalCanceled' | 'MetadataSet' | 'MetadataCleared' | 'MetadataTransferred'; - } - - /** @name PalletDemocracyVoteThreshold (72) */ - interface PalletDemocracyVoteThreshold extends Enum { - readonly isSuperMajorityApprove: boolean; - readonly isSuperMajorityAgainst: boolean; - readonly isSimpleMajority: boolean; - readonly type: 'SuperMajorityApprove' | 'SuperMajorityAgainst' | 'SimpleMajority'; - } - - /** @name PalletDemocracyVoteAccountVote (73) */ - interface PalletDemocracyVoteAccountVote extends Enum { - readonly isStandard: boolean; - readonly asStandard: { - readonly vote: Vote; - readonly balance: u128; - } & Struct; - readonly isSplit: boolean; - readonly asSplit: { - readonly aye: u128; - readonly nay: u128; - } & Struct; - readonly type: 'Standard' | 'Split'; - } - - /** @name PalletDemocracyMetadataOwner (75) */ - interface PalletDemocracyMetadataOwner extends Enum { - readonly isExternal: boolean; - readonly isProposal: boolean; - readonly asProposal: u32; - readonly isReferendum: boolean; - readonly asReferendum: u32; - readonly type: 'External' | 'Proposal' | 'Referendum'; - } - - /** @name PalletCollectiveEvent (76) */ - interface PalletCollectiveEvent extends Enum { - readonly isProposed: boolean; - readonly asProposed: { - readonly account: AccountId32; - readonly proposalIndex: u32; - readonly proposalHash: H256; - readonly threshold: u32; - } & Struct; - readonly isVoted: boolean; - readonly asVoted: { - readonly account: AccountId32; - readonly proposalHash: H256; - readonly voted: bool; - readonly yes: u32; - readonly no: u32; - } & Struct; - readonly isApproved: boolean; - readonly asApproved: { - readonly proposalHash: H256; - } & Struct; - readonly isDisapproved: boolean; - readonly asDisapproved: { - readonly proposalHash: H256; - } & Struct; - readonly isExecuted: boolean; - readonly asExecuted: { - readonly proposalHash: H256; - readonly result: Result; - } & Struct; - readonly isMemberExecuted: boolean; - readonly asMemberExecuted: { - readonly proposalHash: H256; - readonly result: Result; - } & Struct; - readonly isClosed: boolean; - readonly asClosed: { - readonly proposalHash: H256; - readonly yes: u32; - readonly no: u32; - } & Struct; - readonly type: 'Proposed' | 'Voted' | 'Approved' | 'Disapproved' | 'Executed' | 'MemberExecuted' | 'Closed'; - } - - /** @name PalletMembershipEvent (79) */ - interface PalletMembershipEvent extends Enum { - readonly isMemberAdded: boolean; - readonly isMemberRemoved: boolean; - readonly isMembersSwapped: boolean; - readonly isMembersReset: boolean; - readonly isKeyChanged: boolean; - readonly isDummy: boolean; - readonly type: 'MemberAdded' | 'MemberRemoved' | 'MembersSwapped' | 'MembersReset' | 'KeyChanged' | 'Dummy'; - } - - /** @name PalletRankedCollectiveEvent (81) */ - interface PalletRankedCollectiveEvent extends Enum { - readonly isMemberAdded: boolean; - readonly asMemberAdded: { - readonly who: AccountId32; - } & Struct; - readonly isRankChanged: boolean; - readonly asRankChanged: { - readonly who: AccountId32; - readonly rank: u16; - } & Struct; - readonly isMemberRemoved: boolean; - readonly asMemberRemoved: { - readonly who: AccountId32; - readonly rank: u16; - } & Struct; - readonly isVoted: boolean; - readonly asVoted: { - readonly who: AccountId32; - readonly poll: u32; - readonly vote: PalletRankedCollectiveVoteRecord; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly type: 'MemberAdded' | 'RankChanged' | 'MemberRemoved' | 'Voted'; - } - - /** @name PalletRankedCollectiveVoteRecord (83) */ - interface PalletRankedCollectiveVoteRecord extends Enum { - readonly isAye: boolean; - readonly asAye: u32; - readonly isNay: boolean; - readonly asNay: u32; - readonly type: 'Aye' | 'Nay'; - } - - /** @name PalletRankedCollectiveTally (84) */ - interface PalletRankedCollectiveTally extends Struct { - readonly bareAyes: u32; - readonly ayes: u32; - readonly nays: u32; - } - - /** @name PalletReferendaEvent (85) */ - interface PalletReferendaEvent extends Enum { - readonly isSubmitted: boolean; - readonly asSubmitted: { - readonly index: u32; - readonly track: u16; - readonly proposal: FrameSupportPreimagesBounded; - } & Struct; - readonly isDecisionDepositPlaced: boolean; - readonly asDecisionDepositPlaced: { - readonly index: u32; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isDecisionDepositRefunded: boolean; - readonly asDecisionDepositRefunded: { - readonly index: u32; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isDepositSlashed: boolean; - readonly asDepositSlashed: { - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isDecisionStarted: boolean; - readonly asDecisionStarted: { - readonly index: u32; - readonly track: u16; - readonly proposal: FrameSupportPreimagesBounded; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isConfirmStarted: boolean; - readonly asConfirmStarted: { - readonly index: u32; - } & Struct; - readonly isConfirmAborted: boolean; - readonly asConfirmAborted: { - readonly index: u32; - } & Struct; - readonly isConfirmed: boolean; - readonly asConfirmed: { - readonly index: u32; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isApproved: boolean; - readonly asApproved: { - readonly index: u32; - } & Struct; - readonly isRejected: boolean; - readonly asRejected: { - readonly index: u32; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isTimedOut: boolean; - readonly asTimedOut: { - readonly index: u32; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isCancelled: boolean; - readonly asCancelled: { - readonly index: u32; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isKilled: boolean; - readonly asKilled: { - readonly index: u32; - readonly tally: PalletRankedCollectiveTally; - } & Struct; - readonly isSubmissionDepositRefunded: boolean; - readonly asSubmissionDepositRefunded: { - readonly index: u32; - readonly who: AccountId32; - readonly amount: u128; - } & Struct; - readonly isMetadataSet: boolean; - readonly asMetadataSet: { - readonly index: u32; - readonly hash_: H256; - } & Struct; - readonly isMetadataCleared: boolean; - readonly asMetadataCleared: { - readonly index: u32; - readonly hash_: H256; - } & Struct; - readonly type: 'Submitted' | 'DecisionDepositPlaced' | 'DecisionDepositRefunded' | 'DepositSlashed' | 'DecisionStarted' | 'ConfirmStarted' | 'ConfirmAborted' | 'Confirmed' | 'Approved' | 'Rejected' | 'TimedOut' | 'Cancelled' | 'Killed' | 'SubmissionDepositRefunded' | 'MetadataSet' | 'MetadataCleared'; - } - - /** @name FrameSupportPreimagesBounded (86) */ - interface FrameSupportPreimagesBounded extends Enum { - readonly isLegacy: boolean; - readonly asLegacy: { - readonly hash_: H256; - } & Struct; - readonly isInline: boolean; - readonly asInline: Bytes; - readonly isLookup: boolean; - readonly asLookup: { - readonly hash_: H256; - readonly len: u32; - } & Struct; - readonly type: 'Legacy' | 'Inline' | 'Lookup'; - } - - /** @name FrameSystemCall (88) */ - interface FrameSystemCall extends Enum { - readonly isRemark: boolean; - readonly asRemark: { - readonly remark: Bytes; - } & Struct; - readonly isSetHeapPages: boolean; - readonly asSetHeapPages: { - readonly pages: u64; - } & Struct; - readonly isSetCode: boolean; - readonly asSetCode: { - readonly code: Bytes; - } & Struct; - readonly isSetCodeWithoutChecks: boolean; - readonly asSetCodeWithoutChecks: { - readonly code: Bytes; - } & Struct; - readonly isSetStorage: boolean; - readonly asSetStorage: { - readonly items: Vec>; - } & Struct; - readonly isKillStorage: boolean; - readonly asKillStorage: { - readonly keys_: Vec; - } & Struct; - readonly isKillPrefix: boolean; - readonly asKillPrefix: { - readonly prefix: Bytes; - readonly subkeys: u32; - } & Struct; - readonly isRemarkWithEvent: boolean; - readonly asRemarkWithEvent: { - readonly remark: Bytes; - } & Struct; - readonly type: 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent'; - } - - /** @name PalletStateTrieMigrationCall (92) */ - interface PalletStateTrieMigrationCall extends Enum { - readonly isControlAutoMigration: boolean; - readonly asControlAutoMigration: { - readonly maybeConfig: Option; - } & Struct; - readonly isContinueMigrate: boolean; - readonly asContinueMigrate: { - readonly limits: PalletStateTrieMigrationMigrationLimits; - readonly realSizeUpper: u32; - readonly witnessTask: PalletStateTrieMigrationMigrationTask; - } & Struct; - readonly isMigrateCustomTop: boolean; - readonly asMigrateCustomTop: { - readonly keys_: Vec; - readonly witnessSize: u32; - } & Struct; - readonly isMigrateCustomChild: boolean; - readonly asMigrateCustomChild: { - readonly root: Bytes; - readonly childKeys: Vec; - readonly totalSize: u32; - } & Struct; - readonly isSetSignedMaxLimits: boolean; - readonly asSetSignedMaxLimits: { - readonly limits: PalletStateTrieMigrationMigrationLimits; - } & Struct; - readonly isForceSetProgress: boolean; - readonly asForceSetProgress: { - readonly progressTop: PalletStateTrieMigrationProgress; - readonly progressChild: PalletStateTrieMigrationProgress; - } & Struct; - readonly type: 'ControlAutoMigration' | 'ContinueMigrate' | 'MigrateCustomTop' | 'MigrateCustomChild' | 'SetSignedMaxLimits' | 'ForceSetProgress'; - } - - /** @name PalletStateTrieMigrationMigrationLimits (94) */ - interface PalletStateTrieMigrationMigrationLimits extends Struct { - readonly size_: u32; - readonly item: u32; - } - - /** @name PalletStateTrieMigrationMigrationTask (95) */ - interface PalletStateTrieMigrationMigrationTask extends Struct { - readonly progressTop: PalletStateTrieMigrationProgress; - readonly progressChild: PalletStateTrieMigrationProgress; - readonly size_: u32; - readonly topItems: u32; - readonly childItems: u32; - } - - /** @name PalletStateTrieMigrationProgress (96) */ - interface PalletStateTrieMigrationProgress extends Enum { - readonly isToStart: boolean; - readonly isLastKey: boolean; - readonly asLastKey: Bytes; - readonly isComplete: boolean; - readonly type: 'ToStart' | 'LastKey' | 'Complete'; - } - - /** @name CumulusPalletParachainSystemCall (98) */ - interface CumulusPalletParachainSystemCall extends Enum { - readonly isSetValidationData: boolean; - readonly asSetValidationData: { - readonly data: CumulusPrimitivesParachainInherentParachainInherentData; - } & Struct; - readonly isSudoSendUpwardMessage: boolean; - readonly asSudoSendUpwardMessage: { - readonly message: Bytes; - } & Struct; - readonly isAuthorizeUpgrade: boolean; - readonly asAuthorizeUpgrade: { - readonly codeHash: H256; - readonly checkVersion: bool; - } & Struct; - readonly isEnactAuthorizedUpgrade: boolean; - readonly asEnactAuthorizedUpgrade: { - readonly code: Bytes; - } & Struct; - readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade'; - } - - /** @name CumulusPrimitivesParachainInherentParachainInherentData (99) */ - interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct { - readonly validationData: PolkadotPrimitivesV5PersistedValidationData; - readonly relayChainState: SpTrieStorageProof; - readonly downwardMessages: Vec; - readonly horizontalMessages: BTreeMap>; - } - - /** @name PolkadotPrimitivesV5PersistedValidationData (100) */ - interface PolkadotPrimitivesV5PersistedValidationData extends Struct { - readonly parentHead: Bytes; - readonly relayParentNumber: u32; - readonly relayParentStorageRoot: H256; - readonly maxPovSize: u32; - } - - /** @name SpTrieStorageProof (102) */ - interface SpTrieStorageProof extends Struct { - readonly trieNodes: BTreeSet; - } - - /** @name PolkadotCorePrimitivesInboundDownwardMessage (105) */ - interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct { - readonly sentAt: u32; - readonly msg: Bytes; - } - - /** @name PolkadotCorePrimitivesInboundHrmpMessage (109) */ - interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct { - readonly sentAt: u32; - readonly data: Bytes; - } - - /** @name ParachainInfoCall (112) */ - type ParachainInfoCall = Null; - - /** @name PalletCollatorSelectionCall (113) */ - interface PalletCollatorSelectionCall extends Enum { - readonly isAddInvulnerable: boolean; - readonly asAddInvulnerable: { - readonly new_: AccountId32; - } & Struct; - readonly isRemoveInvulnerable: boolean; - readonly asRemoveInvulnerable: { - readonly who: AccountId32; - } & Struct; - readonly isGetLicense: boolean; - readonly isOnboard: boolean; - readonly isOffboard: boolean; - readonly isReleaseLicense: boolean; - readonly isForceReleaseLicense: boolean; - readonly asForceReleaseLicense: { - readonly who: AccountId32; - } & Struct; - readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceReleaseLicense'; - } - - /** @name PalletSessionCall (114) */ - interface PalletSessionCall extends Enum { - readonly isSetKeys: boolean; - readonly asSetKeys: { - readonly keys_: OpalRuntimeRuntimeCommonSessionKeys; - readonly proof: Bytes; - } & Struct; - readonly isPurgeKeys: boolean; - readonly type: 'SetKeys' | 'PurgeKeys'; - } - - /** @name OpalRuntimeRuntimeCommonSessionKeys (115) */ - interface OpalRuntimeRuntimeCommonSessionKeys extends Struct { - readonly aura: SpConsensusAuraSr25519AppSr25519Public; - } - - /** @name SpConsensusAuraSr25519AppSr25519Public (116) */ - interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public {} - - /** @name SpCoreSr25519Public (117) */ - interface SpCoreSr25519Public extends U8aFixed {} - - /** @name PalletBalancesCall (118) */ - interface PalletBalancesCall extends Enum { - readonly isTransferAllowDeath: boolean; - readonly asTransferAllowDeath: { - readonly dest: MultiAddress; - readonly value: Compact; - } & Struct; - readonly isSetBalanceDeprecated: boolean; - readonly asSetBalanceDeprecated: { - readonly who: MultiAddress; - readonly newFree: Compact; - readonly oldReserved: Compact; - } & Struct; - readonly isForceTransfer: boolean; - readonly asForceTransfer: { - readonly source: MultiAddress; - readonly dest: MultiAddress; - readonly value: Compact; - } & Struct; - readonly isTransferKeepAlive: boolean; - readonly asTransferKeepAlive: { - readonly dest: MultiAddress; - readonly value: Compact; - } & Struct; - readonly isTransferAll: boolean; - readonly asTransferAll: { - readonly dest: MultiAddress; - readonly keepAlive: bool; - } & Struct; - readonly isForceUnreserve: boolean; - readonly asForceUnreserve: { - readonly who: MultiAddress; - readonly amount: u128; - } & Struct; - readonly isUpgradeAccounts: boolean; - readonly asUpgradeAccounts: { - readonly who: Vec; - } & Struct; - readonly isTransfer: boolean; - readonly asTransfer: { - readonly dest: MultiAddress; - readonly value: Compact; - } & Struct; - readonly isForceSetBalance: boolean; - readonly asForceSetBalance: { - readonly who: MultiAddress; - readonly newFree: Compact; - } & Struct; - readonly type: 'TransferAllowDeath' | 'SetBalanceDeprecated' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve' | 'UpgradeAccounts' | 'Transfer' | 'ForceSetBalance'; - } - - /** @name PalletTimestampCall (122) */ - interface PalletTimestampCall extends Enum { - readonly isSet: boolean; - readonly asSet: { - readonly now: Compact; - } & Struct; - readonly type: 'Set'; - } - - /** @name PalletTreasuryCall (123) */ - interface PalletTreasuryCall extends Enum { - readonly isProposeSpend: boolean; - readonly asProposeSpend: { - readonly value: Compact; - readonly beneficiary: MultiAddress; - } & Struct; - readonly isRejectProposal: boolean; - readonly asRejectProposal: { - readonly proposalId: Compact; - } & Struct; - readonly isApproveProposal: boolean; - readonly asApproveProposal: { - readonly proposalId: Compact; - } & Struct; - readonly isSpend: boolean; - readonly asSpend: { - readonly amount: Compact; - readonly beneficiary: MultiAddress; - } & Struct; - readonly isRemoveApproval: boolean; - readonly asRemoveApproval: { - readonly proposalId: Compact; - } & Struct; - readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval'; - } - - /** @name PalletSudoCall (124) */ - interface PalletSudoCall extends Enum { - readonly isSudo: boolean; - readonly asSudo: { - readonly call: Call; - } & Struct; - readonly isSudoUncheckedWeight: boolean; - readonly asSudoUncheckedWeight: { - readonly call: Call; - readonly weight: SpWeightsWeightV2Weight; - } & Struct; - readonly isSetKey: boolean; - readonly asSetKey: { - readonly new_: MultiAddress; - } & Struct; - readonly isSudoAs: boolean; - readonly asSudoAs: { - readonly who: MultiAddress; - readonly call: Call; - } & Struct; - readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs'; - } - - /** @name OrmlVestingModuleCall (125) */ - interface OrmlVestingModuleCall extends Enum { - readonly isClaim: boolean; - readonly isVestedTransfer: boolean; - readonly asVestedTransfer: { - readonly dest: MultiAddress; - readonly schedule: OrmlVestingVestingSchedule; - } & Struct; - readonly isUpdateVestingSchedules: boolean; - readonly asUpdateVestingSchedules: { - readonly who: MultiAddress; - readonly vestingSchedules: Vec; - } & Struct; - readonly isClaimFor: boolean; - readonly asClaimFor: { - readonly dest: MultiAddress; - } & Struct; - readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor'; - } - - /** @name OrmlXtokensModuleCall (127) */ - interface OrmlXtokensModuleCall extends Enum { - readonly isTransfer: boolean; - readonly asTransfer: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: u128; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isTransferMultiasset: boolean; - readonly asTransferMultiasset: { - readonly asset: StagingXcmVersionedMultiAsset; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isTransferWithFee: boolean; - readonly asTransferWithFee: { - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: u128; - readonly fee: u128; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isTransferMultiassetWithFee: boolean; - readonly asTransferMultiassetWithFee: { - readonly asset: StagingXcmVersionedMultiAsset; - readonly fee: StagingXcmVersionedMultiAsset; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isTransferMulticurrencies: boolean; - readonly asTransferMulticurrencies: { - readonly currencies: Vec>; - readonly feeItem: u32; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isTransferMultiassets: boolean; - readonly asTransferMultiassets: { - readonly assets: StagingXcmVersionedMultiAssets; - readonly feeItem: u32; - readonly dest: StagingXcmVersionedMultiLocation; - readonly destWeightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets'; - } - - /** @name StagingXcmVersionedMultiLocation (128) */ - interface StagingXcmVersionedMultiLocation extends Enum { - readonly isV2: boolean; - readonly asV2: StagingXcmV2MultiLocation; - readonly isV3: boolean; - readonly asV3: StagingXcmV3MultiLocation; - readonly type: 'V2' | 'V3'; - } - - /** @name StagingXcmV2MultiLocation (129) */ - interface StagingXcmV2MultiLocation extends Struct { - readonly parents: u8; - readonly interior: StagingXcmV2MultilocationJunctions; - } - - /** @name StagingXcmV2MultilocationJunctions (130) */ - interface StagingXcmV2MultilocationJunctions extends Enum { - readonly isHere: boolean; - readonly isX1: boolean; - readonly asX1: StagingXcmV2Junction; - readonly isX2: boolean; - readonly asX2: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX3: boolean; - readonly asX3: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX4: boolean; - readonly asX4: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX5: boolean; - readonly asX5: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX6: boolean; - readonly asX6: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX7: boolean; - readonly asX7: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly isX8: boolean; - readonly asX8: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; - readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; - } - - /** @name StagingXcmV2Junction (131) */ - interface StagingXcmV2Junction extends Enum { - readonly isParachain: boolean; - readonly asParachain: Compact; - readonly isAccountId32: boolean; - readonly asAccountId32: { - readonly network: StagingXcmV2NetworkId; - readonly id: U8aFixed; - } & Struct; - readonly isAccountIndex64: boolean; - readonly asAccountIndex64: { - readonly network: StagingXcmV2NetworkId; - readonly index: Compact; - } & Struct; - readonly isAccountKey20: boolean; - readonly asAccountKey20: { - readonly network: StagingXcmV2NetworkId; - readonly key: U8aFixed; - } & Struct; - readonly isPalletInstance: boolean; - readonly asPalletInstance: u8; - readonly isGeneralIndex: boolean; - readonly asGeneralIndex: Compact; - readonly isGeneralKey: boolean; - readonly asGeneralKey: Bytes; - readonly isOnlyChild: boolean; - readonly isPlurality: boolean; - readonly asPlurality: { - readonly id: StagingXcmV2BodyId; - readonly part: StagingXcmV2BodyPart; - } & Struct; - readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality'; - } - - /** @name StagingXcmV2NetworkId (132) */ - interface StagingXcmV2NetworkId extends Enum { - readonly isAny: boolean; - readonly isNamed: boolean; - readonly asNamed: Bytes; - readonly isPolkadot: boolean; - readonly isKusama: boolean; - readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama'; - } - - /** @name StagingXcmV2BodyId (134) */ - interface StagingXcmV2BodyId extends Enum { - readonly isUnit: boolean; - readonly isNamed: boolean; - readonly asNamed: Bytes; - readonly isIndex: boolean; - readonly asIndex: Compact; - readonly isExecutive: boolean; - readonly isTechnical: boolean; - readonly isLegislative: boolean; - readonly isJudicial: boolean; - readonly isDefense: boolean; - readonly isAdministration: boolean; - readonly isTreasury: boolean; - readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury'; - } - - /** @name StagingXcmV2BodyPart (135) */ - interface StagingXcmV2BodyPart extends Enum { - readonly isVoice: boolean; - readonly isMembers: boolean; - readonly asMembers: { - readonly count: Compact; - } & Struct; - readonly isFraction: boolean; - readonly asFraction: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly isAtLeastProportion: boolean; - readonly asAtLeastProportion: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly isMoreThanProportion: boolean; - readonly asMoreThanProportion: { - readonly nom: Compact; - readonly denom: Compact; - } & Struct; - readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion'; - } - - /** @name StagingXcmV3WeightLimit (136) */ - interface StagingXcmV3WeightLimit extends Enum { - readonly isUnlimited: boolean; - readonly isLimited: boolean; - readonly asLimited: SpWeightsWeightV2Weight; - readonly type: 'Unlimited' | 'Limited'; - } - - /** @name StagingXcmVersionedMultiAsset (137) */ - interface StagingXcmVersionedMultiAsset extends Enum { - readonly isV2: boolean; - readonly asV2: StagingXcmV2MultiAsset; - readonly isV3: boolean; - readonly asV3: StagingXcmV3MultiAsset; - readonly type: 'V2' | 'V3'; - } - - /** @name StagingXcmV2MultiAsset (138) */ - interface StagingXcmV2MultiAsset extends Struct { - readonly id: StagingXcmV2MultiassetAssetId; - readonly fun: StagingXcmV2MultiassetFungibility; - } - - /** @name StagingXcmV2MultiassetAssetId (139) */ - interface StagingXcmV2MultiassetAssetId extends Enum { - readonly isConcrete: boolean; - readonly asConcrete: StagingXcmV2MultiLocation; - readonly isAbstract: boolean; - readonly asAbstract: Bytes; - readonly type: 'Concrete' | 'Abstract'; - } - - /** @name StagingXcmV2MultiassetFungibility (140) */ - interface StagingXcmV2MultiassetFungibility extends Enum { - readonly isFungible: boolean; - readonly asFungible: Compact; - readonly isNonFungible: boolean; - readonly asNonFungible: StagingXcmV2MultiassetAssetInstance; - readonly type: 'Fungible' | 'NonFungible'; - } - - /** @name StagingXcmV2MultiassetAssetInstance (141) */ - interface StagingXcmV2MultiassetAssetInstance extends Enum { - readonly isUndefined: boolean; - readonly isIndex: boolean; - readonly asIndex: Compact; - readonly isArray4: boolean; - readonly asArray4: U8aFixed; - readonly isArray8: boolean; - readonly asArray8: U8aFixed; - readonly isArray16: boolean; - readonly asArray16: U8aFixed; - readonly isArray32: boolean; - readonly asArray32: U8aFixed; - readonly isBlob: boolean; - readonly asBlob: Bytes; - readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob'; - } - - /** @name StagingXcmVersionedMultiAssets (144) */ - interface StagingXcmVersionedMultiAssets extends Enum { - readonly isV2: boolean; - readonly asV2: StagingXcmV2MultiassetMultiAssets; - readonly isV3: boolean; - readonly asV3: StagingXcmV3MultiassetMultiAssets; - readonly type: 'V2' | 'V3'; - } - - /** @name StagingXcmV2MultiassetMultiAssets (145) */ - interface StagingXcmV2MultiassetMultiAssets extends Vec {} - - /** @name OrmlTokensModuleCall (147) */ - interface OrmlTokensModuleCall extends Enum { - readonly isTransfer: boolean; - readonly asTransfer: { - readonly dest: MultiAddress; - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: Compact; - } & Struct; - readonly isTransferAll: boolean; - readonly asTransferAll: { - readonly dest: MultiAddress; - readonly currencyId: PalletForeignAssetsAssetId; - readonly keepAlive: bool; - } & Struct; - readonly isTransferKeepAlive: boolean; - readonly asTransferKeepAlive: { - readonly dest: MultiAddress; - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: Compact; - } & Struct; - readonly isForceTransfer: boolean; - readonly asForceTransfer: { - readonly source: MultiAddress; - readonly dest: MultiAddress; - readonly currencyId: PalletForeignAssetsAssetId; - readonly amount: Compact; - } & Struct; - readonly isSetBalance: boolean; - readonly asSetBalance: { - readonly who: MultiAddress; - readonly currencyId: PalletForeignAssetsAssetId; - readonly newFree: Compact; - readonly newReserved: Compact; - } & Struct; - readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance'; - } - - /** @name PalletIdentityCall (148) */ - interface PalletIdentityCall extends Enum { - readonly isAddRegistrar: boolean; - readonly asAddRegistrar: { - readonly account: MultiAddress; - } & Struct; - readonly isSetIdentity: boolean; - readonly asSetIdentity: { - readonly info: PalletIdentityIdentityInfo; - } & Struct; - readonly isSetSubs: boolean; - readonly asSetSubs: { - readonly subs: Vec>; - } & Struct; - readonly isClearIdentity: boolean; - readonly isRequestJudgement: boolean; - readonly asRequestJudgement: { - readonly regIndex: Compact; - readonly maxFee: Compact; - } & Struct; - readonly isCancelRequest: boolean; - readonly asCancelRequest: { - readonly regIndex: u32; - } & Struct; - readonly isSetFee: boolean; - readonly asSetFee: { - readonly index: Compact; - readonly fee: Compact; - } & Struct; - readonly isSetAccountId: boolean; - readonly asSetAccountId: { - readonly index: Compact; - readonly new_: MultiAddress; - } & Struct; - readonly isSetFields: boolean; - readonly asSetFields: { - readonly index: Compact; - readonly fields: PalletIdentityBitFlags; - } & Struct; - readonly isProvideJudgement: boolean; - readonly asProvideJudgement: { - readonly regIndex: Compact; - readonly target: MultiAddress; - readonly judgement: PalletIdentityJudgement; - readonly identity: H256; - } & Struct; - readonly isKillIdentity: boolean; - readonly asKillIdentity: { - readonly target: MultiAddress; - } & Struct; - readonly isAddSub: boolean; - readonly asAddSub: { - readonly sub: MultiAddress; - readonly data: Data; - } & Struct; - readonly isRenameSub: boolean; - readonly asRenameSub: { - readonly sub: MultiAddress; - readonly data: Data; - } & Struct; - readonly isRemoveSub: boolean; - readonly asRemoveSub: { - readonly sub: MultiAddress; - } & Struct; - readonly isQuitSub: boolean; - readonly isForceInsertIdentities: boolean; - readonly asForceInsertIdentities: { - readonly identities: Vec>; - } & Struct; - readonly isForceRemoveIdentities: boolean; - readonly asForceRemoveIdentities: { - readonly identities: Vec; - } & Struct; - readonly isForceSetSubs: boolean; - readonly asForceSetSubs: { - readonly subs: Vec>]>]>>; - } & Struct; - readonly type: 'AddRegistrar' | 'SetIdentity' | 'SetSubs' | 'ClearIdentity' | 'RequestJudgement' | 'CancelRequest' | 'SetFee' | 'SetAccountId' | 'SetFields' | 'ProvideJudgement' | 'KillIdentity' | 'AddSub' | 'RenameSub' | 'RemoveSub' | 'QuitSub' | 'ForceInsertIdentities' | 'ForceRemoveIdentities' | 'ForceSetSubs'; - } - - /** @name PalletIdentityIdentityInfo (149) */ - interface PalletIdentityIdentityInfo extends Struct { - readonly additional: Vec>; - readonly display: Data; - readonly legal: Data; - readonly web: Data; - readonly riot: Data; - readonly email: Data; - readonly pgpFingerprint: Option; - readonly image: Data; - readonly twitter: Data; - } - - /** @name PalletIdentityBitFlags (185) */ - interface PalletIdentityBitFlags extends Set { - readonly isDisplay: boolean; - readonly isLegal: boolean; - readonly isWeb: boolean; - readonly isRiot: boolean; - readonly isEmail: boolean; - readonly isPgpFingerprint: boolean; - readonly isImage: boolean; - readonly isTwitter: boolean; - } - - /** @name PalletIdentityIdentityField (186) */ - interface PalletIdentityIdentityField extends Enum { - readonly isDisplay: boolean; - readonly isLegal: boolean; - readonly isWeb: boolean; - readonly isRiot: boolean; - readonly isEmail: boolean; - readonly isPgpFingerprint: boolean; - readonly isImage: boolean; - readonly isTwitter: boolean; - readonly type: 'Display' | 'Legal' | 'Web' | 'Riot' | 'Email' | 'PgpFingerprint' | 'Image' | 'Twitter'; - } - - /** @name PalletIdentityJudgement (187) */ - interface PalletIdentityJudgement extends Enum { - readonly isUnknown: boolean; - readonly isFeePaid: boolean; - readonly asFeePaid: u128; - readonly isReasonable: boolean; - readonly isKnownGood: boolean; - readonly isOutOfDate: boolean; - readonly isLowQuality: boolean; - readonly isErroneous: boolean; - readonly type: 'Unknown' | 'FeePaid' | 'Reasonable' | 'KnownGood' | 'OutOfDate' | 'LowQuality' | 'Erroneous'; - } - - /** @name PalletIdentityRegistration (190) */ - interface PalletIdentityRegistration extends Struct { - readonly judgements: Vec>; - readonly deposit: u128; - readonly info: PalletIdentityIdentityInfo; - } - - /** @name PalletPreimageCall (198) */ - interface PalletPreimageCall extends Enum { - readonly isNotePreimage: boolean; - readonly asNotePreimage: { - readonly bytes: Bytes; - } & Struct; - readonly isUnnotePreimage: boolean; - readonly asUnnotePreimage: { - readonly hash_: H256; - } & Struct; - readonly isRequestPreimage: boolean; - readonly asRequestPreimage: { - readonly hash_: H256; - } & Struct; - readonly isUnrequestPreimage: boolean; - readonly asUnrequestPreimage: { - readonly hash_: H256; - } & Struct; - readonly type: 'NotePreimage' | 'UnnotePreimage' | 'RequestPreimage' | 'UnrequestPreimage'; - } - - /** @name PalletDemocracyCall (199) */ - interface PalletDemocracyCall extends Enum { - readonly isPropose: boolean; - readonly asPropose: { - readonly proposal: FrameSupportPreimagesBounded; - readonly value: Compact; - } & Struct; - readonly isSecond: boolean; - readonly asSecond: { - readonly proposal: Compact; - } & Struct; - readonly isVote: boolean; - readonly asVote: { - readonly refIndex: Compact; - readonly vote: PalletDemocracyVoteAccountVote; - } & Struct; - readonly isEmergencyCancel: boolean; - readonly asEmergencyCancel: { - readonly refIndex: u32; - } & Struct; - readonly isExternalPropose: boolean; - readonly asExternalPropose: { - readonly proposal: FrameSupportPreimagesBounded; - } & Struct; - readonly isExternalProposeMajority: boolean; - readonly asExternalProposeMajority: { - readonly proposal: FrameSupportPreimagesBounded; - } & Struct; - readonly isExternalProposeDefault: boolean; - readonly asExternalProposeDefault: { - readonly proposal: FrameSupportPreimagesBounded; - } & Struct; - readonly isFastTrack: boolean; - readonly asFastTrack: { - readonly proposalHash: H256; - readonly votingPeriod: u32; - readonly delay: u32; - } & Struct; - readonly isVetoExternal: boolean; - readonly asVetoExternal: { - readonly proposalHash: H256; - } & Struct; - readonly isCancelReferendum: boolean; - readonly asCancelReferendum: { - readonly refIndex: Compact; - } & Struct; - readonly isDelegate: boolean; - readonly asDelegate: { - readonly to: MultiAddress; - readonly conviction: PalletDemocracyConviction; - readonly balance: u128; - } & Struct; - readonly isUndelegate: boolean; - readonly isClearPublicProposals: boolean; - readonly isUnlock: boolean; - readonly asUnlock: { - readonly target: MultiAddress; - } & Struct; - readonly isRemoveVote: boolean; - readonly asRemoveVote: { - readonly index: u32; - } & Struct; - readonly isRemoveOtherVote: boolean; - readonly asRemoveOtherVote: { - readonly target: MultiAddress; - readonly index: u32; - } & Struct; - readonly isBlacklist: boolean; - readonly asBlacklist: { - readonly proposalHash: H256; - readonly maybeRefIndex: Option; - } & Struct; - readonly isCancelProposal: boolean; - readonly asCancelProposal: { - readonly propIndex: Compact; - } & Struct; - readonly isSetMetadata: boolean; - readonly asSetMetadata: { - readonly owner: PalletDemocracyMetadataOwner; - readonly maybeHash: Option; - } & Struct; - readonly type: 'Propose' | 'Second' | 'Vote' | 'EmergencyCancel' | 'ExternalPropose' | 'ExternalProposeMajority' | 'ExternalProposeDefault' | 'FastTrack' | 'VetoExternal' | 'CancelReferendum' | 'Delegate' | 'Undelegate' | 'ClearPublicProposals' | 'Unlock' | 'RemoveVote' | 'RemoveOtherVote' | 'Blacklist' | 'CancelProposal' | 'SetMetadata'; - } - - /** @name PalletDemocracyConviction (200) */ - interface PalletDemocracyConviction extends Enum { - readonly isNone: boolean; - readonly isLocked1x: boolean; - readonly isLocked2x: boolean; - readonly isLocked3x: boolean; - readonly isLocked4x: boolean; - readonly isLocked5x: boolean; - readonly isLocked6x: boolean; - readonly type: 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x'; - } - - /** @name PalletCollectiveCall (203) */ - interface PalletCollectiveCall extends Enum { - readonly isSetMembers: boolean; - readonly asSetMembers: { - readonly newMembers: Vec; - readonly prime: Option; - readonly oldCount: u32; - } & Struct; - readonly isExecute: boolean; - readonly asExecute: { - readonly proposal: Call; - readonly lengthBound: Compact; - } & Struct; - readonly isPropose: boolean; - readonly asPropose: { - readonly threshold: Compact; - readonly proposal: Call; - readonly lengthBound: Compact; - } & Struct; - readonly isVote: boolean; - readonly asVote: { - readonly proposal: H256; - readonly index: Compact; - readonly approve: bool; - } & Struct; - readonly isDisapproveProposal: boolean; - readonly asDisapproveProposal: { - readonly proposalHash: H256; - } & Struct; - readonly isClose: boolean; - readonly asClose: { - readonly proposalHash: H256; - readonly index: Compact; - readonly proposalWeightBound: SpWeightsWeightV2Weight; - readonly lengthBound: Compact; - } & Struct; - readonly type: 'SetMembers' | 'Execute' | 'Propose' | 'Vote' | 'DisapproveProposal' | 'Close'; - } - - /** @name PalletMembershipCall (205) */ - interface PalletMembershipCall extends Enum { - readonly isAddMember: boolean; - readonly asAddMember: { - readonly who: MultiAddress; - } & Struct; - readonly isRemoveMember: boolean; - readonly asRemoveMember: { - readonly who: MultiAddress; - } & Struct; - readonly isSwapMember: boolean; - readonly asSwapMember: { - readonly remove: MultiAddress; - readonly add: MultiAddress; - } & Struct; - readonly isResetMembers: boolean; - readonly asResetMembers: { - readonly members: Vec; - } & Struct; - readonly isChangeKey: boolean; - readonly asChangeKey: { - readonly new_: MultiAddress; - } & Struct; - readonly isSetPrime: boolean; - readonly asSetPrime: { - readonly who: MultiAddress; - } & Struct; - readonly isClearPrime: boolean; - readonly type: 'AddMember' | 'RemoveMember' | 'SwapMember' | 'ResetMembers' | 'ChangeKey' | 'SetPrime' | 'ClearPrime'; - } - - /** @name PalletRankedCollectiveCall (207) */ - interface PalletRankedCollectiveCall extends Enum { - readonly isAddMember: boolean; - readonly asAddMember: { - readonly who: MultiAddress; - } & Struct; - readonly isPromoteMember: boolean; - readonly asPromoteMember: { - readonly who: MultiAddress; - } & Struct; - readonly isDemoteMember: boolean; - readonly asDemoteMember: { - readonly who: MultiAddress; - } & Struct; - readonly isRemoveMember: boolean; - readonly asRemoveMember: { - readonly who: MultiAddress; - readonly minRank: u16; - } & Struct; - readonly isVote: boolean; - readonly asVote: { - readonly poll: u32; - readonly aye: bool; - } & Struct; - readonly isCleanupPoll: boolean; - readonly asCleanupPoll: { - readonly pollIndex: u32; - readonly max: u32; - } & Struct; - readonly type: 'AddMember' | 'PromoteMember' | 'DemoteMember' | 'RemoveMember' | 'Vote' | 'CleanupPoll'; - } - - /** @name PalletReferendaCall (208) */ - interface PalletReferendaCall extends Enum { - readonly isSubmit: boolean; - readonly asSubmit: { - readonly proposalOrigin: OpalRuntimeOriginCaller; - readonly proposal: FrameSupportPreimagesBounded; - readonly enactmentMoment: FrameSupportScheduleDispatchTime; - } & Struct; - readonly isPlaceDecisionDeposit: boolean; - readonly asPlaceDecisionDeposit: { - readonly index: u32; - } & Struct; - readonly isRefundDecisionDeposit: boolean; - readonly asRefundDecisionDeposit: { - readonly index: u32; - } & Struct; - readonly isCancel: boolean; - readonly asCancel: { - readonly index: u32; - } & Struct; - readonly isKill: boolean; - readonly asKill: { - readonly index: u32; - } & Struct; - readonly isNudgeReferendum: boolean; - readonly asNudgeReferendum: { - readonly index: u32; - } & Struct; - readonly isOneFewerDeciding: boolean; - readonly asOneFewerDeciding: { - readonly track: u16; - } & Struct; - readonly isRefundSubmissionDeposit: boolean; - readonly asRefundSubmissionDeposit: { - readonly index: u32; - } & Struct; - readonly isSetMetadata: boolean; - readonly asSetMetadata: { - readonly index: u32; - readonly maybeHash: Option; - } & Struct; - readonly type: 'Submit' | 'PlaceDecisionDeposit' | 'RefundDecisionDeposit' | 'Cancel' | 'Kill' | 'NudgeReferendum' | 'OneFewerDeciding' | 'RefundSubmissionDeposit' | 'SetMetadata'; - } - - /** @name OpalRuntimeOriginCaller (209) */ - interface OpalRuntimeOriginCaller extends Enum { - readonly isSystem: boolean; - readonly asSystem: FrameSupportDispatchRawOrigin; - readonly isVoid: boolean; - readonly isCouncil: boolean; - readonly asCouncil: PalletCollectiveRawOrigin; - readonly isTechnicalCommittee: boolean; - readonly asTechnicalCommittee: PalletCollectiveRawOrigin; - readonly isPolkadotXcm: boolean; - readonly asPolkadotXcm: PalletXcmOrigin; - readonly isCumulusXcm: boolean; - readonly asCumulusXcm: CumulusPalletXcmOrigin; - readonly isOrigins: boolean; - readonly asOrigins: PalletGovOriginsOrigin; - readonly isEthereum: boolean; - readonly asEthereum: PalletEthereumRawOrigin; - readonly type: 'System' | 'Void' | 'Council' | 'TechnicalCommittee' | 'PolkadotXcm' | 'CumulusXcm' | 'Origins' | 'Ethereum'; - } - - /** @name FrameSupportDispatchRawOrigin (210) */ - interface FrameSupportDispatchRawOrigin extends Enum { - readonly isRoot: boolean; - readonly isSigned: boolean; - readonly asSigned: AccountId32; - readonly isNone: boolean; - readonly type: 'Root' | 'Signed' | 'None'; - } - - /** @name PalletCollectiveRawOrigin (211) */ - interface PalletCollectiveRawOrigin extends Enum { - readonly isMembers: boolean; - readonly asMembers: ITuple<[u32, u32]>; - readonly isMember: boolean; - readonly asMember: AccountId32; - readonly isPhantom: boolean; - readonly type: 'Members' | 'Member' | 'Phantom'; - } - - /** @name PalletGovOriginsOrigin (213) */ - interface PalletGovOriginsOrigin extends Enum { - readonly isFellowshipProposition: boolean; - readonly type: 'FellowshipProposition'; - } - - /** @name PalletXcmOrigin (214) */ - interface PalletXcmOrigin extends Enum { - readonly isXcm: boolean; - readonly asXcm: StagingXcmV3MultiLocation; - readonly isResponse: boolean; - readonly asResponse: StagingXcmV3MultiLocation; - readonly type: 'Xcm' | 'Response'; - } - - /** @name CumulusPalletXcmOrigin (215) */ - interface CumulusPalletXcmOrigin extends Enum { - readonly isRelay: boolean; - readonly isSiblingParachain: boolean; - readonly asSiblingParachain: u32; - readonly type: 'Relay' | 'SiblingParachain'; - } - - /** @name PalletEthereumRawOrigin (216) */ - interface PalletEthereumRawOrigin extends Enum { - readonly isEthereumTransaction: boolean; - readonly asEthereumTransaction: H160; - readonly type: 'EthereumTransaction'; - } - - /** @name SpCoreVoid (218) */ - type SpCoreVoid = Null; - - /** @name FrameSupportScheduleDispatchTime (219) */ - interface FrameSupportScheduleDispatchTime extends Enum { - readonly isAt: boolean; - readonly asAt: u32; - readonly isAfter: boolean; - readonly asAfter: u32; - readonly type: 'At' | 'After'; - } - - /** @name PalletSchedulerCall (220) */ - interface PalletSchedulerCall extends Enum { - readonly isSchedule: boolean; - readonly asSchedule: { - readonly when: u32; - readonly maybePeriodic: Option>; - readonly priority: u8; - readonly call: Call; - } & Struct; - readonly isCancel: boolean; - readonly asCancel: { - readonly when: u32; - readonly index: u32; - } & Struct; - readonly isScheduleNamed: boolean; - readonly asScheduleNamed: { - readonly id: U8aFixed; - readonly when: u32; - readonly maybePeriodic: Option>; - readonly priority: u8; - readonly call: Call; - } & Struct; - readonly isCancelNamed: boolean; - readonly asCancelNamed: { - readonly id: U8aFixed; - } & Struct; - readonly isScheduleAfter: boolean; - readonly asScheduleAfter: { - readonly after: u32; - readonly maybePeriodic: Option>; - readonly priority: u8; - readonly call: Call; - } & Struct; - readonly isScheduleNamedAfter: boolean; - readonly asScheduleNamedAfter: { - readonly id: U8aFixed; - readonly after: u32; - readonly maybePeriodic: Option>; - readonly priority: u8; - readonly call: Call; - } & Struct; - readonly type: 'Schedule' | 'Cancel' | 'ScheduleNamed' | 'CancelNamed' | 'ScheduleAfter' | 'ScheduleNamedAfter'; - } - - /** @name CumulusPalletXcmpQueueCall (223) */ - interface CumulusPalletXcmpQueueCall extends Enum { - readonly isServiceOverweight: boolean; - readonly asServiceOverweight: { - readonly index: u64; - readonly weightLimit: SpWeightsWeightV2Weight; - } & Struct; - readonly isSuspendXcmExecution: boolean; - readonly isResumeXcmExecution: boolean; - readonly isUpdateSuspendThreshold: boolean; - readonly asUpdateSuspendThreshold: { - readonly new_: u32; - } & Struct; - readonly isUpdateDropThreshold: boolean; - readonly asUpdateDropThreshold: { - readonly new_: u32; - } & Struct; - readonly isUpdateResumeThreshold: boolean; - readonly asUpdateResumeThreshold: { - readonly new_: u32; - } & Struct; - readonly isUpdateThresholdWeight: boolean; - readonly asUpdateThresholdWeight: { - readonly new_: SpWeightsWeightV2Weight; - } & Struct; - readonly isUpdateWeightRestrictDecay: boolean; - readonly asUpdateWeightRestrictDecay: { - readonly new_: SpWeightsWeightV2Weight; - } & Struct; - readonly isUpdateXcmpMaxIndividualWeight: boolean; - readonly asUpdateXcmpMaxIndividualWeight: { - readonly new_: SpWeightsWeightV2Weight; - } & Struct; - readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight'; - } - - /** @name PalletXcmCall (224) */ - interface PalletXcmCall extends Enum { - readonly isSend: boolean; - readonly asSend: { - readonly dest: StagingXcmVersionedMultiLocation; - readonly message: StagingXcmVersionedXcm; - } & Struct; - readonly isTeleportAssets: boolean; - readonly asTeleportAssets: { - readonly dest: StagingXcmVersionedMultiLocation; - readonly beneficiary: StagingXcmVersionedMultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - readonly feeAssetItem: u32; - } & Struct; - readonly isReserveTransferAssets: boolean; - readonly asReserveTransferAssets: { - readonly dest: StagingXcmVersionedMultiLocation; - readonly beneficiary: StagingXcmVersionedMultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - readonly feeAssetItem: u32; - } & Struct; - readonly isExecute: boolean; - readonly asExecute: { - readonly message: StagingXcmVersionedXcm; - readonly maxWeight: SpWeightsWeightV2Weight; - } & Struct; - readonly isForceXcmVersion: boolean; - readonly asForceXcmVersion: { - readonly location: StagingXcmV3MultiLocation; - readonly version: u32; - } & Struct; - readonly isForceDefaultXcmVersion: boolean; - readonly asForceDefaultXcmVersion: { - readonly maybeXcmVersion: Option; - } & Struct; - readonly isForceSubscribeVersionNotify: boolean; - readonly asForceSubscribeVersionNotify: { - readonly location: StagingXcmVersionedMultiLocation; - } & Struct; - readonly isForceUnsubscribeVersionNotify: boolean; - readonly asForceUnsubscribeVersionNotify: { - readonly location: StagingXcmVersionedMultiLocation; - } & Struct; - readonly isLimitedReserveTransferAssets: boolean; - readonly asLimitedReserveTransferAssets: { - readonly dest: StagingXcmVersionedMultiLocation; - readonly beneficiary: StagingXcmVersionedMultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - readonly feeAssetItem: u32; - readonly weightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isLimitedTeleportAssets: boolean; - readonly asLimitedTeleportAssets: { - readonly dest: StagingXcmVersionedMultiLocation; - readonly beneficiary: StagingXcmVersionedMultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - readonly feeAssetItem: u32; - readonly weightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isForceSuspension: boolean; - readonly asForceSuspension: { - readonly suspended: bool; - } & Struct; - readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets' | 'ForceSuspension'; - } - - /** @name StagingXcmVersionedXcm (225) */ - interface StagingXcmVersionedXcm extends Enum { - readonly isV2: boolean; - readonly asV2: StagingXcmV2Xcm; - readonly isV3: boolean; - readonly asV3: StagingXcmV3Xcm; - readonly type: 'V2' | 'V3'; - } - - /** @name StagingXcmV2Xcm (226) */ - interface StagingXcmV2Xcm extends Vec {} - - /** @name StagingXcmV2Instruction (228) */ - interface StagingXcmV2Instruction extends Enum { - readonly isWithdrawAsset: boolean; - readonly asWithdrawAsset: StagingXcmV2MultiassetMultiAssets; - readonly isReserveAssetDeposited: boolean; - readonly asReserveAssetDeposited: StagingXcmV2MultiassetMultiAssets; - readonly isReceiveTeleportedAsset: boolean; - readonly asReceiveTeleportedAsset: StagingXcmV2MultiassetMultiAssets; - readonly isQueryResponse: boolean; - readonly asQueryResponse: { - readonly queryId: Compact; - readonly response: StagingXcmV2Response; - readonly maxWeight: Compact; - } & Struct; - readonly isTransferAsset: boolean; - readonly asTransferAsset: { - readonly assets: StagingXcmV2MultiassetMultiAssets; - readonly beneficiary: StagingXcmV2MultiLocation; - } & Struct; - readonly isTransferReserveAsset: boolean; - readonly asTransferReserveAsset: { - readonly assets: StagingXcmV2MultiassetMultiAssets; - readonly dest: StagingXcmV2MultiLocation; - readonly xcm: StagingXcmV2Xcm; - } & Struct; - readonly isTransact: boolean; - readonly asTransact: { - readonly originType: StagingXcmV2OriginKind; - readonly requireWeightAtMost: Compact; - readonly call: StagingXcmDoubleEncoded; - } & Struct; - readonly isHrmpNewChannelOpenRequest: boolean; - readonly asHrmpNewChannelOpenRequest: { - readonly sender: Compact; - readonly maxMessageSize: Compact; - readonly maxCapacity: Compact; - } & Struct; - readonly isHrmpChannelAccepted: boolean; - readonly asHrmpChannelAccepted: { - readonly recipient: Compact; - } & Struct; - readonly isHrmpChannelClosing: boolean; - readonly asHrmpChannelClosing: { - readonly initiator: Compact; - readonly sender: Compact; - readonly recipient: Compact; - } & Struct; - readonly isClearOrigin: boolean; - readonly isDescendOrigin: boolean; - readonly asDescendOrigin: StagingXcmV2MultilocationJunctions; - readonly isReportError: boolean; - readonly asReportError: { - readonly queryId: Compact; - readonly dest: StagingXcmV2MultiLocation; - readonly maxResponseWeight: Compact; - } & Struct; - readonly isDepositAsset: boolean; - readonly asDepositAsset: { - readonly assets: StagingXcmV2MultiassetMultiAssetFilter; - readonly maxAssets: Compact; - readonly beneficiary: StagingXcmV2MultiLocation; - } & Struct; - readonly isDepositReserveAsset: boolean; - readonly asDepositReserveAsset: { - readonly assets: StagingXcmV2MultiassetMultiAssetFilter; - readonly maxAssets: Compact; - readonly dest: StagingXcmV2MultiLocation; - readonly xcm: StagingXcmV2Xcm; - } & Struct; - readonly isExchangeAsset: boolean; - readonly asExchangeAsset: { - readonly give: StagingXcmV2MultiassetMultiAssetFilter; - readonly receive: StagingXcmV2MultiassetMultiAssets; - } & Struct; - readonly isInitiateReserveWithdraw: boolean; - readonly asInitiateReserveWithdraw: { - readonly assets: StagingXcmV2MultiassetMultiAssetFilter; - readonly reserve: StagingXcmV2MultiLocation; - readonly xcm: StagingXcmV2Xcm; - } & Struct; - readonly isInitiateTeleport: boolean; - readonly asInitiateTeleport: { - readonly assets: StagingXcmV2MultiassetMultiAssetFilter; - readonly dest: StagingXcmV2MultiLocation; - readonly xcm: StagingXcmV2Xcm; - } & Struct; - readonly isQueryHolding: boolean; - readonly asQueryHolding: { - readonly queryId: Compact; - readonly dest: StagingXcmV2MultiLocation; - readonly assets: StagingXcmV2MultiassetMultiAssetFilter; - readonly maxResponseWeight: Compact; - } & Struct; - readonly isBuyExecution: boolean; - readonly asBuyExecution: { - readonly fees: StagingXcmV2MultiAsset; - readonly weightLimit: StagingXcmV2WeightLimit; - } & Struct; - readonly isRefundSurplus: boolean; - readonly isSetErrorHandler: boolean; - readonly asSetErrorHandler: StagingXcmV2Xcm; - readonly isSetAppendix: boolean; - readonly asSetAppendix: StagingXcmV2Xcm; - readonly isClearError: boolean; - readonly isClaimAsset: boolean; - readonly asClaimAsset: { - readonly assets: StagingXcmV2MultiassetMultiAssets; - readonly ticket: StagingXcmV2MultiLocation; - } & Struct; - readonly isTrap: boolean; - readonly asTrap: Compact; - readonly isSubscribeVersion: boolean; - readonly asSubscribeVersion: { - readonly queryId: Compact; - readonly maxResponseWeight: Compact; - } & Struct; - readonly isUnsubscribeVersion: boolean; - readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion'; - } - - /** @name StagingXcmV2Response (229) */ - interface StagingXcmV2Response extends Enum { - readonly isNull: boolean; - readonly isAssets: boolean; - readonly asAssets: StagingXcmV2MultiassetMultiAssets; - readonly isExecutionResult: boolean; - readonly asExecutionResult: Option>; - readonly isVersion: boolean; - readonly asVersion: u32; - readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version'; - } - - /** @name StagingXcmV2TraitsError (232) */ - interface StagingXcmV2TraitsError extends Enum { - readonly isOverflow: boolean; - readonly isUnimplemented: boolean; - readonly isUntrustedReserveLocation: boolean; - readonly isUntrustedTeleportLocation: boolean; - readonly isMultiLocationFull: boolean; - readonly isMultiLocationNotInvertible: boolean; - readonly isBadOrigin: boolean; - readonly isInvalidLocation: boolean; - readonly isAssetNotFound: boolean; - readonly isFailedToTransactAsset: boolean; - readonly isNotWithdrawable: boolean; - readonly isLocationCannotHold: boolean; - readonly isExceedsMaxMessageSize: boolean; - readonly isDestinationUnsupported: boolean; - readonly isTransport: boolean; - readonly isUnroutable: boolean; - readonly isUnknownClaim: boolean; - readonly isFailedToDecode: boolean; - readonly isMaxWeightInvalid: boolean; - readonly isNotHoldingFees: boolean; - readonly isTooExpensive: boolean; - readonly isTrap: boolean; - readonly asTrap: u64; - readonly isUnhandledXcmVersion: boolean; - readonly isWeightLimitReached: boolean; - readonly asWeightLimitReached: u64; - readonly isBarrier: boolean; - readonly isWeightNotComputable: boolean; - readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'MultiLocationFull' | 'MultiLocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable'; - } - - /** @name StagingXcmV2OriginKind (233) */ - interface StagingXcmV2OriginKind extends Enum { - readonly isNative: boolean; - readonly isSovereignAccount: boolean; - readonly isSuperuser: boolean; - readonly isXcm: boolean; - readonly type: 'Native' | 'SovereignAccount' | 'Superuser' | 'Xcm'; - } - - /** @name StagingXcmDoubleEncoded (234) */ - interface StagingXcmDoubleEncoded extends Struct { - readonly encoded: Bytes; - } - - /** @name StagingXcmV2MultiassetMultiAssetFilter (235) */ - interface StagingXcmV2MultiassetMultiAssetFilter extends Enum { - readonly isDefinite: boolean; - readonly asDefinite: StagingXcmV2MultiassetMultiAssets; - readonly isWild: boolean; - readonly asWild: StagingXcmV2MultiassetWildMultiAsset; - readonly type: 'Definite' | 'Wild'; - } - - /** @name StagingXcmV2MultiassetWildMultiAsset (236) */ - interface StagingXcmV2MultiassetWildMultiAsset extends Enum { - readonly isAll: boolean; - readonly isAllOf: boolean; - readonly asAllOf: { - readonly id: StagingXcmV2MultiassetAssetId; - readonly fun: StagingXcmV2MultiassetWildFungibility; - } & Struct; - readonly type: 'All' | 'AllOf'; - } - - /** @name StagingXcmV2MultiassetWildFungibility (237) */ - interface StagingXcmV2MultiassetWildFungibility extends Enum { - readonly isFungible: boolean; - readonly isNonFungible: boolean; - readonly type: 'Fungible' | 'NonFungible'; - } - - /** @name StagingXcmV2WeightLimit (238) */ - interface StagingXcmV2WeightLimit extends Enum { - readonly isUnlimited: boolean; - readonly isLimited: boolean; - readonly asLimited: Compact; - readonly type: 'Unlimited' | 'Limited'; - } - - /** @name StagingXcmV3Xcm (239) */ - interface StagingXcmV3Xcm extends Vec {} - - /** @name StagingXcmV3Instruction (241) */ - interface StagingXcmV3Instruction extends Enum { - readonly isWithdrawAsset: boolean; - readonly asWithdrawAsset: StagingXcmV3MultiassetMultiAssets; - readonly isReserveAssetDeposited: boolean; - readonly asReserveAssetDeposited: StagingXcmV3MultiassetMultiAssets; - readonly isReceiveTeleportedAsset: boolean; - readonly asReceiveTeleportedAsset: StagingXcmV3MultiassetMultiAssets; - readonly isQueryResponse: boolean; - readonly asQueryResponse: { - readonly queryId: Compact; - readonly response: StagingXcmV3Response; - readonly maxWeight: SpWeightsWeightV2Weight; - readonly querier: Option; - } & Struct; - readonly isTransferAsset: boolean; - readonly asTransferAsset: { - readonly assets: StagingXcmV3MultiassetMultiAssets; - readonly beneficiary: StagingXcmV3MultiLocation; - } & Struct; - readonly isTransferReserveAsset: boolean; - readonly asTransferReserveAsset: { - readonly assets: StagingXcmV3MultiassetMultiAssets; - readonly dest: StagingXcmV3MultiLocation; - readonly xcm: StagingXcmV3Xcm; - } & Struct; - readonly isTransact: boolean; - readonly asTransact: { - readonly originKind: StagingXcmV2OriginKind; - readonly requireWeightAtMost: SpWeightsWeightV2Weight; - readonly call: StagingXcmDoubleEncoded; - } & Struct; - readonly isHrmpNewChannelOpenRequest: boolean; - readonly asHrmpNewChannelOpenRequest: { - readonly sender: Compact; - readonly maxMessageSize: Compact; - readonly maxCapacity: Compact; - } & Struct; - readonly isHrmpChannelAccepted: boolean; - readonly asHrmpChannelAccepted: { - readonly recipient: Compact; - } & Struct; - readonly isHrmpChannelClosing: boolean; - readonly asHrmpChannelClosing: { - readonly initiator: Compact; - readonly sender: Compact; - readonly recipient: Compact; - } & Struct; - readonly isClearOrigin: boolean; - readonly isDescendOrigin: boolean; - readonly asDescendOrigin: StagingXcmV3Junctions; - readonly isReportError: boolean; - readonly asReportError: StagingXcmV3QueryResponseInfo; - readonly isDepositAsset: boolean; - readonly asDepositAsset: { - readonly assets: StagingXcmV3MultiassetMultiAssetFilter; - readonly beneficiary: StagingXcmV3MultiLocation; - } & Struct; - readonly isDepositReserveAsset: boolean; - readonly asDepositReserveAsset: { - readonly assets: StagingXcmV3MultiassetMultiAssetFilter; - readonly dest: StagingXcmV3MultiLocation; - readonly xcm: StagingXcmV3Xcm; - } & Struct; - readonly isExchangeAsset: boolean; - readonly asExchangeAsset: { - readonly give: StagingXcmV3MultiassetMultiAssetFilter; - readonly want: StagingXcmV3MultiassetMultiAssets; - readonly maximal: bool; - } & Struct; - readonly isInitiateReserveWithdraw: boolean; - readonly asInitiateReserveWithdraw: { - readonly assets: StagingXcmV3MultiassetMultiAssetFilter; - readonly reserve: StagingXcmV3MultiLocation; - readonly xcm: StagingXcmV3Xcm; - } & Struct; - readonly isInitiateTeleport: boolean; - readonly asInitiateTeleport: { - readonly assets: StagingXcmV3MultiassetMultiAssetFilter; - readonly dest: StagingXcmV3MultiLocation; - readonly xcm: StagingXcmV3Xcm; - } & Struct; - readonly isReportHolding: boolean; - readonly asReportHolding: { - readonly responseInfo: StagingXcmV3QueryResponseInfo; - readonly assets: StagingXcmV3MultiassetMultiAssetFilter; - } & Struct; - readonly isBuyExecution: boolean; - readonly asBuyExecution: { - readonly fees: StagingXcmV3MultiAsset; - readonly weightLimit: StagingXcmV3WeightLimit; - } & Struct; - readonly isRefundSurplus: boolean; - readonly isSetErrorHandler: boolean; - readonly asSetErrorHandler: StagingXcmV3Xcm; - readonly isSetAppendix: boolean; - readonly asSetAppendix: StagingXcmV3Xcm; - readonly isClearError: boolean; - readonly isClaimAsset: boolean; - readonly asClaimAsset: { - readonly assets: StagingXcmV3MultiassetMultiAssets; - readonly ticket: StagingXcmV3MultiLocation; - } & Struct; - readonly isTrap: boolean; - readonly asTrap: Compact; - readonly isSubscribeVersion: boolean; - readonly asSubscribeVersion: { - readonly queryId: Compact; - readonly maxResponseWeight: SpWeightsWeightV2Weight; - } & Struct; - readonly isUnsubscribeVersion: boolean; - readonly isBurnAsset: boolean; - readonly asBurnAsset: StagingXcmV3MultiassetMultiAssets; - readonly isExpectAsset: boolean; - readonly asExpectAsset: StagingXcmV3MultiassetMultiAssets; - readonly isExpectOrigin: boolean; - readonly asExpectOrigin: Option; - readonly isExpectError: boolean; - readonly asExpectError: Option>; - readonly isExpectTransactStatus: boolean; - readonly asExpectTransactStatus: StagingXcmV3MaybeErrorCode; - readonly isQueryPallet: boolean; - readonly asQueryPallet: { - readonly moduleName: Bytes; - readonly responseInfo: StagingXcmV3QueryResponseInfo; - } & Struct; - readonly isExpectPallet: boolean; - readonly asExpectPallet: { - readonly index: Compact; - readonly name: Bytes; - readonly moduleName: Bytes; - readonly crateMajor: Compact; - readonly minCrateMinor: Compact; - } & Struct; - readonly isReportTransactStatus: boolean; - readonly asReportTransactStatus: StagingXcmV3QueryResponseInfo; - readonly isClearTransactStatus: boolean; - readonly isUniversalOrigin: boolean; - readonly asUniversalOrigin: StagingXcmV3Junction; - readonly isExportMessage: boolean; - readonly asExportMessage: { - readonly network: StagingXcmV3JunctionNetworkId; - readonly destination: StagingXcmV3Junctions; - readonly xcm: StagingXcmV3Xcm; - } & Struct; - readonly isLockAsset: boolean; - readonly asLockAsset: { - readonly asset: StagingXcmV3MultiAsset; - readonly unlocker: StagingXcmV3MultiLocation; - } & Struct; - readonly isUnlockAsset: boolean; - readonly asUnlockAsset: { - readonly asset: StagingXcmV3MultiAsset; - readonly target: StagingXcmV3MultiLocation; - } & Struct; - readonly isNoteUnlockable: boolean; - readonly asNoteUnlockable: { - readonly asset: StagingXcmV3MultiAsset; - readonly owner: StagingXcmV3MultiLocation; - } & Struct; - readonly isRequestUnlock: boolean; - readonly asRequestUnlock: { - readonly asset: StagingXcmV3MultiAsset; - readonly locker: StagingXcmV3MultiLocation; - } & Struct; - readonly isSetFeesMode: boolean; - readonly asSetFeesMode: { - readonly jitWithdraw: bool; - } & Struct; - readonly isSetTopic: boolean; - readonly asSetTopic: U8aFixed; - readonly isClearTopic: boolean; - readonly isAliasOrigin: boolean; - readonly asAliasOrigin: StagingXcmV3MultiLocation; - readonly isUnpaidExecution: boolean; - readonly asUnpaidExecution: { - readonly weightLimit: StagingXcmV3WeightLimit; - readonly checkOrigin: Option; - } & Struct; - readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'ReportHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion' | 'BurnAsset' | 'ExpectAsset' | 'ExpectOrigin' | 'ExpectError' | 'ExpectTransactStatus' | 'QueryPallet' | 'ExpectPallet' | 'ReportTransactStatus' | 'ClearTransactStatus' | 'UniversalOrigin' | 'ExportMessage' | 'LockAsset' | 'UnlockAsset' | 'NoteUnlockable' | 'RequestUnlock' | 'SetFeesMode' | 'SetTopic' | 'ClearTopic' | 'AliasOrigin' | 'UnpaidExecution'; - } - - /** @name StagingXcmV3Response (242) */ - interface StagingXcmV3Response extends Enum { - readonly isNull: boolean; - readonly isAssets: boolean; - readonly asAssets: StagingXcmV3MultiassetMultiAssets; - readonly isExecutionResult: boolean; - readonly asExecutionResult: Option>; - readonly isVersion: boolean; - readonly asVersion: u32; - readonly isPalletsInfo: boolean; - readonly asPalletsInfo: Vec; - readonly isDispatchResult: boolean; - readonly asDispatchResult: StagingXcmV3MaybeErrorCode; - readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version' | 'PalletsInfo' | 'DispatchResult'; - } - - /** @name StagingXcmV3TraitsError (245) */ - interface StagingXcmV3TraitsError extends Enum { - readonly isOverflow: boolean; - readonly isUnimplemented: boolean; - readonly isUntrustedReserveLocation: boolean; - readonly isUntrustedTeleportLocation: boolean; - readonly isLocationFull: boolean; - readonly isLocationNotInvertible: boolean; - readonly isBadOrigin: boolean; - readonly isInvalidLocation: boolean; - readonly isAssetNotFound: boolean; - readonly isFailedToTransactAsset: boolean; - readonly isNotWithdrawable: boolean; - readonly isLocationCannotHold: boolean; - readonly isExceedsMaxMessageSize: boolean; - readonly isDestinationUnsupported: boolean; - readonly isTransport: boolean; - readonly isUnroutable: boolean; - readonly isUnknownClaim: boolean; - readonly isFailedToDecode: boolean; - readonly isMaxWeightInvalid: boolean; - readonly isNotHoldingFees: boolean; - readonly isTooExpensive: boolean; - readonly isTrap: boolean; - readonly asTrap: u64; - readonly isExpectationFalse: boolean; - readonly isPalletNotFound: boolean; - readonly isNameMismatch: boolean; - readonly isVersionIncompatible: boolean; - readonly isHoldingWouldOverflow: boolean; - readonly isExportError: boolean; - readonly isReanchorFailed: boolean; - readonly isNoDeal: boolean; - readonly isFeesNotMet: boolean; - readonly isLockError: boolean; - readonly isNoPermission: boolean; - readonly isUnanchored: boolean; - readonly isNotDepositable: boolean; - readonly isUnhandledXcmVersion: boolean; - readonly isWeightLimitReached: boolean; - readonly asWeightLimitReached: SpWeightsWeightV2Weight; - readonly isBarrier: boolean; - readonly isWeightNotComputable: boolean; - readonly isExceedsStackLimit: boolean; - readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'LocationFull' | 'LocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'ExpectationFalse' | 'PalletNotFound' | 'NameMismatch' | 'VersionIncompatible' | 'HoldingWouldOverflow' | 'ExportError' | 'ReanchorFailed' | 'NoDeal' | 'FeesNotMet' | 'LockError' | 'NoPermission' | 'Unanchored' | 'NotDepositable' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable' | 'ExceedsStackLimit'; - } - - /** @name StagingXcmV3PalletInfo (247) */ - interface StagingXcmV3PalletInfo extends Struct { - readonly index: Compact; - readonly name: Bytes; - readonly moduleName: Bytes; - readonly major: Compact; - readonly minor: Compact; - readonly patch: Compact; - } - - /** @name StagingXcmV3MaybeErrorCode (250) */ - interface StagingXcmV3MaybeErrorCode extends Enum { - readonly isSuccess: boolean; - readonly isError: boolean; - readonly asError: Bytes; - readonly isTruncatedError: boolean; - readonly asTruncatedError: Bytes; - readonly type: 'Success' | 'Error' | 'TruncatedError'; - } - - /** @name StagingXcmV3QueryResponseInfo (253) */ - interface StagingXcmV3QueryResponseInfo extends Struct { - readonly destination: StagingXcmV3MultiLocation; - readonly queryId: Compact; - readonly maxWeight: SpWeightsWeightV2Weight; - } - - /** @name StagingXcmV3MultiassetMultiAssetFilter (254) */ - interface StagingXcmV3MultiassetMultiAssetFilter extends Enum { - readonly isDefinite: boolean; - readonly asDefinite: StagingXcmV3MultiassetMultiAssets; - readonly isWild: boolean; - readonly asWild: StagingXcmV3MultiassetWildMultiAsset; - readonly type: 'Definite' | 'Wild'; - } - - /** @name StagingXcmV3MultiassetWildMultiAsset (255) */ - interface StagingXcmV3MultiassetWildMultiAsset extends Enum { - readonly isAll: boolean; - readonly isAllOf: boolean; - readonly asAllOf: { - readonly id: StagingXcmV3MultiassetAssetId; - readonly fun: StagingXcmV3MultiassetWildFungibility; - } & Struct; - readonly isAllCounted: boolean; - readonly asAllCounted: Compact; - readonly isAllOfCounted: boolean; - readonly asAllOfCounted: { - readonly id: StagingXcmV3MultiassetAssetId; - readonly fun: StagingXcmV3MultiassetWildFungibility; - readonly count: Compact; - } & Struct; - readonly type: 'All' | 'AllOf' | 'AllCounted' | 'AllOfCounted'; - } - - /** @name StagingXcmV3MultiassetWildFungibility (256) */ - interface StagingXcmV3MultiassetWildFungibility extends Enum { - readonly isFungible: boolean; - readonly isNonFungible: boolean; - readonly type: 'Fungible' | 'NonFungible'; - } - - /** @name CumulusPalletXcmCall (265) */ - type CumulusPalletXcmCall = Null; - - /** @name CumulusPalletDmpQueueCall (266) */ - interface CumulusPalletDmpQueueCall extends Enum { - readonly isServiceOverweight: boolean; - readonly asServiceOverweight: { - readonly index: u64; - readonly weightLimit: SpWeightsWeightV2Weight; - } & Struct; - readonly type: 'ServiceOverweight'; - } - - /** @name PalletInflationCall (267) */ - interface PalletInflationCall extends Enum { - readonly isStartInflation: boolean; - readonly asStartInflation: { - readonly inflationStartRelayBlock: u32; - } & Struct; - readonly type: 'StartInflation'; - } - - /** @name PalletUniqueCall (268) */ - interface PalletUniqueCall extends Enum { - readonly isCreateCollection: boolean; - readonly asCreateCollection: { - readonly collectionName: Vec; - readonly collectionDescription: Vec; - readonly tokenPrefix: Bytes; - readonly mode: UpDataStructsCollectionMode; - } & Struct; - readonly isCreateCollectionEx: boolean; - readonly asCreateCollectionEx: { - readonly data: UpDataStructsCreateCollectionData; - } & Struct; - readonly isDestroyCollection: boolean; - readonly asDestroyCollection: { - readonly collectionId: u32; - } & Struct; - readonly isAddToAllowList: boolean; - readonly asAddToAllowList: { - readonly collectionId: u32; - readonly address: PalletEvmAccountBasicCrossAccountIdRepr; - } & Struct; - readonly isRemoveFromAllowList: boolean; - readonly asRemoveFromAllowList: { - readonly collectionId: u32; - readonly address: PalletEvmAccountBasicCrossAccountIdRepr; - } & Struct; - readonly isChangeCollectionOwner: boolean; - readonly asChangeCollectionOwner: { - readonly collectionId: u32; - readonly newOwner: AccountId32; - } & Struct; - readonly isAddCollectionAdmin: boolean; - readonly asAddCollectionAdmin: { - readonly collectionId: u32; - readonly newAdminId: PalletEvmAccountBasicCrossAccountIdRepr; - } & Struct; - readonly isRemoveCollectionAdmin: boolean; - readonly asRemoveCollectionAdmin: { - readonly collectionId: u32; - readonly accountId: PalletEvmAccountBasicCrossAccountIdRepr; - } & Struct; - readonly isSetCollectionSponsor: boolean; - readonly asSetCollectionSponsor: { - readonly collectionId: u32; - readonly newSponsor: AccountId32; - } & Struct; - readonly isConfirmSponsorship: boolean; - readonly asConfirmSponsorship: { - readonly collectionId: u32; - } & Struct; - readonly isRemoveCollectionSponsor: boolean; - readonly asRemoveCollectionSponsor: { - readonly collectionId: u32; - } & Struct; - readonly isCreateItem: boolean; - readonly asCreateItem: { - readonly collectionId: u32; - readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; - readonly data: UpDataStructsCreateItemData; - } & Struct; - readonly isCreateMultipleItems: boolean; - readonly asCreateMultipleItems: { - readonly collectionId: u32; - readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; - readonly itemsData: Vec; - } & Struct; - readonly isSetCollectionProperties: boolean; - readonly asSetCollectionProperties: { - readonly collectionId: u32; - readonly properties: Vec; - } & Struct; - readonly isDeleteCollectionProperties: boolean; - readonly asDeleteCollectionProperties: { - readonly collectionId: u32; - readonly propertyKeys: Vec; - } & Struct; - readonly isSetTokenProperties: boolean; - readonly asSetTokenProperties: { - readonly collectionId: u32; - readonly tokenId: u32; - readonly properties: Vec; - } & Struct; - readonly isDeleteTokenProperties: boolean; - readonly asDeleteTokenProperties: { - readonly collectionId: u32; - readonly tokenId: u32; - readonly propertyKeys: Vec; - } & Struct; - readonly isSetTokenPropertyPermissions: boolean; - readonly asSetTokenPropertyPermissions: { - readonly collectionId: u32; - readonly propertyPermissions: Vec; - } & Struct; - readonly isCreateMultipleItemsEx: boolean; - readonly asCreateMultipleItemsEx: { - readonly collectionId: u32; - readonly data: UpDataStructsCreateItemExData; - } & Struct; - readonly isSetTransfersEnabledFlag: boolean; - readonly asSetTransfersEnabledFlag: { - readonly collectionId: u32; - readonly value: bool; - } & Struct; - readonly isBurnItem: boolean; - readonly asBurnItem: { - readonly collectionId: u32; - readonly itemId: u32; - readonly value: u128; - } & Struct; - readonly isBurnFrom: boolean; - readonly asBurnFrom: { - readonly collectionId: u32; - readonly from: PalletEvmAccountBasicCrossAccountIdRepr; - readonly itemId: u32; - readonly value: u128; - } & Struct; - readonly isTransfer: boolean; - readonly asTransfer: { - readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr; - readonly collectionId: u32; - readonly itemId: u32; - readonly value: u128; - } & Struct; - readonly isApprove: boolean; - readonly asApprove: { - readonly spender: PalletEvmAccountBasicCrossAccountIdRepr; - readonly collectionId: u32; - readonly itemId: u32; - readonly amount: u128; - } & Struct; - readonly isApproveFrom: boolean; - readonly asApproveFrom: { - readonly from: PalletEvmAccountBasicCrossAccountIdRepr; - readonly to: PalletEvmAccountBasicCrossAccountIdRepr; - readonly collectionId: u32; - readonly itemId: u32; - readonly amount: u128; - } & Struct; - readonly isTransferFrom: boolean; - readonly asTransferFrom: { - readonly from: PalletEvmAccountBasicCrossAccountIdRepr; - readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr; - readonly collectionId: u32; - readonly itemId: u32; - readonly value: u128; - } & Struct; - readonly isSetCollectionLimits: boolean; - readonly asSetCollectionLimits: { - readonly collectionId: u32; - readonly newLimit: UpDataStructsCollectionLimits; - } & Struct; - readonly isSetCollectionPermissions: boolean; - readonly asSetCollectionPermissions: { - readonly collectionId: u32; - readonly newPermission: UpDataStructsCollectionPermissions; - } & Struct; - readonly isRepartition: boolean; - readonly asRepartition: { - readonly collectionId: u32; - readonly tokenId: u32; - readonly amount: u128; - } & Struct; - readonly isSetAllowanceForAll: boolean; - readonly asSetAllowanceForAll: { - readonly collectionId: u32; - readonly operator: PalletEvmAccountBasicCrossAccountIdRepr; - readonly approve: bool; - } & Struct; - readonly isForceRepairCollection: boolean; - readonly asForceRepairCollection: { - readonly collectionId: u32; - } & Struct; - readonly isForceRepairItem: boolean; - readonly asForceRepairItem: { - readonly collectionId: u32; - readonly itemId: u32; - } & Struct; - readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'ApproveFrom' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem'; - } - - /** @name UpDataStructsCollectionMode (273) */ - interface UpDataStructsCollectionMode extends Enum { - readonly isNft: boolean; - readonly isFungible: boolean; - readonly asFungible: u8; - readonly isReFungible: boolean; - readonly type: 'Nft' | 'Fungible' | 'ReFungible'; - } - - /** @name UpDataStructsCreateCollectionData (274) */ - interface UpDataStructsCreateCollectionData extends Struct { - readonly mode: UpDataStructsCollectionMode; - readonly access: Option; - readonly name: Vec; - readonly description: Vec; - readonly tokenPrefix: Bytes; - readonly limits: Option; - readonly permissions: Option; - readonly tokenPropertyPermissions: Vec; - readonly properties: Vec; - readonly adminList: Vec; - readonly pendingSponsor: Option; - readonly flags: U8aFixed; - } - - /** @name PalletEvmAccountBasicCrossAccountIdRepr (275) */ - interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum { - readonly isSubstrate: boolean; - readonly asSubstrate: AccountId32; - readonly isEthereum: boolean; - readonly asEthereum: H160; - readonly type: 'Substrate' | 'Ethereum'; - } - - /** @name UpDataStructsAccessMode (277) */ - interface UpDataStructsAccessMode extends Enum { - readonly isNormal: boolean; - readonly isAllowList: boolean; - readonly type: 'Normal' | 'AllowList'; - } - - /** @name UpDataStructsCollectionLimits (279) */ - interface UpDataStructsCollectionLimits extends Struct { - readonly accountTokenOwnershipLimit: Option; - readonly sponsoredDataSize: Option; - readonly sponsoredDataRateLimit: Option; - readonly tokenLimit: Option; - readonly sponsorTransferTimeout: Option; - readonly sponsorApproveTimeout: Option; - readonly ownerCanTransfer: Option; - readonly ownerCanDestroy: Option; - readonly transfersEnabled: Option; - } - - /** @name UpDataStructsSponsoringRateLimit (281) */ - interface UpDataStructsSponsoringRateLimit extends Enum { - readonly isSponsoringDisabled: boolean; - readonly isBlocks: boolean; - readonly asBlocks: u32; - readonly type: 'SponsoringDisabled' | 'Blocks'; - } - - /** @name UpDataStructsCollectionPermissions (284) */ - interface UpDataStructsCollectionPermissions extends Struct { - readonly access: Option; - readonly mintMode: Option; - readonly nesting: Option; - } - - /** @name UpDataStructsNestingPermissions (286) */ - interface UpDataStructsNestingPermissions extends Struct { - readonly tokenOwner: bool; - readonly collectionAdmin: bool; - readonly restricted: Option; - } - - /** @name UpDataStructsOwnerRestrictedSet (288) */ - interface UpDataStructsOwnerRestrictedSet extends BTreeSet {} - - /** @name UpDataStructsPropertyKeyPermission (294) */ - interface UpDataStructsPropertyKeyPermission extends Struct { - readonly key: Bytes; - readonly permission: UpDataStructsPropertyPermission; - } - - /** @name UpDataStructsPropertyPermission (296) */ - interface UpDataStructsPropertyPermission extends Struct { - readonly mutable: bool; - readonly collectionAdmin: bool; - readonly tokenOwner: bool; - } - - /** @name UpDataStructsProperty (299) */ - interface UpDataStructsProperty extends Struct { - readonly key: Bytes; - readonly value: Bytes; - } - - /** @name UpDataStructsCreateItemData (304) */ - interface UpDataStructsCreateItemData extends Enum { - readonly isNft: boolean; - readonly asNft: UpDataStructsCreateNftData; - readonly isFungible: boolean; - readonly asFungible: UpDataStructsCreateFungibleData; - readonly isReFungible: boolean; - readonly asReFungible: UpDataStructsCreateReFungibleData; - readonly type: 'Nft' | 'Fungible' | 'ReFungible'; - } - - /** @name UpDataStructsCreateNftData (305) */ - interface UpDataStructsCreateNftData extends Struct { - readonly properties: Vec; - } - - /** @name UpDataStructsCreateFungibleData (306) */ - interface UpDataStructsCreateFungibleData extends Struct { - readonly value: u128; - } - - /** @name UpDataStructsCreateReFungibleData (307) */ - interface UpDataStructsCreateReFungibleData extends Struct { - readonly pieces: u128; - readonly properties: Vec; - } - - /** @name UpDataStructsCreateItemExData (311) */ - interface UpDataStructsCreateItemExData extends Enum { - readonly isNft: boolean; - readonly asNft: Vec; - readonly isFungible: boolean; - readonly asFungible: BTreeMap; - readonly isRefungibleMultipleItems: boolean; - readonly asRefungibleMultipleItems: Vec; - readonly isRefungibleMultipleOwners: boolean; - readonly asRefungibleMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners; - readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners'; - } - - /** @name UpDataStructsCreateNftExData (313) */ - interface UpDataStructsCreateNftExData extends Struct { - readonly properties: Vec; - readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; - } - - /** @name UpDataStructsCreateRefungibleExSingleOwner (320) */ - interface UpDataStructsCreateRefungibleExSingleOwner extends Struct { - readonly user: PalletEvmAccountBasicCrossAccountIdRepr; - readonly pieces: u128; - readonly properties: Vec; - } - - /** @name UpDataStructsCreateRefungibleExMultipleOwners (322) */ - interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct { - readonly users: BTreeMap; - readonly properties: Vec; - } - - /** @name PalletConfigurationCall (323) */ - interface PalletConfigurationCall extends Enum { - readonly isSetWeightToFeeCoefficientOverride: boolean; - readonly asSetWeightToFeeCoefficientOverride: { - readonly coeff: Option; - } & Struct; - readonly isSetMinGasPriceOverride: boolean; - readonly asSetMinGasPriceOverride: { - readonly coeff: Option; - } & Struct; - readonly isSetAppPromotionConfigurationOverride: boolean; - readonly asSetAppPromotionConfigurationOverride: { - readonly configuration: PalletConfigurationAppPromotionConfiguration; - } & Struct; - readonly isSetCollatorSelectionDesiredCollators: boolean; - readonly asSetCollatorSelectionDesiredCollators: { - readonly max: Option; - } & Struct; - readonly isSetCollatorSelectionLicenseBond: boolean; - readonly asSetCollatorSelectionLicenseBond: { - readonly amount: Option; - } & Struct; - readonly isSetCollatorSelectionKickThreshold: boolean; - readonly asSetCollatorSelectionKickThreshold: { - readonly threshold: Option; - } & Struct; - readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetAppPromotionConfigurationOverride' | 'SetCollatorSelectionDesiredCollators' | 'SetCollatorSelectionLicenseBond' | 'SetCollatorSelectionKickThreshold'; - } - - /** @name PalletConfigurationAppPromotionConfiguration (325) */ - interface PalletConfigurationAppPromotionConfiguration extends Struct { - readonly recalculationInterval: Option; - readonly pendingInterval: Option; - readonly intervalIncome: Option; - readonly maxStakersPerCalculation: Option; - } - - /** @name PalletStructureCall (330) */ - type PalletStructureCall = Null; - - /** @name PalletAppPromotionCall (331) */ - interface PalletAppPromotionCall extends Enum { - readonly isSetAdminAddress: boolean; - readonly asSetAdminAddress: { - readonly admin: PalletEvmAccountBasicCrossAccountIdRepr; - } & Struct; - readonly isStake: boolean; - readonly asStake: { - readonly amount: u128; - } & Struct; - readonly isUnstakeAll: boolean; - readonly isSponsorCollection: boolean; - readonly asSponsorCollection: { - readonly collectionId: u32; - } & Struct; - readonly isStopSponsoringCollection: boolean; - readonly asStopSponsoringCollection: { - readonly collectionId: u32; - } & Struct; - readonly isSponsorContract: boolean; - readonly asSponsorContract: { - readonly contractId: H160; - } & Struct; - readonly isStopSponsoringContract: boolean; - readonly asStopSponsoringContract: { - readonly contractId: H160; - } & Struct; - readonly isPayoutStakers: boolean; - readonly asPayoutStakers: { - readonly stakersNumber: Option; - } & Struct; - readonly isUnstakePartial: boolean; - readonly asUnstakePartial: { - readonly amount: u128; - } & Struct; - readonly isForceUnstake: boolean; - readonly asForceUnstake: { - readonly pendingBlocks: Vec; - } & Struct; - readonly type: 'SetAdminAddress' | 'Stake' | 'UnstakeAll' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers' | 'UnstakePartial' | 'ForceUnstake'; - } - - /** @name PalletForeignAssetsModuleCall (333) */ - interface PalletForeignAssetsModuleCall extends Enum { - readonly isRegisterForeignAsset: boolean; - readonly asRegisterForeignAsset: { - readonly owner: AccountId32; - readonly location: StagingXcmVersionedMultiLocation; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly isUpdateForeignAsset: boolean; - readonly asUpdateForeignAsset: { - readonly foreignAssetId: u32; - readonly location: StagingXcmVersionedMultiLocation; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset'; - } - - /** @name PalletForeignAssetsModuleAssetMetadata (334) */ - interface PalletForeignAssetsModuleAssetMetadata extends Struct { - readonly name: Bytes; - readonly symbol: Bytes; - readonly decimals: u8; - readonly minimalBalance: u128; - } - - /** @name PalletEvmCall (337) */ - interface PalletEvmCall extends Enum { - readonly isWithdraw: boolean; - readonly asWithdraw: { - readonly address: H160; - readonly value: u128; - } & Struct; - readonly isCall: boolean; - readonly asCall: { - readonly source: H160; - readonly target: H160; - readonly input: Bytes; - readonly value: U256; - readonly gasLimit: u64; - readonly maxFeePerGas: U256; - readonly maxPriorityFeePerGas: Option; - readonly nonce: Option; - readonly accessList: Vec]>>; - } & Struct; - readonly isCreate: boolean; - readonly asCreate: { - readonly source: H160; - readonly init: Bytes; - readonly value: U256; - readonly gasLimit: u64; - readonly maxFeePerGas: U256; - readonly maxPriorityFeePerGas: Option; - readonly nonce: Option; - readonly accessList: Vec]>>; - } & Struct; - readonly isCreate2: boolean; - readonly asCreate2: { - readonly source: H160; - readonly init: Bytes; - readonly salt: H256; - readonly value: U256; - readonly gasLimit: u64; - readonly maxFeePerGas: U256; - readonly maxPriorityFeePerGas: Option; - readonly nonce: Option; - readonly accessList: Vec]>>; - } & Struct; - readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2'; - } - - /** @name PalletEthereumCall (344) */ - interface PalletEthereumCall extends Enum { - readonly isTransact: boolean; - readonly asTransact: { - readonly transaction: EthereumTransactionTransactionV2; - } & Struct; - readonly type: 'Transact'; - } - - /** @name EthereumTransactionTransactionV2 (345) */ - interface EthereumTransactionTransactionV2 extends Enum { - readonly isLegacy: boolean; - readonly asLegacy: EthereumTransactionLegacyTransaction; - readonly isEip2930: boolean; - readonly asEip2930: EthereumTransactionEip2930Transaction; - readonly isEip1559: boolean; - readonly asEip1559: EthereumTransactionEip1559Transaction; - readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; - } - - /** @name EthereumTransactionLegacyTransaction (346) */ - interface EthereumTransactionLegacyTransaction extends Struct { - readonly nonce: U256; - readonly gasPrice: U256; - readonly gasLimit: U256; - readonly action: EthereumTransactionTransactionAction; - readonly value: U256; - readonly input: Bytes; - readonly signature: EthereumTransactionTransactionSignature; - } - - /** @name EthereumTransactionTransactionAction (347) */ - interface EthereumTransactionTransactionAction extends Enum { - readonly isCall: boolean; - readonly asCall: H160; - readonly isCreate: boolean; - readonly type: 'Call' | 'Create'; - } - - /** @name EthereumTransactionTransactionSignature (348) */ - interface EthereumTransactionTransactionSignature extends Struct { - readonly v: u64; - readonly r: H256; - readonly s: H256; - } - - /** @name EthereumTransactionEip2930Transaction (350) */ - interface EthereumTransactionEip2930Transaction extends Struct { - readonly chainId: u64; - readonly nonce: U256; - readonly gasPrice: U256; - readonly gasLimit: U256; - readonly action: EthereumTransactionTransactionAction; - readonly value: U256; - readonly input: Bytes; - readonly accessList: Vec; - readonly oddYParity: bool; - readonly r: H256; - readonly s: H256; - } - - /** @name EthereumTransactionAccessListItem (352) */ - interface EthereumTransactionAccessListItem extends Struct { - readonly address: H160; - readonly storageKeys: Vec; - } - - /** @name EthereumTransactionEip1559Transaction (353) */ - interface EthereumTransactionEip1559Transaction extends Struct { - readonly chainId: u64; - readonly nonce: U256; - readonly maxPriorityFeePerGas: U256; - readonly maxFeePerGas: U256; - readonly gasLimit: U256; - readonly action: EthereumTransactionTransactionAction; - readonly value: U256; - readonly input: Bytes; - readonly accessList: Vec; - readonly oddYParity: bool; - readonly r: H256; - readonly s: H256; - } - - /** @name PalletEvmContractHelpersCall (354) */ - interface PalletEvmContractHelpersCall extends Enum { - readonly isMigrateFromSelfSponsoring: boolean; - readonly asMigrateFromSelfSponsoring: { - readonly addresses: Vec; - } & Struct; - readonly type: 'MigrateFromSelfSponsoring'; - } - - /** @name PalletEvmMigrationCall (356) */ - interface PalletEvmMigrationCall extends Enum { - readonly isBegin: boolean; - readonly asBegin: { - readonly address: H160; - } & Struct; - readonly isSetData: boolean; - readonly asSetData: { - readonly address: H160; - readonly data: Vec>; - } & Struct; - readonly isFinish: boolean; - readonly asFinish: { - readonly address: H160; - readonly code: Bytes; - } & Struct; - readonly isInsertEthLogs: boolean; - readonly asInsertEthLogs: { - readonly logs: Vec; - } & Struct; - readonly isInsertEvents: boolean; - readonly asInsertEvents: { - readonly events: Vec; - } & Struct; - readonly isRemoveRmrkData: boolean; - readonly type: 'Begin' | 'SetData' | 'Finish' | 'InsertEthLogs' | 'InsertEvents' | 'RemoveRmrkData'; - } - - /** @name EthereumLog (360) */ - interface EthereumLog extends Struct { - readonly address: H160; - readonly topics: Vec; - readonly data: Bytes; - } - - /** @name PalletMaintenanceCall (361) */ - interface PalletMaintenanceCall extends Enum { - readonly isEnable: boolean; - readonly isDisable: boolean; - readonly type: 'Enable' | 'Disable'; - } - - /** @name PalletUtilityCall (362) */ - interface PalletUtilityCall extends Enum { - readonly isBatch: boolean; - readonly asBatch: { - readonly calls: Vec; - } & Struct; - readonly isAsDerivative: boolean; - readonly asAsDerivative: { - readonly index: u16; - readonly call: Call; - } & Struct; - readonly isBatchAll: boolean; - readonly asBatchAll: { - readonly calls: Vec; - } & Struct; - readonly isDispatchAs: boolean; - readonly asDispatchAs: { - readonly asOrigin: OpalRuntimeOriginCaller; - readonly call: Call; - } & Struct; - readonly isForceBatch: boolean; - readonly asForceBatch: { - readonly calls: Vec; - } & Struct; - readonly isWithWeight: boolean; - readonly asWithWeight: { - readonly call: Call; - readonly weight: SpWeightsWeightV2Weight; - } & Struct; - readonly type: 'Batch' | 'AsDerivative' | 'BatchAll' | 'DispatchAs' | 'ForceBatch' | 'WithWeight'; - } - - /** @name PalletTestUtilsCall (364) */ - interface PalletTestUtilsCall extends Enum { - readonly isEnable: boolean; - readonly isSetTestValue: boolean; - readonly asSetTestValue: { - readonly value: u32; - } & Struct; - readonly isSetTestValueAndRollback: boolean; - readonly asSetTestValueAndRollback: { - readonly value: u32; - } & Struct; - readonly isIncTestValue: boolean; - readonly isJustTakeFee: boolean; - readonly isBatchAll: boolean; - readonly asBatchAll: { - readonly calls: Vec; - } & Struct; - readonly type: 'Enable' | 'SetTestValue' | 'SetTestValueAndRollback' | 'IncTestValue' | 'JustTakeFee' | 'BatchAll'; - } - - /** @name PalletSchedulerEvent (366) */ - interface PalletSchedulerEvent extends Enum { - readonly isScheduled: boolean; - readonly asScheduled: { - readonly when: u32; - readonly index: u32; - } & Struct; - readonly isCanceled: boolean; - readonly asCanceled: { - readonly when: u32; - readonly index: u32; - } & Struct; - readonly isDispatched: boolean; - readonly asDispatched: { - readonly task: ITuple<[u32, u32]>; - readonly id: Option; - readonly result: Result; - } & Struct; - readonly isCallUnavailable: boolean; - readonly asCallUnavailable: { - readonly task: ITuple<[u32, u32]>; - readonly id: Option; - } & Struct; - readonly isPeriodicFailed: boolean; - readonly asPeriodicFailed: { - readonly task: ITuple<[u32, u32]>; - readonly id: Option; - } & Struct; - readonly isPermanentlyOverweight: boolean; - readonly asPermanentlyOverweight: { - readonly task: ITuple<[u32, u32]>; - readonly id: Option; - } & Struct; - readonly type: 'Scheduled' | 'Canceled' | 'Dispatched' | 'CallUnavailable' | 'PeriodicFailed' | 'PermanentlyOverweight'; - } - - /** @name CumulusPalletXcmpQueueEvent (367) */ - interface CumulusPalletXcmpQueueEvent extends Enum { - readonly isSuccess: boolean; - readonly asSuccess: { - readonly messageHash: U8aFixed; - readonly messageId: U8aFixed; - readonly weight: SpWeightsWeightV2Weight; - } & Struct; - readonly isFail: boolean; - readonly asFail: { - readonly messageHash: U8aFixed; - readonly messageId: U8aFixed; - readonly error: StagingXcmV3TraitsError; - readonly weight: SpWeightsWeightV2Weight; - } & Struct; - readonly isBadVersion: boolean; - readonly asBadVersion: { - readonly messageHash: U8aFixed; - } & Struct; - readonly isBadFormat: boolean; - readonly asBadFormat: { - readonly messageHash: U8aFixed; - } & Struct; - readonly isXcmpMessageSent: boolean; - readonly asXcmpMessageSent: { - readonly messageHash: U8aFixed; - } & Struct; - readonly isOverweightEnqueued: boolean; - readonly asOverweightEnqueued: { - readonly sender: u32; - readonly sentAt: u32; - readonly index: u64; - readonly required: SpWeightsWeightV2Weight; - } & Struct; - readonly isOverweightServiced: boolean; - readonly asOverweightServiced: { - readonly index: u64; - readonly used: SpWeightsWeightV2Weight; - } & Struct; - readonly type: 'Success' | 'Fail' | 'BadVersion' | 'BadFormat' | 'XcmpMessageSent' | 'OverweightEnqueued' | 'OverweightServiced'; - } - - /** @name PalletXcmEvent (368) */ - interface PalletXcmEvent extends Enum { - readonly isAttempted: boolean; - readonly asAttempted: { - readonly outcome: StagingXcmV3TraitsOutcome; - } & Struct; - readonly isSent: boolean; - readonly asSent: { - readonly origin: StagingXcmV3MultiLocation; - readonly destination: StagingXcmV3MultiLocation; - readonly message: StagingXcmV3Xcm; - readonly messageId: U8aFixed; - } & Struct; - readonly isUnexpectedResponse: boolean; - readonly asUnexpectedResponse: { - readonly origin: StagingXcmV3MultiLocation; - readonly queryId: u64; - } & Struct; - readonly isResponseReady: boolean; - readonly asResponseReady: { - readonly queryId: u64; - readonly response: StagingXcmV3Response; - } & Struct; - readonly isNotified: boolean; - readonly asNotified: { - readonly queryId: u64; - readonly palletIndex: u8; - readonly callIndex: u8; - } & Struct; - readonly isNotifyOverweight: boolean; - readonly asNotifyOverweight: { - readonly queryId: u64; - readonly palletIndex: u8; - readonly callIndex: u8; - readonly actualWeight: SpWeightsWeightV2Weight; - readonly maxBudgetedWeight: SpWeightsWeightV2Weight; - } & Struct; - readonly isNotifyDispatchError: boolean; - readonly asNotifyDispatchError: { - readonly queryId: u64; - readonly palletIndex: u8; - readonly callIndex: u8; - } & Struct; - readonly isNotifyDecodeFailed: boolean; - readonly asNotifyDecodeFailed: { - readonly queryId: u64; - readonly palletIndex: u8; - readonly callIndex: u8; - } & Struct; - readonly isInvalidResponder: boolean; - readonly asInvalidResponder: { - readonly origin: StagingXcmV3MultiLocation; - readonly queryId: u64; - readonly expectedLocation: Option; - } & Struct; - readonly isInvalidResponderVersion: boolean; - readonly asInvalidResponderVersion: { - readonly origin: StagingXcmV3MultiLocation; - readonly queryId: u64; - } & Struct; - readonly isResponseTaken: boolean; - readonly asResponseTaken: { - readonly queryId: u64; - } & Struct; - readonly isAssetsTrapped: boolean; - readonly asAssetsTrapped: { - readonly hash_: H256; - readonly origin: StagingXcmV3MultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - } & Struct; - readonly isVersionChangeNotified: boolean; - readonly asVersionChangeNotified: { - readonly destination: StagingXcmV3MultiLocation; - readonly result: u32; - readonly cost: StagingXcmV3MultiassetMultiAssets; - readonly messageId: U8aFixed; - } & Struct; - readonly isSupportedVersionChanged: boolean; - readonly asSupportedVersionChanged: { - readonly location: StagingXcmV3MultiLocation; - readonly version: u32; - } & Struct; - readonly isNotifyTargetSendFail: boolean; - readonly asNotifyTargetSendFail: { - readonly location: StagingXcmV3MultiLocation; - readonly queryId: u64; - readonly error: StagingXcmV3TraitsError; - } & Struct; - readonly isNotifyTargetMigrationFail: boolean; - readonly asNotifyTargetMigrationFail: { - readonly location: StagingXcmVersionedMultiLocation; - readonly queryId: u64; - } & Struct; - readonly isInvalidQuerierVersion: boolean; - readonly asInvalidQuerierVersion: { - readonly origin: StagingXcmV3MultiLocation; - readonly queryId: u64; - } & Struct; - readonly isInvalidQuerier: boolean; - readonly asInvalidQuerier: { - readonly origin: StagingXcmV3MultiLocation; - readonly queryId: u64; - readonly expectedQuerier: StagingXcmV3MultiLocation; - readonly maybeActualQuerier: Option; - } & Struct; - readonly isVersionNotifyStarted: boolean; - readonly asVersionNotifyStarted: { - readonly destination: StagingXcmV3MultiLocation; - readonly cost: StagingXcmV3MultiassetMultiAssets; - readonly messageId: U8aFixed; - } & Struct; - readonly isVersionNotifyRequested: boolean; - readonly asVersionNotifyRequested: { - readonly destination: StagingXcmV3MultiLocation; - readonly cost: StagingXcmV3MultiassetMultiAssets; - readonly messageId: U8aFixed; - } & Struct; - readonly isVersionNotifyUnrequested: boolean; - readonly asVersionNotifyUnrequested: { - readonly destination: StagingXcmV3MultiLocation; - readonly cost: StagingXcmV3MultiassetMultiAssets; - readonly messageId: U8aFixed; - } & Struct; - readonly isFeesPaid: boolean; - readonly asFeesPaid: { - readonly paying: StagingXcmV3MultiLocation; - readonly fees: StagingXcmV3MultiassetMultiAssets; - } & Struct; - readonly isAssetsClaimed: boolean; - readonly asAssetsClaimed: { - readonly hash_: H256; - readonly origin: StagingXcmV3MultiLocation; - readonly assets: StagingXcmVersionedMultiAssets; - } & Struct; - readonly type: 'Attempted' | 'Sent' | 'UnexpectedResponse' | 'ResponseReady' | 'Notified' | 'NotifyOverweight' | 'NotifyDispatchError' | 'NotifyDecodeFailed' | 'InvalidResponder' | 'InvalidResponderVersion' | 'ResponseTaken' | 'AssetsTrapped' | 'VersionChangeNotified' | 'SupportedVersionChanged' | 'NotifyTargetSendFail' | 'NotifyTargetMigrationFail' | 'InvalidQuerierVersion' | 'InvalidQuerier' | 'VersionNotifyStarted' | 'VersionNotifyRequested' | 'VersionNotifyUnrequested' | 'FeesPaid' | 'AssetsClaimed'; - } - - /** @name StagingXcmV3TraitsOutcome (369) */ - interface StagingXcmV3TraitsOutcome extends Enum { - readonly isComplete: boolean; - readonly asComplete: SpWeightsWeightV2Weight; - readonly isIncomplete: boolean; - readonly asIncomplete: ITuple<[SpWeightsWeightV2Weight, StagingXcmV3TraitsError]>; - readonly isError: boolean; - readonly asError: StagingXcmV3TraitsError; - readonly type: 'Complete' | 'Incomplete' | 'Error'; - } - - /** @name CumulusPalletXcmEvent (370) */ - interface CumulusPalletXcmEvent extends Enum { - readonly isInvalidFormat: boolean; - readonly asInvalidFormat: U8aFixed; - readonly isUnsupportedVersion: boolean; - readonly asUnsupportedVersion: U8aFixed; - readonly isExecutedDownward: boolean; - readonly asExecutedDownward: ITuple<[U8aFixed, StagingXcmV3TraitsOutcome]>; - readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward'; - } - - /** @name CumulusPalletDmpQueueEvent (371) */ - interface CumulusPalletDmpQueueEvent extends Enum { - readonly isInvalidFormat: boolean; - readonly asInvalidFormat: { - readonly messageHash: U8aFixed; - } & Struct; - readonly isUnsupportedVersion: boolean; - readonly asUnsupportedVersion: { - readonly messageHash: U8aFixed; - } & Struct; - readonly isExecutedDownward: boolean; - readonly asExecutedDownward: { - readonly messageHash: U8aFixed; - readonly messageId: U8aFixed; - readonly outcome: StagingXcmV3TraitsOutcome; - } & Struct; - readonly isWeightExhausted: boolean; - readonly asWeightExhausted: { - readonly messageHash: U8aFixed; - readonly messageId: U8aFixed; - readonly remainingWeight: SpWeightsWeightV2Weight; - readonly requiredWeight: SpWeightsWeightV2Weight; - } & Struct; - readonly isOverweightEnqueued: boolean; - readonly asOverweightEnqueued: { - readonly messageHash: U8aFixed; - readonly messageId: U8aFixed; - readonly overweightIndex: u64; - readonly requiredWeight: SpWeightsWeightV2Weight; - } & Struct; - readonly isOverweightServiced: boolean; - readonly asOverweightServiced: { - readonly overweightIndex: u64; - readonly weightUsed: SpWeightsWeightV2Weight; - } & Struct; - readonly isMaxMessagesExhausted: boolean; - readonly asMaxMessagesExhausted: { - readonly messageHash: U8aFixed; - } & Struct; - readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced' | 'MaxMessagesExhausted'; - } - - /** @name PalletConfigurationEvent (372) */ - interface PalletConfigurationEvent extends Enum { - readonly isNewDesiredCollators: boolean; - readonly asNewDesiredCollators: { - readonly desiredCollators: Option; - } & Struct; - readonly isNewCollatorLicenseBond: boolean; - readonly asNewCollatorLicenseBond: { - readonly bondCost: Option; - } & Struct; - readonly isNewCollatorKickThreshold: boolean; - readonly asNewCollatorKickThreshold: { - readonly lengthInBlocks: Option; - } & Struct; - readonly type: 'NewDesiredCollators' | 'NewCollatorLicenseBond' | 'NewCollatorKickThreshold'; - } - - /** @name PalletCommonEvent (373) */ - interface PalletCommonEvent extends Enum { - readonly isCollectionCreated: boolean; - readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>; - readonly isCollectionDestroyed: boolean; - readonly asCollectionDestroyed: u32; - readonly isItemCreated: boolean; - readonly asItemCreated: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; - readonly isItemDestroyed: boolean; - readonly asItemDestroyed: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; - readonly isTransfer: boolean; - readonly asTransfer: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; - readonly isApproved: boolean; - readonly asApproved: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; - readonly isApprovedForAll: boolean; - readonly asApprovedForAll: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>; - readonly isCollectionPropertySet: boolean; - readonly asCollectionPropertySet: ITuple<[u32, Bytes]>; - readonly isCollectionPropertyDeleted: boolean; - readonly asCollectionPropertyDeleted: ITuple<[u32, Bytes]>; - readonly isTokenPropertySet: boolean; - readonly asTokenPropertySet: ITuple<[u32, u32, Bytes]>; - readonly isTokenPropertyDeleted: boolean; - readonly asTokenPropertyDeleted: ITuple<[u32, u32, Bytes]>; - readonly isPropertyPermissionSet: boolean; - readonly asPropertyPermissionSet: ITuple<[u32, Bytes]>; - readonly isAllowListAddressAdded: boolean; - readonly asAllowListAddressAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - readonly isAllowListAddressRemoved: boolean; - readonly asAllowListAddressRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - readonly isCollectionAdminAdded: boolean; - readonly asCollectionAdminAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - readonly isCollectionAdminRemoved: boolean; - readonly asCollectionAdminRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - readonly isCollectionLimitSet: boolean; - readonly asCollectionLimitSet: u32; - readonly isCollectionOwnerChanged: boolean; - readonly asCollectionOwnerChanged: ITuple<[u32, AccountId32]>; - readonly isCollectionPermissionSet: boolean; - readonly asCollectionPermissionSet: u32; - readonly isCollectionSponsorSet: boolean; - readonly asCollectionSponsorSet: ITuple<[u32, AccountId32]>; - readonly isSponsorshipConfirmed: boolean; - readonly asSponsorshipConfirmed: ITuple<[u32, AccountId32]>; - readonly isCollectionSponsorRemoved: boolean; - readonly asCollectionSponsorRemoved: u32; - readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet' | 'AllowListAddressAdded' | 'AllowListAddressRemoved' | 'CollectionAdminAdded' | 'CollectionAdminRemoved' | 'CollectionLimitSet' | 'CollectionOwnerChanged' | 'CollectionPermissionSet' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionSponsorRemoved'; - } - - /** @name PalletStructureEvent (374) */ - interface PalletStructureEvent extends Enum { - readonly isExecuted: boolean; - readonly asExecuted: Result; - readonly type: 'Executed'; - } - - /** @name PalletAppPromotionEvent (375) */ - interface PalletAppPromotionEvent extends Enum { - readonly isStakingRecalculation: boolean; - readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>; - readonly isStake: boolean; - readonly asStake: ITuple<[AccountId32, u128]>; - readonly isUnstake: boolean; - readonly asUnstake: ITuple<[AccountId32, u128]>; - readonly isSetAdmin: boolean; - readonly asSetAdmin: AccountId32; - readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin'; - } - - /** @name PalletForeignAssetsModuleEvent (376) */ - interface PalletForeignAssetsModuleEvent extends Enum { - readonly isForeignAssetRegistered: boolean; - readonly asForeignAssetRegistered: { - readonly assetId: u32; - readonly assetAddress: StagingXcmV3MultiLocation; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly isForeignAssetUpdated: boolean; - readonly asForeignAssetUpdated: { - readonly assetId: u32; - readonly assetAddress: StagingXcmV3MultiLocation; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly isAssetRegistered: boolean; - readonly asAssetRegistered: { - readonly assetId: PalletForeignAssetsAssetId; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly isAssetUpdated: boolean; - readonly asAssetUpdated: { - readonly assetId: PalletForeignAssetsAssetId; - readonly metadata: PalletForeignAssetsModuleAssetMetadata; - } & Struct; - readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated'; - } - - /** @name PalletEvmEvent (377) */ - interface PalletEvmEvent extends Enum { - readonly isLog: boolean; - readonly asLog: { - readonly log: EthereumLog; - } & Struct; - readonly isCreated: boolean; - readonly asCreated: { - readonly address: H160; - } & Struct; - readonly isCreatedFailed: boolean; - readonly asCreatedFailed: { - readonly address: H160; - } & Struct; - readonly isExecuted: boolean; - readonly asExecuted: { - readonly address: H160; - } & Struct; - readonly isExecutedFailed: boolean; - readonly asExecutedFailed: { - readonly address: H160; - } & Struct; - readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed'; - } - - /** @name PalletEthereumEvent (378) */ - interface PalletEthereumEvent extends Enum { - readonly isExecuted: boolean; - readonly asExecuted: { - readonly from: H160; - readonly to: H160; - readonly transactionHash: H256; - readonly exitReason: EvmCoreErrorExitReason; - readonly extraData: Bytes; - } & Struct; - readonly type: 'Executed'; - } - - /** @name EvmCoreErrorExitReason (379) */ - interface EvmCoreErrorExitReason extends Enum { - readonly isSucceed: boolean; - readonly asSucceed: EvmCoreErrorExitSucceed; - readonly isError: boolean; - readonly asError: EvmCoreErrorExitError; - readonly isRevert: boolean; - readonly asRevert: EvmCoreErrorExitRevert; - readonly isFatal: boolean; - readonly asFatal: EvmCoreErrorExitFatal; - readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal'; - } - - /** @name EvmCoreErrorExitSucceed (380) */ - interface EvmCoreErrorExitSucceed extends Enum { - readonly isStopped: boolean; - readonly isReturned: boolean; - readonly isSuicided: boolean; - readonly type: 'Stopped' | 'Returned' | 'Suicided'; - } - - /** @name EvmCoreErrorExitError (381) */ - interface EvmCoreErrorExitError extends Enum { - readonly isStackUnderflow: boolean; - readonly isStackOverflow: boolean; - readonly isInvalidJump: boolean; - readonly isInvalidRange: boolean; - readonly isDesignatedInvalid: boolean; - readonly isCallTooDeep: boolean; - readonly isCreateCollision: boolean; - readonly isCreateContractLimit: boolean; - readonly isOutOfOffset: boolean; - readonly isOutOfGas: boolean; - readonly isOutOfFund: boolean; - readonly isPcUnderflow: boolean; - readonly isCreateEmpty: boolean; - readonly isOther: boolean; - readonly asOther: Text; - readonly isMaxNonce: boolean; - readonly isInvalidCode: boolean; - readonly asInvalidCode: u8; - readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'MaxNonce' | 'InvalidCode'; - } - - /** @name EvmCoreErrorExitRevert (385) */ - interface EvmCoreErrorExitRevert extends Enum { - readonly isReverted: boolean; - readonly type: 'Reverted'; - } - - /** @name EvmCoreErrorExitFatal (386) */ - interface EvmCoreErrorExitFatal extends Enum { - readonly isNotSupported: boolean; - readonly isUnhandledInterrupt: boolean; - readonly isCallErrorAsFatal: boolean; - readonly asCallErrorAsFatal: EvmCoreErrorExitError; - readonly isOther: boolean; - readonly asOther: Text; - readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other'; - } - - /** @name PalletEvmContractHelpersEvent (387) */ - interface PalletEvmContractHelpersEvent extends Enum { - readonly isContractSponsorSet: boolean; - readonly asContractSponsorSet: ITuple<[H160, AccountId32]>; - readonly isContractSponsorshipConfirmed: boolean; - readonly asContractSponsorshipConfirmed: ITuple<[H160, AccountId32]>; - readonly isContractSponsorRemoved: boolean; - readonly asContractSponsorRemoved: H160; - readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved'; - } - - /** @name PalletEvmMigrationEvent (388) */ - interface PalletEvmMigrationEvent extends Enum { - readonly isTestEvent: boolean; - readonly type: 'TestEvent'; - } - - /** @name PalletMaintenanceEvent (389) */ - interface PalletMaintenanceEvent extends Enum { - readonly isMaintenanceEnabled: boolean; - readonly isMaintenanceDisabled: boolean; - readonly type: 'MaintenanceEnabled' | 'MaintenanceDisabled'; - } - - /** @name PalletUtilityEvent (390) */ - interface PalletUtilityEvent extends Enum { - readonly isBatchInterrupted: boolean; - readonly asBatchInterrupted: { - readonly index: u32; - readonly error: SpRuntimeDispatchError; - } & Struct; - readonly isBatchCompleted: boolean; - readonly isBatchCompletedWithErrors: boolean; - readonly isItemCompleted: boolean; - readonly isItemFailed: boolean; - readonly asItemFailed: { - readonly error: SpRuntimeDispatchError; - } & Struct; - readonly isDispatchedAs: boolean; - readonly asDispatchedAs: { - readonly result: Result; - } & Struct; - readonly type: 'BatchInterrupted' | 'BatchCompleted' | 'BatchCompletedWithErrors' | 'ItemCompleted' | 'ItemFailed' | 'DispatchedAs'; - } - - /** @name PalletTestUtilsEvent (391) */ - interface PalletTestUtilsEvent extends Enum { - readonly isValueIsSet: boolean; - readonly isShouldRollback: boolean; - readonly isBatchCompleted: boolean; - readonly type: 'ValueIsSet' | 'ShouldRollback' | 'BatchCompleted'; - } - - /** @name FrameSystemPhase (392) */ - interface FrameSystemPhase extends Enum { - readonly isApplyExtrinsic: boolean; - readonly asApplyExtrinsic: u32; - readonly isFinalization: boolean; - readonly isInitialization: boolean; - readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization'; - } - - /** @name FrameSystemLastRuntimeUpgradeInfo (394) */ - interface FrameSystemLastRuntimeUpgradeInfo extends Struct { - readonly specVersion: Compact; - readonly specName: Text; - } - - /** @name FrameSystemLimitsBlockWeights (395) */ - interface FrameSystemLimitsBlockWeights extends Struct { - readonly baseBlock: SpWeightsWeightV2Weight; - readonly maxBlock: SpWeightsWeightV2Weight; - readonly perClass: FrameSupportDispatchPerDispatchClassWeightsPerClass; - } - - /** @name FrameSupportDispatchPerDispatchClassWeightsPerClass (396) */ - interface FrameSupportDispatchPerDispatchClassWeightsPerClass extends Struct { - readonly normal: FrameSystemLimitsWeightsPerClass; - readonly operational: FrameSystemLimitsWeightsPerClass; - readonly mandatory: FrameSystemLimitsWeightsPerClass; - } - - /** @name FrameSystemLimitsWeightsPerClass (397) */ - interface FrameSystemLimitsWeightsPerClass extends Struct { - readonly baseExtrinsic: SpWeightsWeightV2Weight; - readonly maxExtrinsic: Option; - readonly maxTotal: Option; - readonly reserved: Option; - } - - /** @name FrameSystemLimitsBlockLength (399) */ - interface FrameSystemLimitsBlockLength extends Struct { - readonly max: FrameSupportDispatchPerDispatchClassU32; - } - - /** @name FrameSupportDispatchPerDispatchClassU32 (400) */ - interface FrameSupportDispatchPerDispatchClassU32 extends Struct { - readonly normal: u32; - readonly operational: u32; - readonly mandatory: u32; - } - - /** @name SpWeightsRuntimeDbWeight (401) */ - interface SpWeightsRuntimeDbWeight extends Struct { - readonly read: u64; - readonly write: u64; - } - - /** @name SpVersionRuntimeVersion (402) */ - interface SpVersionRuntimeVersion extends Struct { - readonly specName: Text; - readonly implName: Text; - readonly authoringVersion: u32; - readonly specVersion: u32; - readonly implVersion: u32; - readonly apis: Vec>; - readonly transactionVersion: u32; - readonly stateVersion: u8; - } - - /** @name FrameSystemError (406) */ - interface FrameSystemError extends Enum { - readonly isInvalidSpecName: boolean; - readonly isSpecVersionNeedsToIncrease: boolean; - readonly isFailedToExtractRuntimeVersion: boolean; - readonly isNonDefaultComposite: boolean; - readonly isNonZeroRefCount: boolean; - readonly isCallFiltered: boolean; - readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered'; - } - - /** @name CumulusPalletParachainSystemUnincludedSegmentAncestor (408) */ - interface CumulusPalletParachainSystemUnincludedSegmentAncestor extends Struct { - readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth; - readonly paraHeadHash: Option; - readonly consumedGoAheadSignal: Option; - } - - /** @name CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth (409) */ - interface CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth extends Struct { - readonly umpMsgCount: u32; - readonly umpTotalBytes: u32; - readonly hrmpOutgoing: BTreeMap; - } - - /** @name CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate (411) */ - interface CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate extends Struct { - readonly msgCount: u32; - readonly totalBytes: u32; - } - - /** @name PolkadotPrimitivesV5UpgradeGoAhead (415) */ - interface PolkadotPrimitivesV5UpgradeGoAhead extends Enum { - readonly isAbort: boolean; - readonly isGoAhead: boolean; - readonly type: 'Abort' | 'GoAhead'; - } - - /** @name CumulusPalletParachainSystemUnincludedSegmentSegmentTracker (416) */ - interface CumulusPalletParachainSystemUnincludedSegmentSegmentTracker extends Struct { - readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth; - readonly hrmpWatermark: Option; - readonly consumedGoAheadSignal: Option; - } - - /** @name PolkadotPrimitivesV5UpgradeRestriction (418) */ - interface PolkadotPrimitivesV5UpgradeRestriction extends Enum { - readonly isPresent: boolean; - readonly type: 'Present'; - } - - /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (419) */ - interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct { - readonly dmqMqcHead: H256; - readonly relayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity; - readonly ingressChannels: Vec>; - readonly egressChannels: Vec>; - } - - /** @name CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity (420) */ - interface CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity extends Struct { - readonly remainingCount: u32; - readonly remainingSize: u32; - } - - /** @name PolkadotPrimitivesV5AbridgedHrmpChannel (423) */ - interface PolkadotPrimitivesV5AbridgedHrmpChannel extends Struct { - readonly maxCapacity: u32; - readonly maxTotalSize: u32; - readonly maxMessageSize: u32; - readonly msgCount: u32; - readonly totalSize: u32; - readonly mqcHead: Option; - } - - /** @name PolkadotPrimitivesV5AbridgedHostConfiguration (424) */ - interface PolkadotPrimitivesV5AbridgedHostConfiguration extends Struct { - readonly maxCodeSize: u32; - readonly maxHeadDataSize: u32; - readonly maxUpwardQueueCount: u32; - readonly maxUpwardQueueSize: u32; - readonly maxUpwardMessageSize: u32; - readonly maxUpwardMessageNumPerCandidate: u32; - readonly hrmpMaxMessageNumPerCandidate: u32; - readonly validationUpgradeCooldown: u32; - readonly validationUpgradeDelay: u32; - readonly asyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams; - } - - /** @name PolkadotPrimitivesVstagingAsyncBackingParams (425) */ - interface PolkadotPrimitivesVstagingAsyncBackingParams extends Struct { - readonly maxCandidateDepth: u32; - readonly allowedAncestryLen: u32; - } - - /** @name PolkadotCorePrimitivesOutboundHrmpMessage (431) */ - interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct { - readonly recipient: u32; - readonly data: Bytes; - } - - /** @name CumulusPalletParachainSystemCodeUpgradeAuthorization (432) */ - interface CumulusPalletParachainSystemCodeUpgradeAuthorization extends Struct { - readonly codeHash: H256; - readonly checkVersion: bool; - } - - /** @name CumulusPalletParachainSystemError (433) */ - interface CumulusPalletParachainSystemError extends Enum { - readonly isOverlappingUpgrades: boolean; - readonly isProhibitedByPolkadot: boolean; - readonly isTooBig: boolean; - readonly isValidationDataNotAvailable: boolean; - readonly isHostConfigurationNotAvailable: boolean; - readonly isNotScheduled: boolean; - readonly isNothingAuthorized: boolean; - readonly isUnauthorized: boolean; - readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized'; - } - - /** @name PalletCollatorSelectionError (435) */ - interface PalletCollatorSelectionError extends Enum { - readonly isTooManyCandidates: boolean; - readonly isUnknown: boolean; - readonly isPermission: boolean; - readonly isAlreadyHoldingLicense: boolean; - readonly isNoLicense: boolean; - readonly isAlreadyCandidate: boolean; - readonly isNotCandidate: boolean; - readonly isTooManyInvulnerables: boolean; - readonly isTooFewInvulnerables: boolean; - readonly isAlreadyInvulnerable: boolean; - readonly isNotInvulnerable: boolean; - readonly isNoAssociatedValidatorId: boolean; - readonly isValidatorNotRegistered: boolean; - readonly type: 'TooManyCandidates' | 'Unknown' | 'Permission' | 'AlreadyHoldingLicense' | 'NoLicense' | 'AlreadyCandidate' | 'NotCandidate' | 'TooManyInvulnerables' | 'TooFewInvulnerables' | 'AlreadyInvulnerable' | 'NotInvulnerable' | 'NoAssociatedValidatorId' | 'ValidatorNotRegistered'; - } - - /** @name SpCoreCryptoKeyTypeId (439) */ - interface SpCoreCryptoKeyTypeId extends U8aFixed {} - - /** @name PalletSessionError (440) */ - interface PalletSessionError extends Enum { - readonly isInvalidProof: boolean; - readonly isNoAssociatedValidatorId: boolean; - readonly isDuplicatedKey: boolean; - readonly isNoKeys: boolean; - readonly isNoAccount: boolean; - readonly type: 'InvalidProof' | 'NoAssociatedValidatorId' | 'DuplicatedKey' | 'NoKeys' | 'NoAccount'; - } - - /** @name PalletBalancesBalanceLock (446) */ - interface PalletBalancesBalanceLock extends Struct { - readonly id: U8aFixed; - readonly amount: u128; - readonly reasons: PalletBalancesReasons; - } - - /** @name PalletBalancesReasons (447) */ - interface PalletBalancesReasons extends Enum { - readonly isFee: boolean; - readonly isMisc: boolean; - readonly isAll: boolean; - readonly type: 'Fee' | 'Misc' | 'All'; - } - - /** @name PalletBalancesReserveData (450) */ - interface PalletBalancesReserveData extends Struct { - readonly id: U8aFixed; - readonly amount: u128; - } - - /** @name OpalRuntimeRuntimeHoldReason (454) */ - interface OpalRuntimeRuntimeHoldReason extends Enum { - readonly isCollatorSelection: boolean; - readonly asCollatorSelection: PalletCollatorSelectionHoldReason; - readonly type: 'CollatorSelection'; - } - - /** @name PalletCollatorSelectionHoldReason (455) */ - interface PalletCollatorSelectionHoldReason extends Enum { - readonly isLicenseBond: boolean; - readonly type: 'LicenseBond'; - } - - /** @name PalletBalancesIdAmount (458) */ - interface PalletBalancesIdAmount extends Struct { - readonly id: U8aFixed; - readonly amount: u128; - } - - /** @name PalletBalancesError (460) */ - interface PalletBalancesError extends Enum { - readonly isVestingBalance: boolean; - readonly isLiquidityRestrictions: boolean; - readonly isInsufficientBalance: boolean; - readonly isExistentialDeposit: boolean; - readonly isExpendability: boolean; - readonly isExistingVestingSchedule: boolean; - readonly isDeadAccount: boolean; - readonly isTooManyReserves: boolean; - readonly isTooManyHolds: boolean; - readonly isTooManyFreezes: boolean; - readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'Expendability' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves' | 'TooManyHolds' | 'TooManyFreezes'; - } - - /** @name PalletTransactionPaymentReleases (462) */ - interface PalletTransactionPaymentReleases extends Enum { - readonly isV1Ancient: boolean; - readonly isV2: boolean; - readonly type: 'V1Ancient' | 'V2'; - } - - /** @name PalletTreasuryProposal (463) */ - interface PalletTreasuryProposal extends Struct { - readonly proposer: AccountId32; - readonly value: u128; - readonly beneficiary: AccountId32; - readonly bond: u128; - } - - /** @name FrameSupportPalletId (466) */ - interface FrameSupportPalletId extends U8aFixed {} - - /** @name PalletTreasuryError (467) */ - interface PalletTreasuryError extends Enum { - readonly isInsufficientProposersBalance: boolean; - readonly isInvalidIndex: boolean; - readonly isTooManyApprovals: boolean; - readonly isInsufficientPermission: boolean; - readonly isProposalNotApproved: boolean; - readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved'; - } - - /** @name PalletSudoError (468) */ - interface PalletSudoError extends Enum { - readonly isRequireSudo: boolean; - readonly type: 'RequireSudo'; - } - - /** @name OrmlVestingModuleError (470) */ - interface OrmlVestingModuleError extends Enum { - readonly isZeroVestingPeriod: boolean; - readonly isZeroVestingPeriodCount: boolean; - readonly isInsufficientBalanceToLock: boolean; - readonly isTooManyVestingSchedules: boolean; - readonly isAmountLow: boolean; - readonly isMaxVestingSchedulesExceeded: boolean; - readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded'; - } - - /** @name OrmlXtokensModuleError (471) */ - interface OrmlXtokensModuleError extends Enum { - readonly isAssetHasNoReserve: boolean; - readonly isNotCrossChainTransfer: boolean; - readonly isInvalidDest: boolean; - readonly isNotCrossChainTransferableCurrency: boolean; - readonly isUnweighableMessage: boolean; - readonly isXcmExecutionFailed: boolean; - readonly isCannotReanchor: boolean; - readonly isInvalidAncestry: boolean; - readonly isInvalidAsset: boolean; - readonly isDestinationNotInvertible: boolean; - readonly isBadVersion: boolean; - readonly isDistinctReserveForAssetAndFee: boolean; - readonly isZeroFee: boolean; - readonly isZeroAmount: boolean; - readonly isTooManyAssetsBeingSent: boolean; - readonly isAssetIndexNonExistent: boolean; - readonly isFeeNotEnough: boolean; - readonly isNotSupportedMultiLocation: boolean; - readonly isMinXcmFeeNotDefined: boolean; - readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined'; - } - - /** @name OrmlTokensBalanceLock (474) */ - interface OrmlTokensBalanceLock extends Struct { - readonly id: U8aFixed; - readonly amount: u128; - } - - /** @name OrmlTokensAccountData (476) */ - interface OrmlTokensAccountData extends Struct { - readonly free: u128; - readonly reserved: u128; - readonly frozen: u128; - } - - /** @name OrmlTokensReserveData (478) */ - interface OrmlTokensReserveData extends Struct { - readonly id: Null; - readonly amount: u128; - } - - /** @name OrmlTokensModuleError (480) */ - interface OrmlTokensModuleError extends Enum { - readonly isBalanceTooLow: boolean; - readonly isAmountIntoBalanceFailed: boolean; - readonly isLiquidityRestrictions: boolean; - readonly isMaxLocksExceeded: boolean; - readonly isKeepAlive: boolean; - readonly isExistentialDeposit: boolean; - readonly isDeadAccount: boolean; - readonly isTooManyReserves: boolean; - readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves'; - } - - /** @name PalletIdentityRegistrarInfo (485) */ - interface PalletIdentityRegistrarInfo extends Struct { - readonly account: AccountId32; - readonly fee: u128; - readonly fields: PalletIdentityBitFlags; - } - - /** @name PalletIdentityError (487) */ - interface PalletIdentityError extends Enum { - readonly isTooManySubAccounts: boolean; - readonly isNotFound: boolean; - readonly isNotNamed: boolean; - readonly isEmptyIndex: boolean; - readonly isFeeChanged: boolean; - readonly isNoIdentity: boolean; - readonly isStickyJudgement: boolean; - readonly isJudgementGiven: boolean; - readonly isInvalidJudgement: boolean; - readonly isInvalidIndex: boolean; - readonly isInvalidTarget: boolean; - readonly isTooManyFields: boolean; - readonly isTooManyRegistrars: boolean; - readonly isAlreadyClaimed: boolean; - readonly isNotSub: boolean; - readonly isNotOwned: boolean; - readonly isJudgementForDifferentIdentity: boolean; - readonly isJudgementPaymentFailed: boolean; - readonly type: 'TooManySubAccounts' | 'NotFound' | 'NotNamed' | 'EmptyIndex' | 'FeeChanged' | 'NoIdentity' | 'StickyJudgement' | 'JudgementGiven' | 'InvalidJudgement' | 'InvalidIndex' | 'InvalidTarget' | 'TooManyFields' | 'TooManyRegistrars' | 'AlreadyClaimed' | 'NotSub' | 'NotOwned' | 'JudgementForDifferentIdentity' | 'JudgementPaymentFailed'; - } - - /** @name PalletPreimageRequestStatus (488) */ - interface PalletPreimageRequestStatus extends Enum { - readonly isUnrequested: boolean; - readonly asUnrequested: { - readonly deposit: ITuple<[AccountId32, u128]>; - readonly len: u32; - } & Struct; - readonly isRequested: boolean; - readonly asRequested: { - readonly deposit: Option>; - readonly count: u32; - readonly len: Option; - } & Struct; - readonly type: 'Unrequested' | 'Requested'; - } - - /** @name PalletPreimageError (493) */ - interface PalletPreimageError extends Enum { - readonly isTooBig: boolean; - readonly isAlreadyNoted: boolean; - readonly isNotAuthorized: boolean; - readonly isNotNoted: boolean; - readonly isRequested: boolean; - readonly isNotRequested: boolean; - readonly type: 'TooBig' | 'AlreadyNoted' | 'NotAuthorized' | 'NotNoted' | 'Requested' | 'NotRequested'; - } - - /** @name PalletDemocracyReferendumInfo (499) */ - interface PalletDemocracyReferendumInfo extends Enum { - readonly isOngoing: boolean; - readonly asOngoing: PalletDemocracyReferendumStatus; - readonly isFinished: boolean; - readonly asFinished: { - readonly approved: bool; - readonly end: u32; - } & Struct; - readonly type: 'Ongoing' | 'Finished'; - } - - /** @name PalletDemocracyReferendumStatus (500) */ - interface PalletDemocracyReferendumStatus extends Struct { - readonly end: u32; - readonly proposal: FrameSupportPreimagesBounded; - readonly threshold: PalletDemocracyVoteThreshold; - readonly delay: u32; - readonly tally: PalletDemocracyTally; - } - - /** @name PalletDemocracyTally (501) */ - interface PalletDemocracyTally extends Struct { - readonly ayes: u128; - readonly nays: u128; - readonly turnout: u128; - } - - /** @name PalletDemocracyVoteVoting (502) */ - interface PalletDemocracyVoteVoting extends Enum { - readonly isDirect: boolean; - readonly asDirect: { - readonly votes: Vec>; - readonly delegations: PalletDemocracyDelegations; - readonly prior: PalletDemocracyVotePriorLock; - } & Struct; - readonly isDelegating: boolean; - readonly asDelegating: { - readonly balance: u128; - readonly target: AccountId32; - readonly conviction: PalletDemocracyConviction; - readonly delegations: PalletDemocracyDelegations; - readonly prior: PalletDemocracyVotePriorLock; - } & Struct; - readonly type: 'Direct' | 'Delegating'; - } - - /** @name PalletDemocracyDelegations (506) */ - interface PalletDemocracyDelegations extends Struct { - readonly votes: u128; - readonly capital: u128; - } - - /** @name PalletDemocracyVotePriorLock (507) */ - interface PalletDemocracyVotePriorLock extends ITuple<[u32, u128]> {} - - /** @name PalletDemocracyError (510) */ - interface PalletDemocracyError extends Enum { - readonly isValueLow: boolean; - readonly isProposalMissing: boolean; - readonly isAlreadyCanceled: boolean; - readonly isDuplicateProposal: boolean; - readonly isProposalBlacklisted: boolean; - readonly isNotSimpleMajority: boolean; - readonly isInvalidHash: boolean; - readonly isNoProposal: boolean; - readonly isAlreadyVetoed: boolean; - readonly isReferendumInvalid: boolean; - readonly isNoneWaiting: boolean; - readonly isNotVoter: boolean; - readonly isNoPermission: boolean; - readonly isAlreadyDelegating: boolean; - readonly isInsufficientFunds: boolean; - readonly isNotDelegating: boolean; - readonly isVotesExist: boolean; - readonly isInstantNotAllowed: boolean; - readonly isNonsense: boolean; - readonly isWrongUpperBound: boolean; - readonly isMaxVotesReached: boolean; - readonly isTooMany: boolean; - readonly isVotingPeriodLow: boolean; - readonly isPreimageNotExist: boolean; - readonly type: 'ValueLow' | 'ProposalMissing' | 'AlreadyCanceled' | 'DuplicateProposal' | 'ProposalBlacklisted' | 'NotSimpleMajority' | 'InvalidHash' | 'NoProposal' | 'AlreadyVetoed' | 'ReferendumInvalid' | 'NoneWaiting' | 'NotVoter' | 'NoPermission' | 'AlreadyDelegating' | 'InsufficientFunds' | 'NotDelegating' | 'VotesExist' | 'InstantNotAllowed' | 'Nonsense' | 'WrongUpperBound' | 'MaxVotesReached' | 'TooMany' | 'VotingPeriodLow' | 'PreimageNotExist'; - } - - /** @name PalletCollectiveVotes (512) */ - interface PalletCollectiveVotes extends Struct { - readonly index: u32; - readonly threshold: u32; - readonly ayes: Vec; - readonly nays: Vec; - readonly end: u32; - } - - /** @name PalletCollectiveError (513) */ - interface PalletCollectiveError extends Enum { - readonly isNotMember: boolean; - readonly isDuplicateProposal: boolean; - readonly isProposalMissing: boolean; - readonly isWrongIndex: boolean; - readonly isDuplicateVote: boolean; - readonly isAlreadyInitialized: boolean; - readonly isTooEarly: boolean; - readonly isTooManyProposals: boolean; - readonly isWrongProposalWeight: boolean; - readonly isWrongProposalLength: boolean; - readonly isPrimeAccountNotMember: boolean; - readonly type: 'NotMember' | 'DuplicateProposal' | 'ProposalMissing' | 'WrongIndex' | 'DuplicateVote' | 'AlreadyInitialized' | 'TooEarly' | 'TooManyProposals' | 'WrongProposalWeight' | 'WrongProposalLength' | 'PrimeAccountNotMember'; - } - - /** @name PalletMembershipError (517) */ - interface PalletMembershipError extends Enum { - readonly isAlreadyMember: boolean; - readonly isNotMember: boolean; - readonly isTooManyMembers: boolean; - readonly type: 'AlreadyMember' | 'NotMember' | 'TooManyMembers'; - } - - /** @name PalletRankedCollectiveMemberRecord (520) */ - interface PalletRankedCollectiveMemberRecord extends Struct { - readonly rank: u16; - } - - /** @name PalletRankedCollectiveError (525) */ - interface PalletRankedCollectiveError extends Enum { - readonly isAlreadyMember: boolean; - readonly isNotMember: boolean; - readonly isNotPolling: boolean; - readonly isOngoing: boolean; - readonly isNoneRemaining: boolean; - readonly isCorruption: boolean; - readonly isRankTooLow: boolean; - readonly isInvalidWitness: boolean; - readonly isNoPermission: boolean; - readonly type: 'AlreadyMember' | 'NotMember' | 'NotPolling' | 'Ongoing' | 'NoneRemaining' | 'Corruption' | 'RankTooLow' | 'InvalidWitness' | 'NoPermission'; - } - - /** @name PalletReferendaReferendumInfo (526) */ - interface PalletReferendaReferendumInfo extends Enum { - readonly isOngoing: boolean; - readonly asOngoing: PalletReferendaReferendumStatus; - readonly isApproved: boolean; - readonly asApproved: ITuple<[u32, Option, Option]>; - readonly isRejected: boolean; - readonly asRejected: ITuple<[u32, Option, Option]>; - readonly isCancelled: boolean; - readonly asCancelled: ITuple<[u32, Option, Option]>; - readonly isTimedOut: boolean; - readonly asTimedOut: ITuple<[u32, Option, Option]>; - readonly isKilled: boolean; - readonly asKilled: u32; - readonly type: 'Ongoing' | 'Approved' | 'Rejected' | 'Cancelled' | 'TimedOut' | 'Killed'; - } - - /** @name PalletReferendaReferendumStatus (527) */ - interface PalletReferendaReferendumStatus extends Struct { - readonly track: u16; - readonly origin: OpalRuntimeOriginCaller; - readonly proposal: FrameSupportPreimagesBounded; - readonly enactment: FrameSupportScheduleDispatchTime; - readonly submitted: u32; - readonly submissionDeposit: PalletReferendaDeposit; - readonly decisionDeposit: Option; - readonly deciding: Option; - readonly tally: PalletRankedCollectiveTally; - readonly inQueue: bool; - readonly alarm: Option]>>; - } - - /** @name PalletReferendaDeposit (528) */ - interface PalletReferendaDeposit extends Struct { - readonly who: AccountId32; - readonly amount: u128; - } - - /** @name PalletReferendaDecidingStatus (531) */ - interface PalletReferendaDecidingStatus extends Struct { - readonly since: u32; - readonly confirming: Option; - } - - /** @name PalletReferendaTrackInfo (537) */ - interface PalletReferendaTrackInfo extends Struct { - readonly name: Text; - readonly maxDeciding: u32; - readonly decisionDeposit: u128; - readonly preparePeriod: u32; - readonly decisionPeriod: u32; - readonly confirmPeriod: u32; - readonly minEnactmentPeriod: u32; - readonly minApproval: PalletReferendaCurve; - readonly minSupport: PalletReferendaCurve; - } - - /** @name PalletReferendaCurve (538) */ - interface PalletReferendaCurve extends Enum { - readonly isLinearDecreasing: boolean; - readonly asLinearDecreasing: { - readonly length: Perbill; - readonly floor: Perbill; - readonly ceil: Perbill; - } & Struct; - readonly isSteppedDecreasing: boolean; - readonly asSteppedDecreasing: { - readonly begin: Perbill; - readonly end: Perbill; - readonly step: Perbill; - readonly period: Perbill; - } & Struct; - readonly isReciprocal: boolean; - readonly asReciprocal: { - readonly factor: i64; - readonly xOffset: i64; - readonly yOffset: i64; - } & Struct; - readonly type: 'LinearDecreasing' | 'SteppedDecreasing' | 'Reciprocal'; - } - - /** @name PalletReferendaError (541) */ - interface PalletReferendaError extends Enum { - readonly isNotOngoing: boolean; - readonly isHasDeposit: boolean; - readonly isBadTrack: boolean; - readonly isFull: boolean; - readonly isQueueEmpty: boolean; - readonly isBadReferendum: boolean; - readonly isNothingToDo: boolean; - readonly isNoTrack: boolean; - readonly isUnfinished: boolean; - readonly isNoPermission: boolean; - readonly isNoDeposit: boolean; - readonly isBadStatus: boolean; - readonly isPreimageNotExist: boolean; - readonly type: 'NotOngoing' | 'HasDeposit' | 'BadTrack' | 'Full' | 'QueueEmpty' | 'BadReferendum' | 'NothingToDo' | 'NoTrack' | 'Unfinished' | 'NoPermission' | 'NoDeposit' | 'BadStatus' | 'PreimageNotExist'; - } - - /** @name PalletSchedulerScheduled (544) */ - interface PalletSchedulerScheduled extends Struct { - readonly maybeId: Option; - readonly priority: u8; - readonly call: FrameSupportPreimagesBounded; - readonly maybePeriodic: Option>; - readonly origin: OpalRuntimeOriginCaller; - } - - /** @name PalletSchedulerError (546) */ - interface PalletSchedulerError extends Enum { - readonly isFailedToSchedule: boolean; - readonly isNotFound: boolean; - readonly isTargetBlockNumberInPast: boolean; - readonly isRescheduleNoChange: boolean; - readonly isNamed: boolean; - readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange' | 'Named'; - } - - /** @name CumulusPalletXcmpQueueInboundChannelDetails (548) */ - interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct { - readonly sender: u32; - readonly state: CumulusPalletXcmpQueueInboundState; - readonly messageMetadata: Vec>; - } - - /** @name CumulusPalletXcmpQueueInboundState (549) */ - interface CumulusPalletXcmpQueueInboundState extends Enum { - readonly isOk: boolean; - readonly isSuspended: boolean; - readonly type: 'Ok' | 'Suspended'; - } - - /** @name PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat (552) */ - interface PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat extends Enum { - readonly isConcatenatedVersionedXcm: boolean; - readonly isConcatenatedEncodedBlob: boolean; - readonly isSignals: boolean; - readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals'; - } - - /** @name CumulusPalletXcmpQueueOutboundChannelDetails (555) */ - interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct { - readonly recipient: u32; - readonly state: CumulusPalletXcmpQueueOutboundState; - readonly signalsExist: bool; - readonly firstIndex: u16; - readonly lastIndex: u16; - } - - /** @name CumulusPalletXcmpQueueOutboundState (556) */ - interface CumulusPalletXcmpQueueOutboundState extends Enum { - readonly isOk: boolean; - readonly isSuspended: boolean; - readonly type: 'Ok' | 'Suspended'; - } - - /** @name CumulusPalletXcmpQueueQueueConfigData (558) */ - interface CumulusPalletXcmpQueueQueueConfigData extends Struct { - readonly suspendThreshold: u32; - readonly dropThreshold: u32; - readonly resumeThreshold: u32; - readonly thresholdWeight: SpWeightsWeightV2Weight; - readonly weightRestrictDecay: SpWeightsWeightV2Weight; - readonly xcmpMaxIndividualWeight: SpWeightsWeightV2Weight; - } - - /** @name CumulusPalletXcmpQueueError (560) */ - interface CumulusPalletXcmpQueueError extends Enum { - readonly isFailedToSend: boolean; - readonly isBadXcmOrigin: boolean; - readonly isBadXcm: boolean; - readonly isBadOverweightIndex: boolean; - readonly isWeightOverLimit: boolean; - readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit'; - } - - /** @name PalletXcmQueryStatus (561) */ - interface PalletXcmQueryStatus extends Enum { - readonly isPending: boolean; - readonly asPending: { - readonly responder: StagingXcmVersionedMultiLocation; - readonly maybeMatchQuerier: Option; - readonly maybeNotify: Option>; - readonly timeout: u32; - } & Struct; - readonly isVersionNotifier: boolean; - readonly asVersionNotifier: { - readonly origin: StagingXcmVersionedMultiLocation; - readonly isActive: bool; - } & Struct; - readonly isReady: boolean; - readonly asReady: { - readonly response: StagingXcmVersionedResponse; - readonly at: u32; - } & Struct; - readonly type: 'Pending' | 'VersionNotifier' | 'Ready'; - } - - /** @name StagingXcmVersionedResponse (565) */ - interface StagingXcmVersionedResponse extends Enum { - readonly isV2: boolean; - readonly asV2: StagingXcmV2Response; - readonly isV3: boolean; - readonly asV3: StagingXcmV3Response; - readonly type: 'V2' | 'V3'; - } - - /** @name PalletXcmVersionMigrationStage (571) */ - interface PalletXcmVersionMigrationStage extends Enum { - readonly isMigrateSupportedVersion: boolean; - readonly isMigrateVersionNotifiers: boolean; - readonly isNotifyCurrentTargets: boolean; - readonly asNotifyCurrentTargets: Option; - readonly isMigrateAndNotifyOldTargets: boolean; - readonly type: 'MigrateSupportedVersion' | 'MigrateVersionNotifiers' | 'NotifyCurrentTargets' | 'MigrateAndNotifyOldTargets'; - } - - /** @name StagingXcmVersionedAssetId (574) */ - interface StagingXcmVersionedAssetId extends Enum { - readonly isV3: boolean; - readonly asV3: StagingXcmV3MultiassetAssetId; - readonly type: 'V3'; - } - - /** @name PalletXcmRemoteLockedFungibleRecord (575) */ - interface PalletXcmRemoteLockedFungibleRecord extends Struct { - readonly amount: u128; - readonly owner: StagingXcmVersionedMultiLocation; - readonly locker: StagingXcmVersionedMultiLocation; - readonly consumers: Vec>; - } - - /** @name PalletXcmError (582) */ - interface PalletXcmError extends Enum { - readonly isUnreachable: boolean; - readonly isSendFailure: boolean; - readonly isFiltered: boolean; - readonly isUnweighableMessage: boolean; - readonly isDestinationNotInvertible: boolean; - readonly isEmpty: boolean; - readonly isCannotReanchor: boolean; - readonly isTooManyAssets: boolean; - readonly isInvalidOrigin: boolean; - readonly isBadVersion: boolean; - readonly isBadLocation: boolean; - readonly isNoSubscription: boolean; - readonly isAlreadySubscribed: boolean; - readonly isInvalidAsset: boolean; - readonly isLowBalance: boolean; - readonly isTooManyLocks: boolean; - readonly isAccountNotSovereign: boolean; - readonly isFeesNotMet: boolean; - readonly isLockNotFound: boolean; - readonly isInUse: boolean; - readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed' | 'InvalidAsset' | 'LowBalance' | 'TooManyLocks' | 'AccountNotSovereign' | 'FeesNotMet' | 'LockNotFound' | 'InUse'; - } - - /** @name CumulusPalletXcmError (583) */ - type CumulusPalletXcmError = Null; - - /** @name CumulusPalletDmpQueueConfigData (584) */ - interface CumulusPalletDmpQueueConfigData extends Struct { - readonly maxIndividual: SpWeightsWeightV2Weight; - } - - /** @name CumulusPalletDmpQueuePageIndexData (585) */ - interface CumulusPalletDmpQueuePageIndexData extends Struct { - readonly beginUsed: u32; - readonly endUsed: u32; - readonly overweightCount: u64; - } - - /** @name CumulusPalletDmpQueueError (588) */ - interface CumulusPalletDmpQueueError extends Enum { - readonly isUnknown: boolean; - readonly isOverLimit: boolean; - readonly type: 'Unknown' | 'OverLimit'; - } - - /** @name PalletUniqueError (592) */ - interface PalletUniqueError extends Enum { - readonly isCollectionDecimalPointLimitExceeded: boolean; - readonly isEmptyArgument: boolean; - readonly isRepartitionCalledOnNonRefungibleCollection: boolean; - readonly type: 'CollectionDecimalPointLimitExceeded' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection'; - } - - /** @name PalletConfigurationError (593) */ - interface PalletConfigurationError extends Enum { - readonly isInconsistentConfiguration: boolean; - readonly type: 'InconsistentConfiguration'; - } - - /** @name UpDataStructsCollection (594) */ - interface UpDataStructsCollection extends Struct { - readonly owner: AccountId32; - readonly mode: UpDataStructsCollectionMode; - readonly name: Vec; - readonly description: Vec; - readonly tokenPrefix: Bytes; - readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; - readonly limits: UpDataStructsCollectionLimits; - readonly permissions: UpDataStructsCollectionPermissions; - readonly flags: U8aFixed; - } - - /** @name UpDataStructsSponsorshipStateAccountId32 (595) */ - interface UpDataStructsSponsorshipStateAccountId32 extends Enum { - readonly isDisabled: boolean; - readonly isUnconfirmed: boolean; - readonly asUnconfirmed: AccountId32; - readonly isConfirmed: boolean; - readonly asConfirmed: AccountId32; - readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; - } - - /** @name UpDataStructsProperties (596) */ - interface UpDataStructsProperties extends Struct { - readonly map: UpDataStructsPropertiesMapBoundedVec; - readonly consumedSpace: u32; - readonly reserved: u32; - } - - /** @name UpDataStructsPropertiesMapBoundedVec (597) */ - interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap {} - - /** @name UpDataStructsPropertiesMapPropertyPermission (602) */ - interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap {} - - /** @name UpDataStructsCollectionStats (609) */ - interface UpDataStructsCollectionStats extends Struct { - readonly created: u32; - readonly destroyed: u32; - readonly alive: u32; - } - - /** @name UpDataStructsTokenChild (610) */ - interface UpDataStructsTokenChild extends Struct { - readonly token: u32; - readonly collection: u32; - } - - /** @name PhantomTypeUpDataStructs (611) */ - interface PhantomTypeUpDataStructs extends Vec> {} - - /** @name UpDataStructsTokenData (613) */ - interface UpDataStructsTokenData extends Struct { - readonly properties: Vec; - readonly owner: Option; - readonly pieces: u128; - } - - /** @name UpDataStructsRpcCollection (614) */ - interface UpDataStructsRpcCollection extends Struct { - readonly owner: AccountId32; - readonly mode: UpDataStructsCollectionMode; - readonly name: Vec; - readonly description: Vec; - readonly tokenPrefix: Bytes; - readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; - readonly limits: UpDataStructsCollectionLimits; - readonly permissions: UpDataStructsCollectionPermissions; - readonly tokenPropertyPermissions: Vec; - readonly properties: Vec; - readonly readOnly: bool; - readonly flags: UpDataStructsRpcCollectionFlags; - } - - /** @name UpDataStructsRpcCollectionFlags (615) */ - interface UpDataStructsRpcCollectionFlags extends Struct { - readonly foreign: bool; - readonly erc721metadata: bool; - } - - /** @name UpPovEstimateRpcPovInfo (616) */ - interface UpPovEstimateRpcPovInfo extends Struct { - readonly proofSize: u64; - readonly compactProofSize: u64; - readonly compressedProofSize: u64; - readonly results: Vec, SpRuntimeTransactionValidityTransactionValidityError>>; - readonly keyValues: Vec; - } - - /** @name SpRuntimeTransactionValidityTransactionValidityError (619) */ - interface SpRuntimeTransactionValidityTransactionValidityError extends Enum { - readonly isInvalid: boolean; - readonly asInvalid: SpRuntimeTransactionValidityInvalidTransaction; - readonly isUnknown: boolean; - readonly asUnknown: SpRuntimeTransactionValidityUnknownTransaction; - readonly type: 'Invalid' | 'Unknown'; - } - - /** @name SpRuntimeTransactionValidityInvalidTransaction (620) */ - interface SpRuntimeTransactionValidityInvalidTransaction extends Enum { - readonly isCall: boolean; - readonly isPayment: boolean; - readonly isFuture: boolean; - readonly isStale: boolean; - readonly isBadProof: boolean; - readonly isAncientBirthBlock: boolean; - readonly isExhaustsResources: boolean; - readonly isCustom: boolean; - readonly asCustom: u8; - readonly isBadMandatory: boolean; - readonly isMandatoryValidation: boolean; - readonly isBadSigner: boolean; - readonly type: 'Call' | 'Payment' | 'Future' | 'Stale' | 'BadProof' | 'AncientBirthBlock' | 'ExhaustsResources' | 'Custom' | 'BadMandatory' | 'MandatoryValidation' | 'BadSigner'; - } - - /** @name SpRuntimeTransactionValidityUnknownTransaction (621) */ - interface SpRuntimeTransactionValidityUnknownTransaction extends Enum { - readonly isCannotLookup: boolean; - readonly isNoUnsignedValidator: boolean; - readonly isCustom: boolean; - readonly asCustom: u8; - readonly type: 'CannotLookup' | 'NoUnsignedValidator' | 'Custom'; - } - - /** @name UpPovEstimateRpcTrieKeyValue (623) */ - interface UpPovEstimateRpcTrieKeyValue extends Struct { - readonly key: Bytes; - readonly value: Bytes; - } - - /** @name PalletCommonError (625) */ - interface PalletCommonError extends Enum { - readonly isCollectionNotFound: boolean; - readonly isMustBeTokenOwner: boolean; - readonly isNoPermission: boolean; - readonly isCantDestroyNotEmptyCollection: boolean; - readonly isPublicMintingNotAllowed: boolean; - readonly isAddressNotInAllowlist: boolean; - readonly isCollectionNameLimitExceeded: boolean; - readonly isCollectionDescriptionLimitExceeded: boolean; - readonly isCollectionTokenPrefixLimitExceeded: boolean; - readonly isTotalCollectionsLimitExceeded: boolean; - readonly isCollectionAdminCountExceeded: boolean; - readonly isCollectionLimitBoundsExceeded: boolean; - readonly isOwnerPermissionsCantBeReverted: boolean; - readonly isTransferNotAllowed: boolean; - readonly isAccountTokenLimitExceeded: boolean; - readonly isCollectionTokenLimitExceeded: boolean; - readonly isMetadataFlagFrozen: boolean; - readonly isTokenNotFound: boolean; - readonly isTokenValueTooLow: boolean; - readonly isApprovedValueTooLow: boolean; - readonly isCantApproveMoreThanOwned: boolean; - readonly isAddressIsNotEthMirror: boolean; - readonly isAddressIsZero: boolean; - readonly isUnsupportedOperation: boolean; - readonly isNotSufficientFounds: boolean; - readonly isUserIsNotAllowedToNest: boolean; - readonly isSourceCollectionIsNotAllowedToNest: boolean; - readonly isCollectionFieldSizeExceeded: boolean; - readonly isNoSpaceForProperty: boolean; - readonly isPropertyLimitReached: boolean; - readonly isPropertyKeyIsTooLong: boolean; - readonly isInvalidCharacterInPropertyKey: boolean; - readonly isEmptyPropertyKey: boolean; - readonly isCollectionIsExternal: boolean; - readonly isCollectionIsInternal: boolean; - readonly isConfirmSponsorshipFail: boolean; - readonly isUserIsNotCollectionAdmin: boolean; - readonly isFungibleItemsHaveNoId: boolean; - readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsNotEthMirror' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal' | 'ConfirmSponsorshipFail' | 'UserIsNotCollectionAdmin' | 'FungibleItemsHaveNoId'; - } - - /** @name PalletFungibleError (627) */ - interface PalletFungibleError extends Enum { - readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean; - readonly isFungibleItemsDontHaveData: boolean; - readonly isFungibleDisallowsNesting: boolean; - readonly isSettingPropertiesNotAllowed: boolean; - readonly isSettingAllowanceForAllNotAllowed: boolean; - readonly isFungibleTokensAreAlwaysValid: boolean; - readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingAllowanceForAllNotAllowed' | 'FungibleTokensAreAlwaysValid'; - } - - /** @name PalletRefungibleError (632) */ - interface PalletRefungibleError extends Enum { - readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean; - readonly isWrongRefungiblePieces: boolean; - readonly isRepartitionWhileNotOwningAllPieces: boolean; - readonly isRefungibleDisallowsNesting: boolean; - readonly isSettingPropertiesNotAllowed: boolean; - readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; - } - - /** @name PalletNonfungibleItemData (633) */ - interface PalletNonfungibleItemData extends Struct { - readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; - } - - /** @name UpDataStructsPropertyScope (635) */ - interface UpDataStructsPropertyScope extends Enum { - readonly isNone: boolean; - readonly isRmrk: boolean; - readonly type: 'None' | 'Rmrk'; - } - - /** @name PalletNonfungibleError (638) */ - interface PalletNonfungibleError extends Enum { - readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean; - readonly isNonfungibleItemsHaveNoAmount: boolean; - readonly isCantBurnNftWithChildren: boolean; - readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren'; - } - - /** @name PalletStructureError (639) */ - interface PalletStructureError extends Enum { - readonly isOuroborosDetected: boolean; - readonly isDepthLimit: boolean; - readonly isBreadthLimit: boolean; - readonly isTokenNotFound: boolean; - readonly isCantNestTokenUnderCollection: boolean; - readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound' | 'CantNestTokenUnderCollection'; - } - - /** @name PalletAppPromotionError (644) */ - interface PalletAppPromotionError extends Enum { - readonly isAdminNotSet: boolean; - readonly isNoPermission: boolean; - readonly isNotSufficientFunds: boolean; - readonly isPendingForBlockOverflow: boolean; - readonly isSponsorNotSet: boolean; - readonly isInsufficientStakedBalance: boolean; - readonly isInconsistencyState: boolean; - readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'InsufficientStakedBalance' | 'InconsistencyState'; - } - - /** @name PalletForeignAssetsModuleError (645) */ - interface PalletForeignAssetsModuleError extends Enum { - readonly isBadLocation: boolean; - readonly isMultiLocationExisted: boolean; - readonly isAssetIdNotExists: boolean; - readonly isAssetIdExisted: boolean; - readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted'; - } - - /** @name PalletEvmCodeMetadata (646) */ - interface PalletEvmCodeMetadata extends Struct { - readonly size_: u64; - readonly hash_: H256; - } - - /** @name PalletEvmError (648) */ - interface PalletEvmError extends Enum { - readonly isBalanceLow: boolean; - readonly isFeeOverflow: boolean; - readonly isPaymentOverflow: boolean; - readonly isWithdrawFailed: boolean; - readonly isGasPriceTooLow: boolean; - readonly isInvalidNonce: boolean; - readonly isGasLimitTooLow: boolean; - readonly isGasLimitTooHigh: boolean; - readonly isUndefined: boolean; - readonly isReentrancy: boolean; - readonly isTransactionMustComeFromEOA: boolean; - readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce' | 'GasLimitTooLow' | 'GasLimitTooHigh' | 'Undefined' | 'Reentrancy' | 'TransactionMustComeFromEOA'; - } - - /** @name FpRpcTransactionStatus (651) */ - interface FpRpcTransactionStatus extends Struct { - readonly transactionHash: H256; - readonly transactionIndex: u32; - readonly from: H160; - readonly to: Option; - readonly contractAddress: Option; - readonly logs: Vec; - readonly logsBloom: EthbloomBloom; - } - - /** @name EthbloomBloom (653) */ - interface EthbloomBloom extends U8aFixed {} - - /** @name EthereumReceiptReceiptV3 (655) */ - interface EthereumReceiptReceiptV3 extends Enum { - readonly isLegacy: boolean; - readonly asLegacy: EthereumReceiptEip658ReceiptData; - readonly isEip2930: boolean; - readonly asEip2930: EthereumReceiptEip658ReceiptData; - readonly isEip1559: boolean; - readonly asEip1559: EthereumReceiptEip658ReceiptData; - readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; - } - - /** @name EthereumReceiptEip658ReceiptData (656) */ - interface EthereumReceiptEip658ReceiptData extends Struct { - readonly statusCode: u8; - readonly usedGas: U256; - readonly logsBloom: EthbloomBloom; - readonly logs: Vec; - } - - /** @name EthereumBlock (657) */ - interface EthereumBlock extends Struct { - readonly header: EthereumHeader; - readonly transactions: Vec; - readonly ommers: Vec; - } - - /** @name EthereumHeader (658) */ - interface EthereumHeader extends Struct { - readonly parentHash: H256; - readonly ommersHash: H256; - readonly beneficiary: H160; - readonly stateRoot: H256; - readonly transactionsRoot: H256; - readonly receiptsRoot: H256; - readonly logsBloom: EthbloomBloom; - readonly difficulty: U256; - readonly number: U256; - readonly gasLimit: U256; - readonly gasUsed: U256; - readonly timestamp: u64; - readonly extraData: Bytes; - readonly mixHash: H256; - readonly nonce: EthereumTypesHashH64; - } - - /** @name EthereumTypesHashH64 (659) */ - interface EthereumTypesHashH64 extends U8aFixed {} - - /** @name PalletEthereumError (664) */ - interface PalletEthereumError extends Enum { - readonly isInvalidSignature: boolean; - readonly isPreLogExists: boolean; - readonly type: 'InvalidSignature' | 'PreLogExists'; - } - - /** @name PalletEvmCoderSubstrateError (665) */ - interface PalletEvmCoderSubstrateError extends Enum { - readonly isOutOfGas: boolean; - readonly isOutOfFund: boolean; - readonly type: 'OutOfGas' | 'OutOfFund'; - } - - /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (666) */ - interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum { - readonly isDisabled: boolean; - readonly isUnconfirmed: boolean; - readonly asUnconfirmed: PalletEvmAccountBasicCrossAccountIdRepr; - readonly isConfirmed: boolean; - readonly asConfirmed: PalletEvmAccountBasicCrossAccountIdRepr; - readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; - } - - /** @name PalletEvmContractHelpersSponsoringModeT (667) */ - interface PalletEvmContractHelpersSponsoringModeT extends Enum { - readonly isDisabled: boolean; - readonly isAllowlisted: boolean; - readonly isGenerous: boolean; - readonly type: 'Disabled' | 'Allowlisted' | 'Generous'; - } - - /** @name PalletEvmContractHelpersError (673) */ - interface PalletEvmContractHelpersError extends Enum { - readonly isNoPermission: boolean; - readonly isNoPendingSponsor: boolean; - readonly isTooManyMethodsHaveSponsoredLimit: boolean; - readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit'; - } - - /** @name PalletEvmMigrationError (674) */ - interface PalletEvmMigrationError extends Enum { - readonly isAccountNotEmpty: boolean; - readonly isAccountIsNotMigrating: boolean; - readonly isBadEvent: boolean; - readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating' | 'BadEvent'; - } - - /** @name PalletMaintenanceError (675) */ - type PalletMaintenanceError = Null; - - /** @name PalletUtilityError (676) */ - interface PalletUtilityError extends Enum { - readonly isTooManyCalls: boolean; - readonly type: 'TooManyCalls'; - } - - /** @name PalletTestUtilsError (677) */ - interface PalletTestUtilsError extends Enum { - readonly isTestPalletDisabled: boolean; - readonly isTriggerRollback: boolean; - readonly type: 'TestPalletDisabled' | 'TriggerRollback'; - } - - /** @name SpRuntimeMultiSignature (679) */ - interface SpRuntimeMultiSignature extends Enum { - readonly isEd25519: boolean; - readonly asEd25519: SpCoreEd25519Signature; - readonly isSr25519: boolean; - readonly asSr25519: SpCoreSr25519Signature; - readonly isEcdsa: boolean; - readonly asEcdsa: SpCoreEcdsaSignature; - readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa'; - } - - /** @name SpCoreEd25519Signature (680) */ - interface SpCoreEd25519Signature extends U8aFixed {} - - /** @name SpCoreSr25519Signature (682) */ - interface SpCoreSr25519Signature extends U8aFixed {} - - /** @name SpCoreEcdsaSignature (683) */ - interface SpCoreEcdsaSignature extends U8aFixed {} - - /** @name FrameSystemExtensionsCheckSpecVersion (686) */ - type FrameSystemExtensionsCheckSpecVersion = Null; - - /** @name FrameSystemExtensionsCheckTxVersion (687) */ - type FrameSystemExtensionsCheckTxVersion = Null; - - /** @name FrameSystemExtensionsCheckGenesis (688) */ - type FrameSystemExtensionsCheckGenesis = Null; - - /** @name FrameSystemExtensionsCheckNonce (691) */ - interface FrameSystemExtensionsCheckNonce extends Compact {} - - /** @name FrameSystemExtensionsCheckWeight (692) */ - type FrameSystemExtensionsCheckWeight = Null; - - /** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance (693) */ - type OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance = Null; - - /** @name OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls (694) */ - type OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls = Null; - - /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (695) */ - interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact {} - - /** @name OpalRuntimeRuntime (696) */ - type OpalRuntimeRuntime = Null; - - /** @name PalletEthereumFakeTransactionFinalizer (697) */ - type PalletEthereumFakeTransactionFinalizer = Null; - -} // declare module --- a/js-packages/types/src/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -export * from './appPromotion/types.js'; -export * from './default/types.js'; -export * from './povinfo/types.js'; -export * from './unique/types.js'; --- a/js-packages/types/src/unique/definitions.ts +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -type RpcParam = { - name: string; - type: string; - isOptional?: true; -}; - -const CROSS_ACCOUNT_ID_TYPE = 'PalletEvmAccountBasicCrossAccountIdRepr'; - -const collectionParam = {name: 'collection', type: 'u32'}; -const tokenParam = {name: 'tokenId', type: 'u32'}; -const propertyKeysParam = {name: 'propertyKeys', type: 'Option>', isOptional: true}; -const crossAccountParam = (name = 'account') => ({name, type: CROSS_ACCOUNT_ID_TYPE}); -const atParam = {name: 'at', type: 'Hash', isOptional: true}; - -const fun = (description: string, params: RpcParam[], type: string) => ({ - description, - params: [...params, atParam], - type, -}); - -export default { - types: {}, - rpc: { - accountTokens: fun( - 'Get tokens owned by an account in a collection', - [collectionParam, crossAccountParam()], - 'Vec', - ), - collectionTokens: fun( - 'Get tokens contained within a collection', - [collectionParam], - 'Vec', - ), - tokenExists: fun( - 'Check if the token exists', - [collectionParam, tokenParam], - 'bool', - ), - - tokenOwner: fun( - 'Get the token owner', - [collectionParam, tokenParam], - `Option<${CROSS_ACCOUNT_ID_TYPE}>`, - ), - topmostTokenOwner: fun( - 'Get the topmost token owner in the hierarchy of a possibly nested token', - [collectionParam, tokenParam], - `Option<${CROSS_ACCOUNT_ID_TYPE}>`, - ), - tokenOwners: fun( - 'Returns 10 tokens owners in no particular order', - [collectionParam, tokenParam], - `Vec<${CROSS_ACCOUNT_ID_TYPE}>`, - ), - tokenChildren: fun( - 'Get tokens nested directly into the token', - [collectionParam, tokenParam], - 'Vec', - ), - - collectionProperties: fun( - 'Get collection properties, optionally limited to the provided keys', - [collectionParam, propertyKeysParam], - 'Vec', - ), - tokenProperties: fun( - 'Get token properties, optionally limited to the provided keys', - [collectionParam, tokenParam, propertyKeysParam], - 'Vec', - ), - propertyPermissions: fun( - 'Get property permissions, optionally limited to the provided keys', - [collectionParam, propertyKeysParam], - 'Vec', - ), - - constMetadata: fun( - 'Get token constant metadata', - [collectionParam, tokenParam], - 'Vec', - ), - variableMetadata: fun( - 'Get token variable metadata', - [collectionParam, tokenParam], - 'Vec', - ), - - tokenData: fun( - 'Get token data, including properties, optionally limited to the provided keys, and total pieces for an RFT', - [collectionParam, tokenParam, propertyKeysParam], - 'UpDataStructsTokenData', - ), - totalSupply: fun( - 'Get the amount of distinctive tokens present in a collection', - [collectionParam], - 'u32', - ), - - accountBalance: fun( - 'Get the amount of any user tokens owned by an account', - [collectionParam, crossAccountParam()], - 'u32', - ), - balance: fun( - 'Get the amount of a specific token owned by an account', - [collectionParam, crossAccountParam(), tokenParam], - 'u128', - ), - allowance: fun( - 'Get the amount of currently possible sponsored transactions on a token for the fee to be taken off a sponsor', - [collectionParam, crossAccountParam('sender'), crossAccountParam('spender'), tokenParam], - 'u128', - ), - - adminlist: fun( - 'Get the list of admin accounts of a collection', - [collectionParam], - 'Vec', - ), - allowlist: fun( - 'Get the list of accounts allowed to operate within a collection', - [collectionParam], - 'Vec', - ), - allowed: fun( - 'Check if a user is allowed to operate within a collection', - [collectionParam, crossAccountParam()], - 'bool', - ), - - lastTokenId: fun( - 'Get the last token ID created in a collection', - [collectionParam], - 'u32', - ), - collectionById: fun( - 'Get a collection by the specified ID', - [collectionParam], - 'Option', - ), - collectionStats: fun( - 'Get chain stats about collections', - [], - 'UpDataStructsCollectionStats', - ), - - nextSponsored: fun( - 'Get the number of blocks until sponsoring a transaction is available', - [collectionParam, crossAccountParam(), tokenParam], - 'Option', - ), - effectiveCollectionLimits: fun( - 'Get effective collection limits', - [collectionParam], - 'Option', - ), - totalPieces: fun( - 'Get the total amount of pieces of an RFT', - [collectionParam, tokenParam], - 'Option', - ), - allowanceForAll: fun( - 'Tells whether the given `owner` approves the `operator`.', - [collectionParam, crossAccountParam('owner'), crossAccountParam('operator')], - 'Option', - ), - }, -}; --- a/js-packages/types/src/unique/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -export * from './types.js'; --- a/js-packages/types/src/unique/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Auto-generated via `yarn polkadot-types-from-defs`, do not edit -/* eslint-disable */ - -export type PHANTOM_UNIQUE = 'unique'; --- a/js-packages/types/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.packages.json", - "compilerOptions": { - "rootDir": "./src", - "outDir": "dist" - } -} \ No newline at end of file --- /dev/null +++ b/js-packages/types/types-lookup.ts @@ -0,0 +1,5540 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +// import type lookup before we augment - in some environments +// this is required to allow for ambient/previous definitions +import '@polkadot/types/lookup'; + +import type { Data } from '@polkadot/types'; +import type { BTreeMap, BTreeSet, Bytes, Compact, Enum, Null, Option, Result, Set, Struct, Text, U256, U8aFixed, Vec, bool, i64, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; +import type { ITuple } from '@polkadot/types-codec/types'; +import type { Vote } from '@polkadot/types/interfaces/elections'; +import type { AccountId32, Call, H160, H256, MultiAddress, Perbill } from '@polkadot/types/interfaces/runtime'; +import type { Event } from '@polkadot/types/interfaces/system'; + +declare module '@polkadot/types/lookup' { + /** @name FrameSystemAccountInfo (3) */ + interface FrameSystemAccountInfo extends Struct { + readonly nonce: u32; + readonly consumers: u32; + readonly providers: u32; + readonly sufficients: u32; + readonly data: PalletBalancesAccountData; + } + + /** @name PalletBalancesAccountData (5) */ + interface PalletBalancesAccountData extends Struct { + readonly free: u128; + readonly reserved: u128; + readonly frozen: u128; + readonly flags: u128; + } + + /** @name FrameSupportDispatchPerDispatchClassWeight (8) */ + interface FrameSupportDispatchPerDispatchClassWeight extends Struct { + readonly normal: SpWeightsWeightV2Weight; + readonly operational: SpWeightsWeightV2Weight; + readonly mandatory: SpWeightsWeightV2Weight; + } + + /** @name SpWeightsWeightV2Weight (9) */ + interface SpWeightsWeightV2Weight extends Struct { + readonly refTime: Compact; + readonly proofSize: Compact; + } + + /** @name SpRuntimeDigest (14) */ + interface SpRuntimeDigest extends Struct { + readonly logs: Vec; + } + + /** @name SpRuntimeDigestDigestItem (16) */ + interface SpRuntimeDigestDigestItem extends Enum { + readonly isOther: boolean; + readonly asOther: Bytes; + readonly isConsensus: boolean; + readonly asConsensus: ITuple<[U8aFixed, Bytes]>; + readonly isSeal: boolean; + readonly asSeal: ITuple<[U8aFixed, Bytes]>; + readonly isPreRuntime: boolean; + readonly asPreRuntime: ITuple<[U8aFixed, Bytes]>; + readonly isRuntimeEnvironmentUpdated: boolean; + readonly type: 'Other' | 'Consensus' | 'Seal' | 'PreRuntime' | 'RuntimeEnvironmentUpdated'; + } + + /** @name FrameSystemEventRecord (19) */ + interface FrameSystemEventRecord extends Struct { + readonly phase: FrameSystemPhase; + readonly event: Event; + readonly topics: Vec; + } + + /** @name FrameSystemEvent (21) */ + interface FrameSystemEvent extends Enum { + readonly isExtrinsicSuccess: boolean; + readonly asExtrinsicSuccess: { + readonly dispatchInfo: FrameSupportDispatchDispatchInfo; + } & Struct; + readonly isExtrinsicFailed: boolean; + readonly asExtrinsicFailed: { + readonly dispatchError: SpRuntimeDispatchError; + readonly dispatchInfo: FrameSupportDispatchDispatchInfo; + } & Struct; + readonly isCodeUpdated: boolean; + readonly isNewAccount: boolean; + readonly asNewAccount: { + readonly account: AccountId32; + } & Struct; + readonly isKilledAccount: boolean; + readonly asKilledAccount: { + readonly account: AccountId32; + } & Struct; + readonly isRemarked: boolean; + readonly asRemarked: { + readonly sender: AccountId32; + readonly hash_: H256; + } & Struct; + readonly type: 'ExtrinsicSuccess' | 'ExtrinsicFailed' | 'CodeUpdated' | 'NewAccount' | 'KilledAccount' | 'Remarked'; + } + + /** @name FrameSupportDispatchDispatchInfo (22) */ + interface FrameSupportDispatchDispatchInfo extends Struct { + readonly weight: SpWeightsWeightV2Weight; + readonly class: FrameSupportDispatchDispatchClass; + readonly paysFee: FrameSupportDispatchPays; + } + + /** @name FrameSupportDispatchDispatchClass (23) */ + interface FrameSupportDispatchDispatchClass extends Enum { + readonly isNormal: boolean; + readonly isOperational: boolean; + readonly isMandatory: boolean; + readonly type: 'Normal' | 'Operational' | 'Mandatory'; + } + + /** @name FrameSupportDispatchPays (24) */ + interface FrameSupportDispatchPays extends Enum { + readonly isYes: boolean; + readonly isNo: boolean; + readonly type: 'Yes' | 'No'; + } + + /** @name SpRuntimeDispatchError (25) */ + interface SpRuntimeDispatchError extends Enum { + readonly isOther: boolean; + readonly isCannotLookup: boolean; + readonly isBadOrigin: boolean; + readonly isModule: boolean; + readonly asModule: SpRuntimeModuleError; + readonly isConsumerRemaining: boolean; + readonly isNoProviders: boolean; + readonly isTooManyConsumers: boolean; + readonly isToken: boolean; + readonly asToken: SpRuntimeTokenError; + readonly isArithmetic: boolean; + readonly asArithmetic: SpArithmeticArithmeticError; + readonly isTransactional: boolean; + readonly asTransactional: SpRuntimeTransactionalError; + readonly isExhausted: boolean; + readonly isCorruption: boolean; + readonly isUnavailable: boolean; + readonly isRootNotAllowed: boolean; + readonly type: 'Other' | 'CannotLookup' | 'BadOrigin' | 'Module' | 'ConsumerRemaining' | 'NoProviders' | 'TooManyConsumers' | 'Token' | 'Arithmetic' | 'Transactional' | 'Exhausted' | 'Corruption' | 'Unavailable' | 'RootNotAllowed'; + } + + /** @name SpRuntimeModuleError (26) */ + interface SpRuntimeModuleError extends Struct { + readonly index: u8; + readonly error: U8aFixed; + } + + /** @name SpRuntimeTokenError (27) */ + interface SpRuntimeTokenError extends Enum { + readonly isFundsUnavailable: boolean; + readonly isOnlyProvider: boolean; + readonly isBelowMinimum: boolean; + readonly isCannotCreate: boolean; + readonly isUnknownAsset: boolean; + readonly isFrozen: boolean; + readonly isUnsupported: boolean; + readonly isCannotCreateHold: boolean; + readonly isNotExpendable: boolean; + readonly isBlocked: boolean; + readonly type: 'FundsUnavailable' | 'OnlyProvider' | 'BelowMinimum' | 'CannotCreate' | 'UnknownAsset' | 'Frozen' | 'Unsupported' | 'CannotCreateHold' | 'NotExpendable' | 'Blocked'; + } + + /** @name SpArithmeticArithmeticError (28) */ + interface SpArithmeticArithmeticError extends Enum { + readonly isUnderflow: boolean; + readonly isOverflow: boolean; + readonly isDivisionByZero: boolean; + readonly type: 'Underflow' | 'Overflow' | 'DivisionByZero'; + } + + /** @name SpRuntimeTransactionalError (29) */ + interface SpRuntimeTransactionalError extends Enum { + readonly isLimitReached: boolean; + readonly isNoLayer: boolean; + readonly type: 'LimitReached' | 'NoLayer'; + } + + /** @name PalletStateTrieMigrationEvent (30) */ + interface PalletStateTrieMigrationEvent extends Enum { + readonly isMigrated: boolean; + readonly asMigrated: { + readonly top: u32; + readonly child: u32; + readonly compute: PalletStateTrieMigrationMigrationCompute; + } & Struct; + readonly isSlashed: boolean; + readonly asSlashed: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isAutoMigrationFinished: boolean; + readonly isHalted: boolean; + readonly asHalted: { + readonly error: PalletStateTrieMigrationError; + } & Struct; + readonly type: 'Migrated' | 'Slashed' | 'AutoMigrationFinished' | 'Halted'; + } + + /** @name PalletStateTrieMigrationMigrationCompute (31) */ + interface PalletStateTrieMigrationMigrationCompute extends Enum { + readonly isSigned: boolean; + readonly isAuto: boolean; + readonly type: 'Signed' | 'Auto'; + } + + /** @name PalletStateTrieMigrationError (32) */ + interface PalletStateTrieMigrationError extends Enum { + readonly isMaxSignedLimits: boolean; + readonly isKeyTooLong: boolean; + readonly isNotEnoughFunds: boolean; + readonly isBadWitness: boolean; + readonly isSignedMigrationNotAllowed: boolean; + readonly isBadChildRoot: boolean; + readonly type: 'MaxSignedLimits' | 'KeyTooLong' | 'NotEnoughFunds' | 'BadWitness' | 'SignedMigrationNotAllowed' | 'BadChildRoot'; + } + + /** @name CumulusPalletParachainSystemEvent (33) */ + interface CumulusPalletParachainSystemEvent extends Enum { + readonly isValidationFunctionStored: boolean; + readonly isValidationFunctionApplied: boolean; + readonly asValidationFunctionApplied: { + readonly relayChainBlockNum: u32; + } & Struct; + readonly isValidationFunctionDiscarded: boolean; + readonly isUpgradeAuthorized: boolean; + readonly asUpgradeAuthorized: { + readonly codeHash: H256; + } & Struct; + readonly isDownwardMessagesReceived: boolean; + readonly asDownwardMessagesReceived: { + readonly count: u32; + } & Struct; + readonly isDownwardMessagesProcessed: boolean; + readonly asDownwardMessagesProcessed: { + readonly weightUsed: SpWeightsWeightV2Weight; + readonly dmqHead: H256; + } & Struct; + readonly isUpwardMessageSent: boolean; + readonly asUpwardMessageSent: { + readonly messageHash: Option; + } & Struct; + readonly type: 'ValidationFunctionStored' | 'ValidationFunctionApplied' | 'ValidationFunctionDiscarded' | 'UpgradeAuthorized' | 'DownwardMessagesReceived' | 'DownwardMessagesProcessed' | 'UpwardMessageSent'; + } + + /** @name PalletCollatorSelectionEvent (35) */ + interface PalletCollatorSelectionEvent extends Enum { + readonly isInvulnerableAdded: boolean; + readonly asInvulnerableAdded: { + readonly invulnerable: AccountId32; + } & Struct; + readonly isInvulnerableRemoved: boolean; + readonly asInvulnerableRemoved: { + readonly invulnerable: AccountId32; + } & Struct; + readonly isLicenseObtained: boolean; + readonly asLicenseObtained: { + readonly accountId: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isLicenseReleased: boolean; + readonly asLicenseReleased: { + readonly accountId: AccountId32; + readonly depositReturned: u128; + } & Struct; + readonly isCandidateAdded: boolean; + readonly asCandidateAdded: { + readonly accountId: AccountId32; + } & Struct; + readonly isCandidateRemoved: boolean; + readonly asCandidateRemoved: { + readonly accountId: AccountId32; + } & Struct; + readonly type: 'InvulnerableAdded' | 'InvulnerableRemoved' | 'LicenseObtained' | 'LicenseReleased' | 'CandidateAdded' | 'CandidateRemoved'; + } + + /** @name PalletSessionEvent (36) */ + interface PalletSessionEvent extends Enum { + readonly isNewSession: boolean; + readonly asNewSession: { + readonly sessionIndex: u32; + } & Struct; + readonly type: 'NewSession'; + } + + /** @name PalletBalancesEvent (37) */ + interface PalletBalancesEvent extends Enum { + readonly isEndowed: boolean; + readonly asEndowed: { + readonly account: AccountId32; + readonly freeBalance: u128; + } & Struct; + readonly isDustLost: boolean; + readonly asDustLost: { + readonly account: AccountId32; + readonly amount: u128; + } & Struct; + readonly isTransfer: boolean; + readonly asTransfer: { + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + } & Struct; + readonly isBalanceSet: boolean; + readonly asBalanceSet: { + readonly who: AccountId32; + readonly free: u128; + } & Struct; + readonly isReserved: boolean; + readonly asReserved: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUnreserved: boolean; + readonly asUnreserved: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isReserveRepatriated: boolean; + readonly asReserveRepatriated: { + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + readonly destinationStatus: FrameSupportTokensMiscBalanceStatus; + } & Struct; + readonly isDeposit: boolean; + readonly asDeposit: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isWithdraw: boolean; + readonly asWithdraw: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isSlashed: boolean; + readonly asSlashed: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isMinted: boolean; + readonly asMinted: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isBurned: boolean; + readonly asBurned: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isSuspended: boolean; + readonly asSuspended: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isRestored: boolean; + readonly asRestored: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUpgraded: boolean; + readonly asUpgraded: { + readonly who: AccountId32; + } & Struct; + readonly isIssued: boolean; + readonly asIssued: { + readonly amount: u128; + } & Struct; + readonly isRescinded: boolean; + readonly asRescinded: { + readonly amount: u128; + } & Struct; + readonly isLocked: boolean; + readonly asLocked: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUnlocked: boolean; + readonly asUnlocked: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isFrozen: boolean; + readonly asFrozen: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isThawed: boolean; + readonly asThawed: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'BalanceSet' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'Deposit' | 'Withdraw' | 'Slashed' | 'Minted' | 'Burned' | 'Suspended' | 'Restored' | 'Upgraded' | 'Issued' | 'Rescinded' | 'Locked' | 'Unlocked' | 'Frozen' | 'Thawed'; + } + + /** @name FrameSupportTokensMiscBalanceStatus (38) */ + interface FrameSupportTokensMiscBalanceStatus extends Enum { + readonly isFree: boolean; + readonly isReserved: boolean; + readonly type: 'Free' | 'Reserved'; + } + + /** @name PalletTransactionPaymentEvent (39) */ + interface PalletTransactionPaymentEvent extends Enum { + readonly isTransactionFeePaid: boolean; + readonly asTransactionFeePaid: { + readonly who: AccountId32; + readonly actualFee: u128; + readonly tip: u128; + } & Struct; + readonly type: 'TransactionFeePaid'; + } + + /** @name PalletTreasuryEvent (40) */ + interface PalletTreasuryEvent extends Enum { + readonly isProposed: boolean; + readonly asProposed: { + readonly proposalIndex: u32; + } & Struct; + readonly isSpending: boolean; + readonly asSpending: { + readonly budgetRemaining: u128; + } & Struct; + readonly isAwarded: boolean; + readonly asAwarded: { + readonly proposalIndex: u32; + readonly award: u128; + readonly account: AccountId32; + } & Struct; + readonly isRejected: boolean; + readonly asRejected: { + readonly proposalIndex: u32; + readonly slashed: u128; + } & Struct; + readonly isBurnt: boolean; + readonly asBurnt: { + readonly burntFunds: u128; + } & Struct; + readonly isRollover: boolean; + readonly asRollover: { + readonly rolloverBalance: u128; + } & Struct; + readonly isDeposit: boolean; + readonly asDeposit: { + readonly value: u128; + } & Struct; + readonly isSpendApproved: boolean; + readonly asSpendApproved: { + readonly proposalIndex: u32; + readonly amount: u128; + readonly beneficiary: AccountId32; + } & Struct; + readonly isUpdatedInactive: boolean; + readonly asUpdatedInactive: { + readonly reactivated: u128; + readonly deactivated: u128; + } & Struct; + readonly type: 'Proposed' | 'Spending' | 'Awarded' | 'Rejected' | 'Burnt' | 'Rollover' | 'Deposit' | 'SpendApproved' | 'UpdatedInactive'; + } + + /** @name PalletSudoEvent (41) */ + interface PalletSudoEvent extends Enum { + readonly isSudid: boolean; + readonly asSudid: { + readonly sudoResult: Result; + } & Struct; + readonly isKeyChanged: boolean; + readonly asKeyChanged: { + readonly oldSudoer: Option; + } & Struct; + readonly isSudoAsDone: boolean; + readonly asSudoAsDone: { + readonly sudoResult: Result; + } & Struct; + readonly type: 'Sudid' | 'KeyChanged' | 'SudoAsDone'; + } + + /** @name OrmlVestingModuleEvent (45) */ + interface OrmlVestingModuleEvent extends Enum { + readonly isVestingScheduleAdded: boolean; + readonly asVestingScheduleAdded: { + readonly from: AccountId32; + readonly to: AccountId32; + readonly vestingSchedule: OrmlVestingVestingSchedule; + } & Struct; + readonly isClaimed: boolean; + readonly asClaimed: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isVestingSchedulesUpdated: boolean; + readonly asVestingSchedulesUpdated: { + readonly who: AccountId32; + } & Struct; + readonly type: 'VestingScheduleAdded' | 'Claimed' | 'VestingSchedulesUpdated'; + } + + /** @name OrmlVestingVestingSchedule (46) */ + interface OrmlVestingVestingSchedule extends Struct { + readonly start: u32; + readonly period: u32; + readonly periodCount: u32; + readonly perPeriod: Compact; + } + + /** @name OrmlXtokensModuleEvent (48) */ + interface OrmlXtokensModuleEvent extends Enum { + readonly isTransferredMultiAssets: boolean; + readonly asTransferredMultiAssets: { + readonly sender: AccountId32; + readonly assets: StagingXcmV3MultiassetMultiAssets; + readonly fee: StagingXcmV3MultiAsset; + readonly dest: StagingXcmV3MultiLocation; + } & Struct; + readonly type: 'TransferredMultiAssets'; + } + + /** @name StagingXcmV3MultiassetMultiAssets (49) */ + interface StagingXcmV3MultiassetMultiAssets extends Vec {} + + /** @name StagingXcmV3MultiAsset (51) */ + interface StagingXcmV3MultiAsset extends Struct { + readonly id: StagingXcmV3MultiassetAssetId; + readonly fun: StagingXcmV3MultiassetFungibility; + } + + /** @name StagingXcmV3MultiassetAssetId (52) */ + interface StagingXcmV3MultiassetAssetId extends Enum { + readonly isConcrete: boolean; + readonly asConcrete: StagingXcmV3MultiLocation; + readonly isAbstract: boolean; + readonly asAbstract: U8aFixed; + readonly type: 'Concrete' | 'Abstract'; + } + + /** @name StagingXcmV3MultiLocation (53) */ + interface StagingXcmV3MultiLocation extends Struct { + readonly parents: u8; + readonly interior: StagingXcmV3Junctions; + } + + /** @name StagingXcmV3Junctions (54) */ + interface StagingXcmV3Junctions extends Enum { + readonly isHere: boolean; + readonly isX1: boolean; + readonly asX1: StagingXcmV3Junction; + readonly isX2: boolean; + readonly asX2: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX3: boolean; + readonly asX3: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX4: boolean; + readonly asX4: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX5: boolean; + readonly asX5: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX6: boolean; + readonly asX6: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX7: boolean; + readonly asX7: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly isX8: boolean; + readonly asX8: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>; + readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; + } + + /** @name StagingXcmV3Junction (55) */ + interface StagingXcmV3Junction extends Enum { + readonly isParachain: boolean; + readonly asParachain: Compact; + readonly isAccountId32: boolean; + readonly asAccountId32: { + readonly network: Option; + readonly id: U8aFixed; + } & Struct; + readonly isAccountIndex64: boolean; + readonly asAccountIndex64: { + readonly network: Option; + readonly index: Compact; + } & Struct; + readonly isAccountKey20: boolean; + readonly asAccountKey20: { + readonly network: Option; + readonly key: U8aFixed; + } & Struct; + readonly isPalletInstance: boolean; + readonly asPalletInstance: u8; + readonly isGeneralIndex: boolean; + readonly asGeneralIndex: Compact; + readonly isGeneralKey: boolean; + readonly asGeneralKey: { + readonly length: u8; + readonly data: U8aFixed; + } & Struct; + readonly isOnlyChild: boolean; + readonly isPlurality: boolean; + readonly asPlurality: { + readonly id: StagingXcmV3JunctionBodyId; + readonly part: StagingXcmV3JunctionBodyPart; + } & Struct; + readonly isGlobalConsensus: boolean; + readonly asGlobalConsensus: StagingXcmV3JunctionNetworkId; + readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality' | 'GlobalConsensus'; + } + + /** @name StagingXcmV3JunctionNetworkId (58) */ + interface StagingXcmV3JunctionNetworkId extends Enum { + readonly isByGenesis: boolean; + readonly asByGenesis: U8aFixed; + readonly isByFork: boolean; + readonly asByFork: { + readonly blockNumber: u64; + readonly blockHash: U8aFixed; + } & Struct; + readonly isPolkadot: boolean; + readonly isKusama: boolean; + readonly isWestend: boolean; + readonly isRococo: boolean; + readonly isWococo: boolean; + readonly isEthereum: boolean; + readonly asEthereum: { + readonly chainId: Compact; + } & Struct; + readonly isBitcoinCore: boolean; + readonly isBitcoinCash: boolean; + readonly type: 'ByGenesis' | 'ByFork' | 'Polkadot' | 'Kusama' | 'Westend' | 'Rococo' | 'Wococo' | 'Ethereum' | 'BitcoinCore' | 'BitcoinCash'; + } + + /** @name StagingXcmV3JunctionBodyId (60) */ + interface StagingXcmV3JunctionBodyId extends Enum { + readonly isUnit: boolean; + readonly isMoniker: boolean; + readonly asMoniker: U8aFixed; + readonly isIndex: boolean; + readonly asIndex: Compact; + readonly isExecutive: boolean; + readonly isTechnical: boolean; + readonly isLegislative: boolean; + readonly isJudicial: boolean; + readonly isDefense: boolean; + readonly isAdministration: boolean; + readonly isTreasury: boolean; + readonly type: 'Unit' | 'Moniker' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury'; + } + + /** @name StagingXcmV3JunctionBodyPart (61) */ + interface StagingXcmV3JunctionBodyPart extends Enum { + readonly isVoice: boolean; + readonly isMembers: boolean; + readonly asMembers: { + readonly count: Compact; + } & Struct; + readonly isFraction: boolean; + readonly asFraction: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly isAtLeastProportion: boolean; + readonly asAtLeastProportion: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly isMoreThanProportion: boolean; + readonly asMoreThanProportion: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion'; + } + + /** @name StagingXcmV3MultiassetFungibility (62) */ + interface StagingXcmV3MultiassetFungibility extends Enum { + readonly isFungible: boolean; + readonly asFungible: Compact; + readonly isNonFungible: boolean; + readonly asNonFungible: StagingXcmV3MultiassetAssetInstance; + readonly type: 'Fungible' | 'NonFungible'; + } + + /** @name StagingXcmV3MultiassetAssetInstance (63) */ + interface StagingXcmV3MultiassetAssetInstance extends Enum { + readonly isUndefined: boolean; + readonly isIndex: boolean; + readonly asIndex: Compact; + readonly isArray4: boolean; + readonly asArray4: U8aFixed; + readonly isArray8: boolean; + readonly asArray8: U8aFixed; + readonly isArray16: boolean; + readonly asArray16: U8aFixed; + readonly isArray32: boolean; + readonly asArray32: U8aFixed; + readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32'; + } + + /** @name OrmlTokensModuleEvent (66) */ + interface OrmlTokensModuleEvent extends Enum { + readonly isEndowed: boolean; + readonly asEndowed: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isDustLost: boolean; + readonly asDustLost: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isTransfer: boolean; + readonly asTransfer: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + } & Struct; + readonly isReserved: boolean; + readonly asReserved: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUnreserved: boolean; + readonly asUnreserved: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isReserveRepatriated: boolean; + readonly asReserveRepatriated: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly from: AccountId32; + readonly to: AccountId32; + readonly amount: u128; + readonly status: FrameSupportTokensMiscBalanceStatus; + } & Struct; + readonly isBalanceSet: boolean; + readonly asBalanceSet: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly free: u128; + readonly reserved: u128; + } & Struct; + readonly isTotalIssuanceSet: boolean; + readonly asTotalIssuanceSet: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: u128; + } & Struct; + readonly isWithdrawn: boolean; + readonly asWithdrawn: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isSlashed: boolean; + readonly asSlashed: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly freeAmount: u128; + readonly reservedAmount: u128; + } & Struct; + readonly isDeposited: boolean; + readonly asDeposited: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isLockSet: boolean; + readonly asLockSet: { + readonly lockId: U8aFixed; + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isLockRemoved: boolean; + readonly asLockRemoved: { + readonly lockId: U8aFixed; + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + } & Struct; + readonly isLocked: boolean; + readonly asLocked: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isUnlocked: boolean; + readonly asUnlocked: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isIssued: boolean; + readonly asIssued: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: u128; + } & Struct; + readonly isRescinded: boolean; + readonly asRescinded: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: u128; + } & Struct; + readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'BalanceSet' | 'TotalIssuanceSet' | 'Withdrawn' | 'Slashed' | 'Deposited' | 'LockSet' | 'LockRemoved' | 'Locked' | 'Unlocked' | 'Issued' | 'Rescinded'; + } + + /** @name PalletForeignAssetsAssetId (67) */ + interface PalletForeignAssetsAssetId extends Enum { + readonly isForeignAssetId: boolean; + readonly asForeignAssetId: u32; + readonly isNativeAssetId: boolean; + readonly asNativeAssetId: PalletForeignAssetsNativeCurrency; + readonly type: 'ForeignAssetId' | 'NativeAssetId'; + } + + /** @name PalletForeignAssetsNativeCurrency (68) */ + interface PalletForeignAssetsNativeCurrency extends Enum { + readonly isHere: boolean; + readonly isParent: boolean; + readonly type: 'Here' | 'Parent'; + } + + /** @name PalletIdentityEvent (69) */ + interface PalletIdentityEvent extends Enum { + readonly isIdentitySet: boolean; + readonly asIdentitySet: { + readonly who: AccountId32; + } & Struct; + readonly isIdentityCleared: boolean; + readonly asIdentityCleared: { + readonly who: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isIdentityKilled: boolean; + readonly asIdentityKilled: { + readonly who: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isIdentitiesInserted: boolean; + readonly asIdentitiesInserted: { + readonly amount: u32; + } & Struct; + readonly isIdentitiesRemoved: boolean; + readonly asIdentitiesRemoved: { + readonly amount: u32; + } & Struct; + readonly isJudgementRequested: boolean; + readonly asJudgementRequested: { + readonly who: AccountId32; + readonly registrarIndex: u32; + } & Struct; + readonly isJudgementUnrequested: boolean; + readonly asJudgementUnrequested: { + readonly who: AccountId32; + readonly registrarIndex: u32; + } & Struct; + readonly isJudgementGiven: boolean; + readonly asJudgementGiven: { + readonly target: AccountId32; + readonly registrarIndex: u32; + } & Struct; + readonly isRegistrarAdded: boolean; + readonly asRegistrarAdded: { + readonly registrarIndex: u32; + } & Struct; + readonly isSubIdentityAdded: boolean; + readonly asSubIdentityAdded: { + readonly sub: AccountId32; + readonly main: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isSubIdentityRemoved: boolean; + readonly asSubIdentityRemoved: { + readonly sub: AccountId32; + readonly main: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isSubIdentityRevoked: boolean; + readonly asSubIdentityRevoked: { + readonly sub: AccountId32; + readonly main: AccountId32; + readonly deposit: u128; + } & Struct; + readonly isSubIdentitiesInserted: boolean; + readonly asSubIdentitiesInserted: { + readonly amount: u32; + } & Struct; + readonly type: 'IdentitySet' | 'IdentityCleared' | 'IdentityKilled' | 'IdentitiesInserted' | 'IdentitiesRemoved' | 'JudgementRequested' | 'JudgementUnrequested' | 'JudgementGiven' | 'RegistrarAdded' | 'SubIdentityAdded' | 'SubIdentityRemoved' | 'SubIdentityRevoked' | 'SubIdentitiesInserted'; + } + + /** @name PalletPreimageEvent (70) */ + interface PalletPreimageEvent extends Enum { + readonly isNoted: boolean; + readonly asNoted: { + readonly hash_: H256; + } & Struct; + readonly isRequested: boolean; + readonly asRequested: { + readonly hash_: H256; + } & Struct; + readonly isCleared: boolean; + readonly asCleared: { + readonly hash_: H256; + } & Struct; + readonly type: 'Noted' | 'Requested' | 'Cleared'; + } + + /** @name PalletDemocracyEvent (71) */ + interface PalletDemocracyEvent extends Enum { + readonly isProposed: boolean; + readonly asProposed: { + readonly proposalIndex: u32; + readonly deposit: u128; + } & Struct; + readonly isTabled: boolean; + readonly asTabled: { + readonly proposalIndex: u32; + readonly deposit: u128; + } & Struct; + readonly isExternalTabled: boolean; + readonly isStarted: boolean; + readonly asStarted: { + readonly refIndex: u32; + readonly threshold: PalletDemocracyVoteThreshold; + } & Struct; + readonly isPassed: boolean; + readonly asPassed: { + readonly refIndex: u32; + } & Struct; + readonly isNotPassed: boolean; + readonly asNotPassed: { + readonly refIndex: u32; + } & Struct; + readonly isCancelled: boolean; + readonly asCancelled: { + readonly refIndex: u32; + } & Struct; + readonly isDelegated: boolean; + readonly asDelegated: { + readonly who: AccountId32; + readonly target: AccountId32; + } & Struct; + readonly isUndelegated: boolean; + readonly asUndelegated: { + readonly account: AccountId32; + } & Struct; + readonly isVetoed: boolean; + readonly asVetoed: { + readonly who: AccountId32; + readonly proposalHash: H256; + readonly until: u32; + } & Struct; + readonly isBlacklisted: boolean; + readonly asBlacklisted: { + readonly proposalHash: H256; + } & Struct; + readonly isVoted: boolean; + readonly asVoted: { + readonly voter: AccountId32; + readonly refIndex: u32; + readonly vote: PalletDemocracyVoteAccountVote; + } & Struct; + readonly isSeconded: boolean; + readonly asSeconded: { + readonly seconder: AccountId32; + readonly propIndex: u32; + } & Struct; + readonly isProposalCanceled: boolean; + readonly asProposalCanceled: { + readonly propIndex: u32; + } & Struct; + readonly isMetadataSet: boolean; + readonly asMetadataSet: { + readonly owner: PalletDemocracyMetadataOwner; + readonly hash_: H256; + } & Struct; + readonly isMetadataCleared: boolean; + readonly asMetadataCleared: { + readonly owner: PalletDemocracyMetadataOwner; + readonly hash_: H256; + } & Struct; + readonly isMetadataTransferred: boolean; + readonly asMetadataTransferred: { + readonly prevOwner: PalletDemocracyMetadataOwner; + readonly owner: PalletDemocracyMetadataOwner; + readonly hash_: H256; + } & Struct; + readonly type: 'Proposed' | 'Tabled' | 'ExternalTabled' | 'Started' | 'Passed' | 'NotPassed' | 'Cancelled' | 'Delegated' | 'Undelegated' | 'Vetoed' | 'Blacklisted' | 'Voted' | 'Seconded' | 'ProposalCanceled' | 'MetadataSet' | 'MetadataCleared' | 'MetadataTransferred'; + } + + /** @name PalletDemocracyVoteThreshold (72) */ + interface PalletDemocracyVoteThreshold extends Enum { + readonly isSuperMajorityApprove: boolean; + readonly isSuperMajorityAgainst: boolean; + readonly isSimpleMajority: boolean; + readonly type: 'SuperMajorityApprove' | 'SuperMajorityAgainst' | 'SimpleMajority'; + } + + /** @name PalletDemocracyVoteAccountVote (73) */ + interface PalletDemocracyVoteAccountVote extends Enum { + readonly isStandard: boolean; + readonly asStandard: { + readonly vote: Vote; + readonly balance: u128; + } & Struct; + readonly isSplit: boolean; + readonly asSplit: { + readonly aye: u128; + readonly nay: u128; + } & Struct; + readonly type: 'Standard' | 'Split'; + } + + /** @name PalletDemocracyMetadataOwner (75) */ + interface PalletDemocracyMetadataOwner extends Enum { + readonly isExternal: boolean; + readonly isProposal: boolean; + readonly asProposal: u32; + readonly isReferendum: boolean; + readonly asReferendum: u32; + readonly type: 'External' | 'Proposal' | 'Referendum'; + } + + /** @name PalletCollectiveEvent (76) */ + interface PalletCollectiveEvent extends Enum { + readonly isProposed: boolean; + readonly asProposed: { + readonly account: AccountId32; + readonly proposalIndex: u32; + readonly proposalHash: H256; + readonly threshold: u32; + } & Struct; + readonly isVoted: boolean; + readonly asVoted: { + readonly account: AccountId32; + readonly proposalHash: H256; + readonly voted: bool; + readonly yes: u32; + readonly no: u32; + } & Struct; + readonly isApproved: boolean; + readonly asApproved: { + readonly proposalHash: H256; + } & Struct; + readonly isDisapproved: boolean; + readonly asDisapproved: { + readonly proposalHash: H256; + } & Struct; + readonly isExecuted: boolean; + readonly asExecuted: { + readonly proposalHash: H256; + readonly result: Result; + } & Struct; + readonly isMemberExecuted: boolean; + readonly asMemberExecuted: { + readonly proposalHash: H256; + readonly result: Result; + } & Struct; + readonly isClosed: boolean; + readonly asClosed: { + readonly proposalHash: H256; + readonly yes: u32; + readonly no: u32; + } & Struct; + readonly type: 'Proposed' | 'Voted' | 'Approved' | 'Disapproved' | 'Executed' | 'MemberExecuted' | 'Closed'; + } + + /** @name PalletMembershipEvent (79) */ + interface PalletMembershipEvent extends Enum { + readonly isMemberAdded: boolean; + readonly isMemberRemoved: boolean; + readonly isMembersSwapped: boolean; + readonly isMembersReset: boolean; + readonly isKeyChanged: boolean; + readonly isDummy: boolean; + readonly type: 'MemberAdded' | 'MemberRemoved' | 'MembersSwapped' | 'MembersReset' | 'KeyChanged' | 'Dummy'; + } + + /** @name PalletRankedCollectiveEvent (81) */ + interface PalletRankedCollectiveEvent extends Enum { + readonly isMemberAdded: boolean; + readonly asMemberAdded: { + readonly who: AccountId32; + } & Struct; + readonly isRankChanged: boolean; + readonly asRankChanged: { + readonly who: AccountId32; + readonly rank: u16; + } & Struct; + readonly isMemberRemoved: boolean; + readonly asMemberRemoved: { + readonly who: AccountId32; + readonly rank: u16; + } & Struct; + readonly isVoted: boolean; + readonly asVoted: { + readonly who: AccountId32; + readonly poll: u32; + readonly vote: PalletRankedCollectiveVoteRecord; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly type: 'MemberAdded' | 'RankChanged' | 'MemberRemoved' | 'Voted'; + } + + /** @name PalletRankedCollectiveVoteRecord (83) */ + interface PalletRankedCollectiveVoteRecord extends Enum { + readonly isAye: boolean; + readonly asAye: u32; + readonly isNay: boolean; + readonly asNay: u32; + readonly type: 'Aye' | 'Nay'; + } + + /** @name PalletRankedCollectiveTally (84) */ + interface PalletRankedCollectiveTally extends Struct { + readonly bareAyes: u32; + readonly ayes: u32; + readonly nays: u32; + } + + /** @name PalletReferendaEvent (85) */ + interface PalletReferendaEvent extends Enum { + readonly isSubmitted: boolean; + readonly asSubmitted: { + readonly index: u32; + readonly track: u16; + readonly proposal: FrameSupportPreimagesBounded; + } & Struct; + readonly isDecisionDepositPlaced: boolean; + readonly asDecisionDepositPlaced: { + readonly index: u32; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isDecisionDepositRefunded: boolean; + readonly asDecisionDepositRefunded: { + readonly index: u32; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isDepositSlashed: boolean; + readonly asDepositSlashed: { + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isDecisionStarted: boolean; + readonly asDecisionStarted: { + readonly index: u32; + readonly track: u16; + readonly proposal: FrameSupportPreimagesBounded; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isConfirmStarted: boolean; + readonly asConfirmStarted: { + readonly index: u32; + } & Struct; + readonly isConfirmAborted: boolean; + readonly asConfirmAborted: { + readonly index: u32; + } & Struct; + readonly isConfirmed: boolean; + readonly asConfirmed: { + readonly index: u32; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isApproved: boolean; + readonly asApproved: { + readonly index: u32; + } & Struct; + readonly isRejected: boolean; + readonly asRejected: { + readonly index: u32; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isTimedOut: boolean; + readonly asTimedOut: { + readonly index: u32; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isCancelled: boolean; + readonly asCancelled: { + readonly index: u32; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isKilled: boolean; + readonly asKilled: { + readonly index: u32; + readonly tally: PalletRankedCollectiveTally; + } & Struct; + readonly isSubmissionDepositRefunded: boolean; + readonly asSubmissionDepositRefunded: { + readonly index: u32; + readonly who: AccountId32; + readonly amount: u128; + } & Struct; + readonly isMetadataSet: boolean; + readonly asMetadataSet: { + readonly index: u32; + readonly hash_: H256; + } & Struct; + readonly isMetadataCleared: boolean; + readonly asMetadataCleared: { + readonly index: u32; + readonly hash_: H256; + } & Struct; + readonly type: 'Submitted' | 'DecisionDepositPlaced' | 'DecisionDepositRefunded' | 'DepositSlashed' | 'DecisionStarted' | 'ConfirmStarted' | 'ConfirmAborted' | 'Confirmed' | 'Approved' | 'Rejected' | 'TimedOut' | 'Cancelled' | 'Killed' | 'SubmissionDepositRefunded' | 'MetadataSet' | 'MetadataCleared'; + } + + /** @name FrameSupportPreimagesBounded (86) */ + interface FrameSupportPreimagesBounded extends Enum { + readonly isLegacy: boolean; + readonly asLegacy: { + readonly hash_: H256; + } & Struct; + readonly isInline: boolean; + readonly asInline: Bytes; + readonly isLookup: boolean; + readonly asLookup: { + readonly hash_: H256; + readonly len: u32; + } & Struct; + readonly type: 'Legacy' | 'Inline' | 'Lookup'; + } + + /** @name FrameSystemCall (88) */ + interface FrameSystemCall extends Enum { + readonly isRemark: boolean; + readonly asRemark: { + readonly remark: Bytes; + } & Struct; + readonly isSetHeapPages: boolean; + readonly asSetHeapPages: { + readonly pages: u64; + } & Struct; + readonly isSetCode: boolean; + readonly asSetCode: { + readonly code: Bytes; + } & Struct; + readonly isSetCodeWithoutChecks: boolean; + readonly asSetCodeWithoutChecks: { + readonly code: Bytes; + } & Struct; + readonly isSetStorage: boolean; + readonly asSetStorage: { + readonly items: Vec>; + } & Struct; + readonly isKillStorage: boolean; + readonly asKillStorage: { + readonly keys_: Vec; + } & Struct; + readonly isKillPrefix: boolean; + readonly asKillPrefix: { + readonly prefix: Bytes; + readonly subkeys: u32; + } & Struct; + readonly isRemarkWithEvent: boolean; + readonly asRemarkWithEvent: { + readonly remark: Bytes; + } & Struct; + readonly type: 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent'; + } + + /** @name PalletStateTrieMigrationCall (92) */ + interface PalletStateTrieMigrationCall extends Enum { + readonly isControlAutoMigration: boolean; + readonly asControlAutoMigration: { + readonly maybeConfig: Option; + } & Struct; + readonly isContinueMigrate: boolean; + readonly asContinueMigrate: { + readonly limits: PalletStateTrieMigrationMigrationLimits; + readonly realSizeUpper: u32; + readonly witnessTask: PalletStateTrieMigrationMigrationTask; + } & Struct; + readonly isMigrateCustomTop: boolean; + readonly asMigrateCustomTop: { + readonly keys_: Vec; + readonly witnessSize: u32; + } & Struct; + readonly isMigrateCustomChild: boolean; + readonly asMigrateCustomChild: { + readonly root: Bytes; + readonly childKeys: Vec; + readonly totalSize: u32; + } & Struct; + readonly isSetSignedMaxLimits: boolean; + readonly asSetSignedMaxLimits: { + readonly limits: PalletStateTrieMigrationMigrationLimits; + } & Struct; + readonly isForceSetProgress: boolean; + readonly asForceSetProgress: { + readonly progressTop: PalletStateTrieMigrationProgress; + readonly progressChild: PalletStateTrieMigrationProgress; + } & Struct; + readonly type: 'ControlAutoMigration' | 'ContinueMigrate' | 'MigrateCustomTop' | 'MigrateCustomChild' | 'SetSignedMaxLimits' | 'ForceSetProgress'; + } + + /** @name PalletStateTrieMigrationMigrationLimits (94) */ + interface PalletStateTrieMigrationMigrationLimits extends Struct { + readonly size_: u32; + readonly item: u32; + } + + /** @name PalletStateTrieMigrationMigrationTask (95) */ + interface PalletStateTrieMigrationMigrationTask extends Struct { + readonly progressTop: PalletStateTrieMigrationProgress; + readonly progressChild: PalletStateTrieMigrationProgress; + readonly size_: u32; + readonly topItems: u32; + readonly childItems: u32; + } + + /** @name PalletStateTrieMigrationProgress (96) */ + interface PalletStateTrieMigrationProgress extends Enum { + readonly isToStart: boolean; + readonly isLastKey: boolean; + readonly asLastKey: Bytes; + readonly isComplete: boolean; + readonly type: 'ToStart' | 'LastKey' | 'Complete'; + } + + /** @name CumulusPalletParachainSystemCall (98) */ + interface CumulusPalletParachainSystemCall extends Enum { + readonly isSetValidationData: boolean; + readonly asSetValidationData: { + readonly data: CumulusPrimitivesParachainInherentParachainInherentData; + } & Struct; + readonly isSudoSendUpwardMessage: boolean; + readonly asSudoSendUpwardMessage: { + readonly message: Bytes; + } & Struct; + readonly isAuthorizeUpgrade: boolean; + readonly asAuthorizeUpgrade: { + readonly codeHash: H256; + readonly checkVersion: bool; + } & Struct; + readonly isEnactAuthorizedUpgrade: boolean; + readonly asEnactAuthorizedUpgrade: { + readonly code: Bytes; + } & Struct; + readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade'; + } + + /** @name CumulusPrimitivesParachainInherentParachainInherentData (99) */ + interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct { + readonly validationData: PolkadotPrimitivesV5PersistedValidationData; + readonly relayChainState: SpTrieStorageProof; + readonly downwardMessages: Vec; + readonly horizontalMessages: BTreeMap>; + } + + /** @name PolkadotPrimitivesV5PersistedValidationData (100) */ + interface PolkadotPrimitivesV5PersistedValidationData extends Struct { + readonly parentHead: Bytes; + readonly relayParentNumber: u32; + readonly relayParentStorageRoot: H256; + readonly maxPovSize: u32; + } + + /** @name SpTrieStorageProof (102) */ + interface SpTrieStorageProof extends Struct { + readonly trieNodes: BTreeSet; + } + + /** @name PolkadotCorePrimitivesInboundDownwardMessage (105) */ + interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct { + readonly sentAt: u32; + readonly msg: Bytes; + } + + /** @name PolkadotCorePrimitivesInboundHrmpMessage (109) */ + interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct { + readonly sentAt: u32; + readonly data: Bytes; + } + + /** @name ParachainInfoCall (112) */ + type ParachainInfoCall = Null; + + /** @name PalletCollatorSelectionCall (113) */ + interface PalletCollatorSelectionCall extends Enum { + readonly isAddInvulnerable: boolean; + readonly asAddInvulnerable: { + readonly new_: AccountId32; + } & Struct; + readonly isRemoveInvulnerable: boolean; + readonly asRemoveInvulnerable: { + readonly who: AccountId32; + } & Struct; + readonly isGetLicense: boolean; + readonly isOnboard: boolean; + readonly isOffboard: boolean; + readonly isReleaseLicense: boolean; + readonly isForceReleaseLicense: boolean; + readonly asForceReleaseLicense: { + readonly who: AccountId32; + } & Struct; + readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceReleaseLicense'; + } + + /** @name PalletSessionCall (114) */ + interface PalletSessionCall extends Enum { + readonly isSetKeys: boolean; + readonly asSetKeys: { + readonly keys_: OpalRuntimeRuntimeCommonSessionKeys; + readonly proof: Bytes; + } & Struct; + readonly isPurgeKeys: boolean; + readonly type: 'SetKeys' | 'PurgeKeys'; + } + + /** @name OpalRuntimeRuntimeCommonSessionKeys (115) */ + interface OpalRuntimeRuntimeCommonSessionKeys extends Struct { + readonly aura: SpConsensusAuraSr25519AppSr25519Public; + } + + /** @name SpConsensusAuraSr25519AppSr25519Public (116) */ + interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public {} + + /** @name SpCoreSr25519Public (117) */ + interface SpCoreSr25519Public extends U8aFixed {} + + /** @name PalletBalancesCall (118) */ + interface PalletBalancesCall extends Enum { + readonly isTransferAllowDeath: boolean; + readonly asTransferAllowDeath: { + readonly dest: MultiAddress; + readonly value: Compact; + } & Struct; + readonly isSetBalanceDeprecated: boolean; + readonly asSetBalanceDeprecated: { + readonly who: MultiAddress; + readonly newFree: Compact; + readonly oldReserved: Compact; + } & Struct; + readonly isForceTransfer: boolean; + readonly asForceTransfer: { + readonly source: MultiAddress; + readonly dest: MultiAddress; + readonly value: Compact; + } & Struct; + readonly isTransferKeepAlive: boolean; + readonly asTransferKeepAlive: { + readonly dest: MultiAddress; + readonly value: Compact; + } & Struct; + readonly isTransferAll: boolean; + readonly asTransferAll: { + readonly dest: MultiAddress; + readonly keepAlive: bool; + } & Struct; + readonly isForceUnreserve: boolean; + readonly asForceUnreserve: { + readonly who: MultiAddress; + readonly amount: u128; + } & Struct; + readonly isUpgradeAccounts: boolean; + readonly asUpgradeAccounts: { + readonly who: Vec; + } & Struct; + readonly isTransfer: boolean; + readonly asTransfer: { + readonly dest: MultiAddress; + readonly value: Compact; + } & Struct; + readonly isForceSetBalance: boolean; + readonly asForceSetBalance: { + readonly who: MultiAddress; + readonly newFree: Compact; + } & Struct; + readonly type: 'TransferAllowDeath' | 'SetBalanceDeprecated' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve' | 'UpgradeAccounts' | 'Transfer' | 'ForceSetBalance'; + } + + /** @name PalletTimestampCall (122) */ + interface PalletTimestampCall extends Enum { + readonly isSet: boolean; + readonly asSet: { + readonly now: Compact; + } & Struct; + readonly type: 'Set'; + } + + /** @name PalletTreasuryCall (123) */ + interface PalletTreasuryCall extends Enum { + readonly isProposeSpend: boolean; + readonly asProposeSpend: { + readonly value: Compact; + readonly beneficiary: MultiAddress; + } & Struct; + readonly isRejectProposal: boolean; + readonly asRejectProposal: { + readonly proposalId: Compact; + } & Struct; + readonly isApproveProposal: boolean; + readonly asApproveProposal: { + readonly proposalId: Compact; + } & Struct; + readonly isSpend: boolean; + readonly asSpend: { + readonly amount: Compact; + readonly beneficiary: MultiAddress; + } & Struct; + readonly isRemoveApproval: boolean; + readonly asRemoveApproval: { + readonly proposalId: Compact; + } & Struct; + readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval'; + } + + /** @name PalletSudoCall (124) */ + interface PalletSudoCall extends Enum { + readonly isSudo: boolean; + readonly asSudo: { + readonly call: Call; + } & Struct; + readonly isSudoUncheckedWeight: boolean; + readonly asSudoUncheckedWeight: { + readonly call: Call; + readonly weight: SpWeightsWeightV2Weight; + } & Struct; + readonly isSetKey: boolean; + readonly asSetKey: { + readonly new_: MultiAddress; + } & Struct; + readonly isSudoAs: boolean; + readonly asSudoAs: { + readonly who: MultiAddress; + readonly call: Call; + } & Struct; + readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs'; + } + + /** @name OrmlVestingModuleCall (125) */ + interface OrmlVestingModuleCall extends Enum { + readonly isClaim: boolean; + readonly isVestedTransfer: boolean; + readonly asVestedTransfer: { + readonly dest: MultiAddress; + readonly schedule: OrmlVestingVestingSchedule; + } & Struct; + readonly isUpdateVestingSchedules: boolean; + readonly asUpdateVestingSchedules: { + readonly who: MultiAddress; + readonly vestingSchedules: Vec; + } & Struct; + readonly isClaimFor: boolean; + readonly asClaimFor: { + readonly dest: MultiAddress; + } & Struct; + readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor'; + } + + /** @name OrmlXtokensModuleCall (127) */ + interface OrmlXtokensModuleCall extends Enum { + readonly isTransfer: boolean; + readonly asTransfer: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: u128; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isTransferMultiasset: boolean; + readonly asTransferMultiasset: { + readonly asset: StagingXcmVersionedMultiAsset; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isTransferWithFee: boolean; + readonly asTransferWithFee: { + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: u128; + readonly fee: u128; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isTransferMultiassetWithFee: boolean; + readonly asTransferMultiassetWithFee: { + readonly asset: StagingXcmVersionedMultiAsset; + readonly fee: StagingXcmVersionedMultiAsset; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isTransferMulticurrencies: boolean; + readonly asTransferMulticurrencies: { + readonly currencies: Vec>; + readonly feeItem: u32; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isTransferMultiassets: boolean; + readonly asTransferMultiassets: { + readonly assets: StagingXcmVersionedMultiAssets; + readonly feeItem: u32; + readonly dest: StagingXcmVersionedMultiLocation; + readonly destWeightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets'; + } + + /** @name StagingXcmVersionedMultiLocation (128) */ + interface StagingXcmVersionedMultiLocation extends Enum { + readonly isV2: boolean; + readonly asV2: StagingXcmV2MultiLocation; + readonly isV3: boolean; + readonly asV3: StagingXcmV3MultiLocation; + readonly type: 'V2' | 'V3'; + } + + /** @name StagingXcmV2MultiLocation (129) */ + interface StagingXcmV2MultiLocation extends Struct { + readonly parents: u8; + readonly interior: StagingXcmV2MultilocationJunctions; + } + + /** @name StagingXcmV2MultilocationJunctions (130) */ + interface StagingXcmV2MultilocationJunctions extends Enum { + readonly isHere: boolean; + readonly isX1: boolean; + readonly asX1: StagingXcmV2Junction; + readonly isX2: boolean; + readonly asX2: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX3: boolean; + readonly asX3: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX4: boolean; + readonly asX4: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX5: boolean; + readonly asX5: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX6: boolean; + readonly asX6: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX7: boolean; + readonly asX7: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly isX8: boolean; + readonly asX8: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>; + readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; + } + + /** @name StagingXcmV2Junction (131) */ + interface StagingXcmV2Junction extends Enum { + readonly isParachain: boolean; + readonly asParachain: Compact; + readonly isAccountId32: boolean; + readonly asAccountId32: { + readonly network: StagingXcmV2NetworkId; + readonly id: U8aFixed; + } & Struct; + readonly isAccountIndex64: boolean; + readonly asAccountIndex64: { + readonly network: StagingXcmV2NetworkId; + readonly index: Compact; + } & Struct; + readonly isAccountKey20: boolean; + readonly asAccountKey20: { + readonly network: StagingXcmV2NetworkId; + readonly key: U8aFixed; + } & Struct; + readonly isPalletInstance: boolean; + readonly asPalletInstance: u8; + readonly isGeneralIndex: boolean; + readonly asGeneralIndex: Compact; + readonly isGeneralKey: boolean; + readonly asGeneralKey: Bytes; + readonly isOnlyChild: boolean; + readonly isPlurality: boolean; + readonly asPlurality: { + readonly id: StagingXcmV2BodyId; + readonly part: StagingXcmV2BodyPart; + } & Struct; + readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality'; + } + + /** @name StagingXcmV2NetworkId (132) */ + interface StagingXcmV2NetworkId extends Enum { + readonly isAny: boolean; + readonly isNamed: boolean; + readonly asNamed: Bytes; + readonly isPolkadot: boolean; + readonly isKusama: boolean; + readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama'; + } + + /** @name StagingXcmV2BodyId (134) */ + interface StagingXcmV2BodyId extends Enum { + readonly isUnit: boolean; + readonly isNamed: boolean; + readonly asNamed: Bytes; + readonly isIndex: boolean; + readonly asIndex: Compact; + readonly isExecutive: boolean; + readonly isTechnical: boolean; + readonly isLegislative: boolean; + readonly isJudicial: boolean; + readonly isDefense: boolean; + readonly isAdministration: boolean; + readonly isTreasury: boolean; + readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury'; + } + + /** @name StagingXcmV2BodyPart (135) */ + interface StagingXcmV2BodyPart extends Enum { + readonly isVoice: boolean; + readonly isMembers: boolean; + readonly asMembers: { + readonly count: Compact; + } & Struct; + readonly isFraction: boolean; + readonly asFraction: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly isAtLeastProportion: boolean; + readonly asAtLeastProportion: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly isMoreThanProportion: boolean; + readonly asMoreThanProportion: { + readonly nom: Compact; + readonly denom: Compact; + } & Struct; + readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion'; + } + + /** @name StagingXcmV3WeightLimit (136) */ + interface StagingXcmV3WeightLimit extends Enum { + readonly isUnlimited: boolean; + readonly isLimited: boolean; + readonly asLimited: SpWeightsWeightV2Weight; + readonly type: 'Unlimited' | 'Limited'; + } + + /** @name StagingXcmVersionedMultiAsset (137) */ + interface StagingXcmVersionedMultiAsset extends Enum { + readonly isV2: boolean; + readonly asV2: StagingXcmV2MultiAsset; + readonly isV3: boolean; + readonly asV3: StagingXcmV3MultiAsset; + readonly type: 'V2' | 'V3'; + } + + /** @name StagingXcmV2MultiAsset (138) */ + interface StagingXcmV2MultiAsset extends Struct { + readonly id: StagingXcmV2MultiassetAssetId; + readonly fun: StagingXcmV2MultiassetFungibility; + } + + /** @name StagingXcmV2MultiassetAssetId (139) */ + interface StagingXcmV2MultiassetAssetId extends Enum { + readonly isConcrete: boolean; + readonly asConcrete: StagingXcmV2MultiLocation; + readonly isAbstract: boolean; + readonly asAbstract: Bytes; + readonly type: 'Concrete' | 'Abstract'; + } + + /** @name StagingXcmV2MultiassetFungibility (140) */ + interface StagingXcmV2MultiassetFungibility extends Enum { + readonly isFungible: boolean; + readonly asFungible: Compact; + readonly isNonFungible: boolean; + readonly asNonFungible: StagingXcmV2MultiassetAssetInstance; + readonly type: 'Fungible' | 'NonFungible'; + } + + /** @name StagingXcmV2MultiassetAssetInstance (141) */ + interface StagingXcmV2MultiassetAssetInstance extends Enum { + readonly isUndefined: boolean; + readonly isIndex: boolean; + readonly asIndex: Compact; + readonly isArray4: boolean; + readonly asArray4: U8aFixed; + readonly isArray8: boolean; + readonly asArray8: U8aFixed; + readonly isArray16: boolean; + readonly asArray16: U8aFixed; + readonly isArray32: boolean; + readonly asArray32: U8aFixed; + readonly isBlob: boolean; + readonly asBlob: Bytes; + readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob'; + } + + /** @name StagingXcmVersionedMultiAssets (144) */ + interface StagingXcmVersionedMultiAssets extends Enum { + readonly isV2: boolean; + readonly asV2: StagingXcmV2MultiassetMultiAssets; + readonly isV3: boolean; + readonly asV3: StagingXcmV3MultiassetMultiAssets; + readonly type: 'V2' | 'V3'; + } + + /** @name StagingXcmV2MultiassetMultiAssets (145) */ + interface StagingXcmV2MultiassetMultiAssets extends Vec {} + + /** @name OrmlTokensModuleCall (147) */ + interface OrmlTokensModuleCall extends Enum { + readonly isTransfer: boolean; + readonly asTransfer: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: Compact; + } & Struct; + readonly isTransferAll: boolean; + readonly asTransferAll: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetId; + readonly keepAlive: bool; + } & Struct; + readonly isTransferKeepAlive: boolean; + readonly asTransferKeepAlive: { + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: Compact; + } & Struct; + readonly isForceTransfer: boolean; + readonly asForceTransfer: { + readonly source: MultiAddress; + readonly dest: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetId; + readonly amount: Compact; + } & Struct; + readonly isSetBalance: boolean; + readonly asSetBalance: { + readonly who: MultiAddress; + readonly currencyId: PalletForeignAssetsAssetId; + readonly newFree: Compact; + readonly newReserved: Compact; + } & Struct; + readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance'; + } + + /** @name PalletIdentityCall (148) */ + interface PalletIdentityCall extends Enum { + readonly isAddRegistrar: boolean; + readonly asAddRegistrar: { + readonly account: MultiAddress; + } & Struct; + readonly isSetIdentity: boolean; + readonly asSetIdentity: { + readonly info: PalletIdentityIdentityInfo; + } & Struct; + readonly isSetSubs: boolean; + readonly asSetSubs: { + readonly subs: Vec>; + } & Struct; + readonly isClearIdentity: boolean; + readonly isRequestJudgement: boolean; + readonly asRequestJudgement: { + readonly regIndex: Compact; + readonly maxFee: Compact; + } & Struct; + readonly isCancelRequest: boolean; + readonly asCancelRequest: { + readonly regIndex: u32; + } & Struct; + readonly isSetFee: boolean; + readonly asSetFee: { + readonly index: Compact; + readonly fee: Compact; + } & Struct; + readonly isSetAccountId: boolean; + readonly asSetAccountId: { + readonly index: Compact; + readonly new_: MultiAddress; + } & Struct; + readonly isSetFields: boolean; + readonly asSetFields: { + readonly index: Compact; + readonly fields: PalletIdentityBitFlags; + } & Struct; + readonly isProvideJudgement: boolean; + readonly asProvideJudgement: { + readonly regIndex: Compact; + readonly target: MultiAddress; + readonly judgement: PalletIdentityJudgement; + readonly identity: H256; + } & Struct; + readonly isKillIdentity: boolean; + readonly asKillIdentity: { + readonly target: MultiAddress; + } & Struct; + readonly isAddSub: boolean; + readonly asAddSub: { + readonly sub: MultiAddress; + readonly data: Data; + } & Struct; + readonly isRenameSub: boolean; + readonly asRenameSub: { + readonly sub: MultiAddress; + readonly data: Data; + } & Struct; + readonly isRemoveSub: boolean; + readonly asRemoveSub: { + readonly sub: MultiAddress; + } & Struct; + readonly isQuitSub: boolean; + readonly isForceInsertIdentities: boolean; + readonly asForceInsertIdentities: { + readonly identities: Vec>; + } & Struct; + readonly isForceRemoveIdentities: boolean; + readonly asForceRemoveIdentities: { + readonly identities: Vec; + } & Struct; + readonly isForceSetSubs: boolean; + readonly asForceSetSubs: { + readonly subs: Vec>]>]>>; + } & Struct; + readonly type: 'AddRegistrar' | 'SetIdentity' | 'SetSubs' | 'ClearIdentity' | 'RequestJudgement' | 'CancelRequest' | 'SetFee' | 'SetAccountId' | 'SetFields' | 'ProvideJudgement' | 'KillIdentity' | 'AddSub' | 'RenameSub' | 'RemoveSub' | 'QuitSub' | 'ForceInsertIdentities' | 'ForceRemoveIdentities' | 'ForceSetSubs'; + } + + /** @name PalletIdentityIdentityInfo (149) */ + interface PalletIdentityIdentityInfo extends Struct { + readonly additional: Vec>; + readonly display: Data; + readonly legal: Data; + readonly web: Data; + readonly riot: Data; + readonly email: Data; + readonly pgpFingerprint: Option; + readonly image: Data; + readonly twitter: Data; + } + + /** @name PalletIdentityBitFlags (185) */ + interface PalletIdentityBitFlags extends Set { + readonly isDisplay: boolean; + readonly isLegal: boolean; + readonly isWeb: boolean; + readonly isRiot: boolean; + readonly isEmail: boolean; + readonly isPgpFingerprint: boolean; + readonly isImage: boolean; + readonly isTwitter: boolean; + } + + /** @name PalletIdentityIdentityField (186) */ + interface PalletIdentityIdentityField extends Enum { + readonly isDisplay: boolean; + readonly isLegal: boolean; + readonly isWeb: boolean; + readonly isRiot: boolean; + readonly isEmail: boolean; + readonly isPgpFingerprint: boolean; + readonly isImage: boolean; + readonly isTwitter: boolean; + readonly type: 'Display' | 'Legal' | 'Web' | 'Riot' | 'Email' | 'PgpFingerprint' | 'Image' | 'Twitter'; + } + + /** @name PalletIdentityJudgement (187) */ + interface PalletIdentityJudgement extends Enum { + readonly isUnknown: boolean; + readonly isFeePaid: boolean; + readonly asFeePaid: u128; + readonly isReasonable: boolean; + readonly isKnownGood: boolean; + readonly isOutOfDate: boolean; + readonly isLowQuality: boolean; + readonly isErroneous: boolean; + readonly type: 'Unknown' | 'FeePaid' | 'Reasonable' | 'KnownGood' | 'OutOfDate' | 'LowQuality' | 'Erroneous'; + } + + /** @name PalletIdentityRegistration (190) */ + interface PalletIdentityRegistration extends Struct { + readonly judgements: Vec>; + readonly deposit: u128; + readonly info: PalletIdentityIdentityInfo; + } + + /** @name PalletPreimageCall (198) */ + interface PalletPreimageCall extends Enum { + readonly isNotePreimage: boolean; + readonly asNotePreimage: { + readonly bytes: Bytes; + } & Struct; + readonly isUnnotePreimage: boolean; + readonly asUnnotePreimage: { + readonly hash_: H256; + } & Struct; + readonly isRequestPreimage: boolean; + readonly asRequestPreimage: { + readonly hash_: H256; + } & Struct; + readonly isUnrequestPreimage: boolean; + readonly asUnrequestPreimage: { + readonly hash_: H256; + } & Struct; + readonly type: 'NotePreimage' | 'UnnotePreimage' | 'RequestPreimage' | 'UnrequestPreimage'; + } + + /** @name PalletDemocracyCall (199) */ + interface PalletDemocracyCall extends Enum { + readonly isPropose: boolean; + readonly asPropose: { + readonly proposal: FrameSupportPreimagesBounded; + readonly value: Compact; + } & Struct; + readonly isSecond: boolean; + readonly asSecond: { + readonly proposal: Compact; + } & Struct; + readonly isVote: boolean; + readonly asVote: { + readonly refIndex: Compact; + readonly vote: PalletDemocracyVoteAccountVote; + } & Struct; + readonly isEmergencyCancel: boolean; + readonly asEmergencyCancel: { + readonly refIndex: u32; + } & Struct; + readonly isExternalPropose: boolean; + readonly asExternalPropose: { + readonly proposal: FrameSupportPreimagesBounded; + } & Struct; + readonly isExternalProposeMajority: boolean; + readonly asExternalProposeMajority: { + readonly proposal: FrameSupportPreimagesBounded; + } & Struct; + readonly isExternalProposeDefault: boolean; + readonly asExternalProposeDefault: { + readonly proposal: FrameSupportPreimagesBounded; + } & Struct; + readonly isFastTrack: boolean; + readonly asFastTrack: { + readonly proposalHash: H256; + readonly votingPeriod: u32; + readonly delay: u32; + } & Struct; + readonly isVetoExternal: boolean; + readonly asVetoExternal: { + readonly proposalHash: H256; + } & Struct; + readonly isCancelReferendum: boolean; + readonly asCancelReferendum: { + readonly refIndex: Compact; + } & Struct; + readonly isDelegate: boolean; + readonly asDelegate: { + readonly to: MultiAddress; + readonly conviction: PalletDemocracyConviction; + readonly balance: u128; + } & Struct; + readonly isUndelegate: boolean; + readonly isClearPublicProposals: boolean; + readonly isUnlock: boolean; + readonly asUnlock: { + readonly target: MultiAddress; + } & Struct; + readonly isRemoveVote: boolean; + readonly asRemoveVote: { + readonly index: u32; + } & Struct; + readonly isRemoveOtherVote: boolean; + readonly asRemoveOtherVote: { + readonly target: MultiAddress; + readonly index: u32; + } & Struct; + readonly isBlacklist: boolean; + readonly asBlacklist: { + readonly proposalHash: H256; + readonly maybeRefIndex: Option; + } & Struct; + readonly isCancelProposal: boolean; + readonly asCancelProposal: { + readonly propIndex: Compact; + } & Struct; + readonly isSetMetadata: boolean; + readonly asSetMetadata: { + readonly owner: PalletDemocracyMetadataOwner; + readonly maybeHash: Option; + } & Struct; + readonly type: 'Propose' | 'Second' | 'Vote' | 'EmergencyCancel' | 'ExternalPropose' | 'ExternalProposeMajority' | 'ExternalProposeDefault' | 'FastTrack' | 'VetoExternal' | 'CancelReferendum' | 'Delegate' | 'Undelegate' | 'ClearPublicProposals' | 'Unlock' | 'RemoveVote' | 'RemoveOtherVote' | 'Blacklist' | 'CancelProposal' | 'SetMetadata'; + } + + /** @name PalletDemocracyConviction (200) */ + interface PalletDemocracyConviction extends Enum { + readonly isNone: boolean; + readonly isLocked1x: boolean; + readonly isLocked2x: boolean; + readonly isLocked3x: boolean; + readonly isLocked4x: boolean; + readonly isLocked5x: boolean; + readonly isLocked6x: boolean; + readonly type: 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x'; + } + + /** @name PalletCollectiveCall (203) */ + interface PalletCollectiveCall extends Enum { + readonly isSetMembers: boolean; + readonly asSetMembers: { + readonly newMembers: Vec; + readonly prime: Option; + readonly oldCount: u32; + } & Struct; + readonly isExecute: boolean; + readonly asExecute: { + readonly proposal: Call; + readonly lengthBound: Compact; + } & Struct; + readonly isPropose: boolean; + readonly asPropose: { + readonly threshold: Compact; + readonly proposal: Call; + readonly lengthBound: Compact; + } & Struct; + readonly isVote: boolean; + readonly asVote: { + readonly proposal: H256; + readonly index: Compact; + readonly approve: bool; + } & Struct; + readonly isDisapproveProposal: boolean; + readonly asDisapproveProposal: { + readonly proposalHash: H256; + } & Struct; + readonly isClose: boolean; + readonly asClose: { + readonly proposalHash: H256; + readonly index: Compact; + readonly proposalWeightBound: SpWeightsWeightV2Weight; + readonly lengthBound: Compact; + } & Struct; + readonly type: 'SetMembers' | 'Execute' | 'Propose' | 'Vote' | 'DisapproveProposal' | 'Close'; + } + + /** @name PalletMembershipCall (205) */ + interface PalletMembershipCall extends Enum { + readonly isAddMember: boolean; + readonly asAddMember: { + readonly who: MultiAddress; + } & Struct; + readonly isRemoveMember: boolean; + readonly asRemoveMember: { + readonly who: MultiAddress; + } & Struct; + readonly isSwapMember: boolean; + readonly asSwapMember: { + readonly remove: MultiAddress; + readonly add: MultiAddress; + } & Struct; + readonly isResetMembers: boolean; + readonly asResetMembers: { + readonly members: Vec; + } & Struct; + readonly isChangeKey: boolean; + readonly asChangeKey: { + readonly new_: MultiAddress; + } & Struct; + readonly isSetPrime: boolean; + readonly asSetPrime: { + readonly who: MultiAddress; + } & Struct; + readonly isClearPrime: boolean; + readonly type: 'AddMember' | 'RemoveMember' | 'SwapMember' | 'ResetMembers' | 'ChangeKey' | 'SetPrime' | 'ClearPrime'; + } + + /** @name PalletRankedCollectiveCall (207) */ + interface PalletRankedCollectiveCall extends Enum { + readonly isAddMember: boolean; + readonly asAddMember: { + readonly who: MultiAddress; + } & Struct; + readonly isPromoteMember: boolean; + readonly asPromoteMember: { + readonly who: MultiAddress; + } & Struct; + readonly isDemoteMember: boolean; + readonly asDemoteMember: { + readonly who: MultiAddress; + } & Struct; + readonly isRemoveMember: boolean; + readonly asRemoveMember: { + readonly who: MultiAddress; + readonly minRank: u16; + } & Struct; + readonly isVote: boolean; + readonly asVote: { + readonly poll: u32; + readonly aye: bool; + } & Struct; + readonly isCleanupPoll: boolean; + readonly asCleanupPoll: { + readonly pollIndex: u32; + readonly max: u32; + } & Struct; + readonly type: 'AddMember' | 'PromoteMember' | 'DemoteMember' | 'RemoveMember' | 'Vote' | 'CleanupPoll'; + } + + /** @name PalletReferendaCall (208) */ + interface PalletReferendaCall extends Enum { + readonly isSubmit: boolean; + readonly asSubmit: { + readonly proposalOrigin: OpalRuntimeOriginCaller; + readonly proposal: FrameSupportPreimagesBounded; + readonly enactmentMoment: FrameSupportScheduleDispatchTime; + } & Struct; + readonly isPlaceDecisionDeposit: boolean; + readonly asPlaceDecisionDeposit: { + readonly index: u32; + } & Struct; + readonly isRefundDecisionDeposit: boolean; + readonly asRefundDecisionDeposit: { + readonly index: u32; + } & Struct; + readonly isCancel: boolean; + readonly asCancel: { + readonly index: u32; + } & Struct; + readonly isKill: boolean; + readonly asKill: { + readonly index: u32; + } & Struct; + readonly isNudgeReferendum: boolean; + readonly asNudgeReferendum: { + readonly index: u32; + } & Struct; + readonly isOneFewerDeciding: boolean; + readonly asOneFewerDeciding: { + readonly track: u16; + } & Struct; + readonly isRefundSubmissionDeposit: boolean; + readonly asRefundSubmissionDeposit: { + readonly index: u32; + } & Struct; + readonly isSetMetadata: boolean; + readonly asSetMetadata: { + readonly index: u32; + readonly maybeHash: Option; + } & Struct; + readonly type: 'Submit' | 'PlaceDecisionDeposit' | 'RefundDecisionDeposit' | 'Cancel' | 'Kill' | 'NudgeReferendum' | 'OneFewerDeciding' | 'RefundSubmissionDeposit' | 'SetMetadata'; + } + + /** @name OpalRuntimeOriginCaller (209) */ + interface OpalRuntimeOriginCaller extends Enum { + readonly isSystem: boolean; + readonly asSystem: FrameSupportDispatchRawOrigin; + readonly isVoid: boolean; + readonly isCouncil: boolean; + readonly asCouncil: PalletCollectiveRawOrigin; + readonly isTechnicalCommittee: boolean; + readonly asTechnicalCommittee: PalletCollectiveRawOrigin; + readonly isPolkadotXcm: boolean; + readonly asPolkadotXcm: PalletXcmOrigin; + readonly isCumulusXcm: boolean; + readonly asCumulusXcm: CumulusPalletXcmOrigin; + readonly isOrigins: boolean; + readonly asOrigins: PalletGovOriginsOrigin; + readonly isEthereum: boolean; + readonly asEthereum: PalletEthereumRawOrigin; + readonly type: 'System' | 'Void' | 'Council' | 'TechnicalCommittee' | 'PolkadotXcm' | 'CumulusXcm' | 'Origins' | 'Ethereum'; + } + + /** @name FrameSupportDispatchRawOrigin (210) */ + interface FrameSupportDispatchRawOrigin extends Enum { + readonly isRoot: boolean; + readonly isSigned: boolean; + readonly asSigned: AccountId32; + readonly isNone: boolean; + readonly type: 'Root' | 'Signed' | 'None'; + } + + /** @name PalletCollectiveRawOrigin (211) */ + interface PalletCollectiveRawOrigin extends Enum { + readonly isMembers: boolean; + readonly asMembers: ITuple<[u32, u32]>; + readonly isMember: boolean; + readonly asMember: AccountId32; + readonly isPhantom: boolean; + readonly type: 'Members' | 'Member' | 'Phantom'; + } + + /** @name PalletGovOriginsOrigin (213) */ + interface PalletGovOriginsOrigin extends Enum { + readonly isFellowshipProposition: boolean; + readonly type: 'FellowshipProposition'; + } + + /** @name PalletXcmOrigin (214) */ + interface PalletXcmOrigin extends Enum { + readonly isXcm: boolean; + readonly asXcm: StagingXcmV3MultiLocation; + readonly isResponse: boolean; + readonly asResponse: StagingXcmV3MultiLocation; + readonly type: 'Xcm' | 'Response'; + } + + /** @name CumulusPalletXcmOrigin (215) */ + interface CumulusPalletXcmOrigin extends Enum { + readonly isRelay: boolean; + readonly isSiblingParachain: boolean; + readonly asSiblingParachain: u32; + readonly type: 'Relay' | 'SiblingParachain'; + } + + /** @name PalletEthereumRawOrigin (216) */ + interface PalletEthereumRawOrigin extends Enum { + readonly isEthereumTransaction: boolean; + readonly asEthereumTransaction: H160; + readonly type: 'EthereumTransaction'; + } + + /** @name SpCoreVoid (218) */ + type SpCoreVoid = Null; + + /** @name FrameSupportScheduleDispatchTime (219) */ + interface FrameSupportScheduleDispatchTime extends Enum { + readonly isAt: boolean; + readonly asAt: u32; + readonly isAfter: boolean; + readonly asAfter: u32; + readonly type: 'At' | 'After'; + } + + /** @name PalletSchedulerCall (220) */ + interface PalletSchedulerCall extends Enum { + readonly isSchedule: boolean; + readonly asSchedule: { + readonly when: u32; + readonly maybePeriodic: Option>; + readonly priority: u8; + readonly call: Call; + } & Struct; + readonly isCancel: boolean; + readonly asCancel: { + readonly when: u32; + readonly index: u32; + } & Struct; + readonly isScheduleNamed: boolean; + readonly asScheduleNamed: { + readonly id: U8aFixed; + readonly when: u32; + readonly maybePeriodic: Option>; + readonly priority: u8; + readonly call: Call; + } & Struct; + readonly isCancelNamed: boolean; + readonly asCancelNamed: { + readonly id: U8aFixed; + } & Struct; + readonly isScheduleAfter: boolean; + readonly asScheduleAfter: { + readonly after: u32; + readonly maybePeriodic: Option>; + readonly priority: u8; + readonly call: Call; + } & Struct; + readonly isScheduleNamedAfter: boolean; + readonly asScheduleNamedAfter: { + readonly id: U8aFixed; + readonly after: u32; + readonly maybePeriodic: Option>; + readonly priority: u8; + readonly call: Call; + } & Struct; + readonly type: 'Schedule' | 'Cancel' | 'ScheduleNamed' | 'CancelNamed' | 'ScheduleAfter' | 'ScheduleNamedAfter'; + } + + /** @name CumulusPalletXcmpQueueCall (223) */ + interface CumulusPalletXcmpQueueCall extends Enum { + readonly isServiceOverweight: boolean; + readonly asServiceOverweight: { + readonly index: u64; + readonly weightLimit: SpWeightsWeightV2Weight; + } & Struct; + readonly isSuspendXcmExecution: boolean; + readonly isResumeXcmExecution: boolean; + readonly isUpdateSuspendThreshold: boolean; + readonly asUpdateSuspendThreshold: { + readonly new_: u32; + } & Struct; + readonly isUpdateDropThreshold: boolean; + readonly asUpdateDropThreshold: { + readonly new_: u32; + } & Struct; + readonly isUpdateResumeThreshold: boolean; + readonly asUpdateResumeThreshold: { + readonly new_: u32; + } & Struct; + readonly isUpdateThresholdWeight: boolean; + readonly asUpdateThresholdWeight: { + readonly new_: SpWeightsWeightV2Weight; + } & Struct; + readonly isUpdateWeightRestrictDecay: boolean; + readonly asUpdateWeightRestrictDecay: { + readonly new_: SpWeightsWeightV2Weight; + } & Struct; + readonly isUpdateXcmpMaxIndividualWeight: boolean; + readonly asUpdateXcmpMaxIndividualWeight: { + readonly new_: SpWeightsWeightV2Weight; + } & Struct; + readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight'; + } + + /** @name PalletXcmCall (224) */ + interface PalletXcmCall extends Enum { + readonly isSend: boolean; + readonly asSend: { + readonly dest: StagingXcmVersionedMultiLocation; + readonly message: StagingXcmVersionedXcm; + } & Struct; + readonly isTeleportAssets: boolean; + readonly asTeleportAssets: { + readonly dest: StagingXcmVersionedMultiLocation; + readonly beneficiary: StagingXcmVersionedMultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + readonly feeAssetItem: u32; + } & Struct; + readonly isReserveTransferAssets: boolean; + readonly asReserveTransferAssets: { + readonly dest: StagingXcmVersionedMultiLocation; + readonly beneficiary: StagingXcmVersionedMultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + readonly feeAssetItem: u32; + } & Struct; + readonly isExecute: boolean; + readonly asExecute: { + readonly message: StagingXcmVersionedXcm; + readonly maxWeight: SpWeightsWeightV2Weight; + } & Struct; + readonly isForceXcmVersion: boolean; + readonly asForceXcmVersion: { + readonly location: StagingXcmV3MultiLocation; + readonly version: u32; + } & Struct; + readonly isForceDefaultXcmVersion: boolean; + readonly asForceDefaultXcmVersion: { + readonly maybeXcmVersion: Option; + } & Struct; + readonly isForceSubscribeVersionNotify: boolean; + readonly asForceSubscribeVersionNotify: { + readonly location: StagingXcmVersionedMultiLocation; + } & Struct; + readonly isForceUnsubscribeVersionNotify: boolean; + readonly asForceUnsubscribeVersionNotify: { + readonly location: StagingXcmVersionedMultiLocation; + } & Struct; + readonly isLimitedReserveTransferAssets: boolean; + readonly asLimitedReserveTransferAssets: { + readonly dest: StagingXcmVersionedMultiLocation; + readonly beneficiary: StagingXcmVersionedMultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + readonly feeAssetItem: u32; + readonly weightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isLimitedTeleportAssets: boolean; + readonly asLimitedTeleportAssets: { + readonly dest: StagingXcmVersionedMultiLocation; + readonly beneficiary: StagingXcmVersionedMultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + readonly feeAssetItem: u32; + readonly weightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isForceSuspension: boolean; + readonly asForceSuspension: { + readonly suspended: bool; + } & Struct; + readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets' | 'ForceSuspension'; + } + + /** @name StagingXcmVersionedXcm (225) */ + interface StagingXcmVersionedXcm extends Enum { + readonly isV2: boolean; + readonly asV2: StagingXcmV2Xcm; + readonly isV3: boolean; + readonly asV3: StagingXcmV3Xcm; + readonly type: 'V2' | 'V3'; + } + + /** @name StagingXcmV2Xcm (226) */ + interface StagingXcmV2Xcm extends Vec {} + + /** @name StagingXcmV2Instruction (228) */ + interface StagingXcmV2Instruction extends Enum { + readonly isWithdrawAsset: boolean; + readonly asWithdrawAsset: StagingXcmV2MultiassetMultiAssets; + readonly isReserveAssetDeposited: boolean; + readonly asReserveAssetDeposited: StagingXcmV2MultiassetMultiAssets; + readonly isReceiveTeleportedAsset: boolean; + readonly asReceiveTeleportedAsset: StagingXcmV2MultiassetMultiAssets; + readonly isQueryResponse: boolean; + readonly asQueryResponse: { + readonly queryId: Compact; + readonly response: StagingXcmV2Response; + readonly maxWeight: Compact; + } & Struct; + readonly isTransferAsset: boolean; + readonly asTransferAsset: { + readonly assets: StagingXcmV2MultiassetMultiAssets; + readonly beneficiary: StagingXcmV2MultiLocation; + } & Struct; + readonly isTransferReserveAsset: boolean; + readonly asTransferReserveAsset: { + readonly assets: StagingXcmV2MultiassetMultiAssets; + readonly dest: StagingXcmV2MultiLocation; + readonly xcm: StagingXcmV2Xcm; + } & Struct; + readonly isTransact: boolean; + readonly asTransact: { + readonly originType: StagingXcmV2OriginKind; + readonly requireWeightAtMost: Compact; + readonly call: StagingXcmDoubleEncoded; + } & Struct; + readonly isHrmpNewChannelOpenRequest: boolean; + readonly asHrmpNewChannelOpenRequest: { + readonly sender: Compact; + readonly maxMessageSize: Compact; + readonly maxCapacity: Compact; + } & Struct; + readonly isHrmpChannelAccepted: boolean; + readonly asHrmpChannelAccepted: { + readonly recipient: Compact; + } & Struct; + readonly isHrmpChannelClosing: boolean; + readonly asHrmpChannelClosing: { + readonly initiator: Compact; + readonly sender: Compact; + readonly recipient: Compact; + } & Struct; + readonly isClearOrigin: boolean; + readonly isDescendOrigin: boolean; + readonly asDescendOrigin: StagingXcmV2MultilocationJunctions; + readonly isReportError: boolean; + readonly asReportError: { + readonly queryId: Compact; + readonly dest: StagingXcmV2MultiLocation; + readonly maxResponseWeight: Compact; + } & Struct; + readonly isDepositAsset: boolean; + readonly asDepositAsset: { + readonly assets: StagingXcmV2MultiassetMultiAssetFilter; + readonly maxAssets: Compact; + readonly beneficiary: StagingXcmV2MultiLocation; + } & Struct; + readonly isDepositReserveAsset: boolean; + readonly asDepositReserveAsset: { + readonly assets: StagingXcmV2MultiassetMultiAssetFilter; + readonly maxAssets: Compact; + readonly dest: StagingXcmV2MultiLocation; + readonly xcm: StagingXcmV2Xcm; + } & Struct; + readonly isExchangeAsset: boolean; + readonly asExchangeAsset: { + readonly give: StagingXcmV2MultiassetMultiAssetFilter; + readonly receive: StagingXcmV2MultiassetMultiAssets; + } & Struct; + readonly isInitiateReserveWithdraw: boolean; + readonly asInitiateReserveWithdraw: { + readonly assets: StagingXcmV2MultiassetMultiAssetFilter; + readonly reserve: StagingXcmV2MultiLocation; + readonly xcm: StagingXcmV2Xcm; + } & Struct; + readonly isInitiateTeleport: boolean; + readonly asInitiateTeleport: { + readonly assets: StagingXcmV2MultiassetMultiAssetFilter; + readonly dest: StagingXcmV2MultiLocation; + readonly xcm: StagingXcmV2Xcm; + } & Struct; + readonly isQueryHolding: boolean; + readonly asQueryHolding: { + readonly queryId: Compact; + readonly dest: StagingXcmV2MultiLocation; + readonly assets: StagingXcmV2MultiassetMultiAssetFilter; + readonly maxResponseWeight: Compact; + } & Struct; + readonly isBuyExecution: boolean; + readonly asBuyExecution: { + readonly fees: StagingXcmV2MultiAsset; + readonly weightLimit: StagingXcmV2WeightLimit; + } & Struct; + readonly isRefundSurplus: boolean; + readonly isSetErrorHandler: boolean; + readonly asSetErrorHandler: StagingXcmV2Xcm; + readonly isSetAppendix: boolean; + readonly asSetAppendix: StagingXcmV2Xcm; + readonly isClearError: boolean; + readonly isClaimAsset: boolean; + readonly asClaimAsset: { + readonly assets: StagingXcmV2MultiassetMultiAssets; + readonly ticket: StagingXcmV2MultiLocation; + } & Struct; + readonly isTrap: boolean; + readonly asTrap: Compact; + readonly isSubscribeVersion: boolean; + readonly asSubscribeVersion: { + readonly queryId: Compact; + readonly maxResponseWeight: Compact; + } & Struct; + readonly isUnsubscribeVersion: boolean; + readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion'; + } + + /** @name StagingXcmV2Response (229) */ + interface StagingXcmV2Response extends Enum { + readonly isNull: boolean; + readonly isAssets: boolean; + readonly asAssets: StagingXcmV2MultiassetMultiAssets; + readonly isExecutionResult: boolean; + readonly asExecutionResult: Option>; + readonly isVersion: boolean; + readonly asVersion: u32; + readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version'; + } + + /** @name StagingXcmV2TraitsError (232) */ + interface StagingXcmV2TraitsError extends Enum { + readonly isOverflow: boolean; + readonly isUnimplemented: boolean; + readonly isUntrustedReserveLocation: boolean; + readonly isUntrustedTeleportLocation: boolean; + readonly isMultiLocationFull: boolean; + readonly isMultiLocationNotInvertible: boolean; + readonly isBadOrigin: boolean; + readonly isInvalidLocation: boolean; + readonly isAssetNotFound: boolean; + readonly isFailedToTransactAsset: boolean; + readonly isNotWithdrawable: boolean; + readonly isLocationCannotHold: boolean; + readonly isExceedsMaxMessageSize: boolean; + readonly isDestinationUnsupported: boolean; + readonly isTransport: boolean; + readonly isUnroutable: boolean; + readonly isUnknownClaim: boolean; + readonly isFailedToDecode: boolean; + readonly isMaxWeightInvalid: boolean; + readonly isNotHoldingFees: boolean; + readonly isTooExpensive: boolean; + readonly isTrap: boolean; + readonly asTrap: u64; + readonly isUnhandledXcmVersion: boolean; + readonly isWeightLimitReached: boolean; + readonly asWeightLimitReached: u64; + readonly isBarrier: boolean; + readonly isWeightNotComputable: boolean; + readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'MultiLocationFull' | 'MultiLocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable'; + } + + /** @name StagingXcmV2OriginKind (233) */ + interface StagingXcmV2OriginKind extends Enum { + readonly isNative: boolean; + readonly isSovereignAccount: boolean; + readonly isSuperuser: boolean; + readonly isXcm: boolean; + readonly type: 'Native' | 'SovereignAccount' | 'Superuser' | 'Xcm'; + } + + /** @name StagingXcmDoubleEncoded (234) */ + interface StagingXcmDoubleEncoded extends Struct { + readonly encoded: Bytes; + } + + /** @name StagingXcmV2MultiassetMultiAssetFilter (235) */ + interface StagingXcmV2MultiassetMultiAssetFilter extends Enum { + readonly isDefinite: boolean; + readonly asDefinite: StagingXcmV2MultiassetMultiAssets; + readonly isWild: boolean; + readonly asWild: StagingXcmV2MultiassetWildMultiAsset; + readonly type: 'Definite' | 'Wild'; + } + + /** @name StagingXcmV2MultiassetWildMultiAsset (236) */ + interface StagingXcmV2MultiassetWildMultiAsset extends Enum { + readonly isAll: boolean; + readonly isAllOf: boolean; + readonly asAllOf: { + readonly id: StagingXcmV2MultiassetAssetId; + readonly fun: StagingXcmV2MultiassetWildFungibility; + } & Struct; + readonly type: 'All' | 'AllOf'; + } + + /** @name StagingXcmV2MultiassetWildFungibility (237) */ + interface StagingXcmV2MultiassetWildFungibility extends Enum { + readonly isFungible: boolean; + readonly isNonFungible: boolean; + readonly type: 'Fungible' | 'NonFungible'; + } + + /** @name StagingXcmV2WeightLimit (238) */ + interface StagingXcmV2WeightLimit extends Enum { + readonly isUnlimited: boolean; + readonly isLimited: boolean; + readonly asLimited: Compact; + readonly type: 'Unlimited' | 'Limited'; + } + + /** @name StagingXcmV3Xcm (239) */ + interface StagingXcmV3Xcm extends Vec {} + + /** @name StagingXcmV3Instruction (241) */ + interface StagingXcmV3Instruction extends Enum { + readonly isWithdrawAsset: boolean; + readonly asWithdrawAsset: StagingXcmV3MultiassetMultiAssets; + readonly isReserveAssetDeposited: boolean; + readonly asReserveAssetDeposited: StagingXcmV3MultiassetMultiAssets; + readonly isReceiveTeleportedAsset: boolean; + readonly asReceiveTeleportedAsset: StagingXcmV3MultiassetMultiAssets; + readonly isQueryResponse: boolean; + readonly asQueryResponse: { + readonly queryId: Compact; + readonly response: StagingXcmV3Response; + readonly maxWeight: SpWeightsWeightV2Weight; + readonly querier: Option; + } & Struct; + readonly isTransferAsset: boolean; + readonly asTransferAsset: { + readonly assets: StagingXcmV3MultiassetMultiAssets; + readonly beneficiary: StagingXcmV3MultiLocation; + } & Struct; + readonly isTransferReserveAsset: boolean; + readonly asTransferReserveAsset: { + readonly assets: StagingXcmV3MultiassetMultiAssets; + readonly dest: StagingXcmV3MultiLocation; + readonly xcm: StagingXcmV3Xcm; + } & Struct; + readonly isTransact: boolean; + readonly asTransact: { + readonly originKind: StagingXcmV2OriginKind; + readonly requireWeightAtMost: SpWeightsWeightV2Weight; + readonly call: StagingXcmDoubleEncoded; + } & Struct; + readonly isHrmpNewChannelOpenRequest: boolean; + readonly asHrmpNewChannelOpenRequest: { + readonly sender: Compact; + readonly maxMessageSize: Compact; + readonly maxCapacity: Compact; + } & Struct; + readonly isHrmpChannelAccepted: boolean; + readonly asHrmpChannelAccepted: { + readonly recipient: Compact; + } & Struct; + readonly isHrmpChannelClosing: boolean; + readonly asHrmpChannelClosing: { + readonly initiator: Compact; + readonly sender: Compact; + readonly recipient: Compact; + } & Struct; + readonly isClearOrigin: boolean; + readonly isDescendOrigin: boolean; + readonly asDescendOrigin: StagingXcmV3Junctions; + readonly isReportError: boolean; + readonly asReportError: StagingXcmV3QueryResponseInfo; + readonly isDepositAsset: boolean; + readonly asDepositAsset: { + readonly assets: StagingXcmV3MultiassetMultiAssetFilter; + readonly beneficiary: StagingXcmV3MultiLocation; + } & Struct; + readonly isDepositReserveAsset: boolean; + readonly asDepositReserveAsset: { + readonly assets: StagingXcmV3MultiassetMultiAssetFilter; + readonly dest: StagingXcmV3MultiLocation; + readonly xcm: StagingXcmV3Xcm; + } & Struct; + readonly isExchangeAsset: boolean; + readonly asExchangeAsset: { + readonly give: StagingXcmV3MultiassetMultiAssetFilter; + readonly want: StagingXcmV3MultiassetMultiAssets; + readonly maximal: bool; + } & Struct; + readonly isInitiateReserveWithdraw: boolean; + readonly asInitiateReserveWithdraw: { + readonly assets: StagingXcmV3MultiassetMultiAssetFilter; + readonly reserve: StagingXcmV3MultiLocation; + readonly xcm: StagingXcmV3Xcm; + } & Struct; + readonly isInitiateTeleport: boolean; + readonly asInitiateTeleport: { + readonly assets: StagingXcmV3MultiassetMultiAssetFilter; + readonly dest: StagingXcmV3MultiLocation; + readonly xcm: StagingXcmV3Xcm; + } & Struct; + readonly isReportHolding: boolean; + readonly asReportHolding: { + readonly responseInfo: StagingXcmV3QueryResponseInfo; + readonly assets: StagingXcmV3MultiassetMultiAssetFilter; + } & Struct; + readonly isBuyExecution: boolean; + readonly asBuyExecution: { + readonly fees: StagingXcmV3MultiAsset; + readonly weightLimit: StagingXcmV3WeightLimit; + } & Struct; + readonly isRefundSurplus: boolean; + readonly isSetErrorHandler: boolean; + readonly asSetErrorHandler: StagingXcmV3Xcm; + readonly isSetAppendix: boolean; + readonly asSetAppendix: StagingXcmV3Xcm; + readonly isClearError: boolean; + readonly isClaimAsset: boolean; + readonly asClaimAsset: { + readonly assets: StagingXcmV3MultiassetMultiAssets; + readonly ticket: StagingXcmV3MultiLocation; + } & Struct; + readonly isTrap: boolean; + readonly asTrap: Compact; + readonly isSubscribeVersion: boolean; + readonly asSubscribeVersion: { + readonly queryId: Compact; + readonly maxResponseWeight: SpWeightsWeightV2Weight; + } & Struct; + readonly isUnsubscribeVersion: boolean; + readonly isBurnAsset: boolean; + readonly asBurnAsset: StagingXcmV3MultiassetMultiAssets; + readonly isExpectAsset: boolean; + readonly asExpectAsset: StagingXcmV3MultiassetMultiAssets; + readonly isExpectOrigin: boolean; + readonly asExpectOrigin: Option; + readonly isExpectError: boolean; + readonly asExpectError: Option>; + readonly isExpectTransactStatus: boolean; + readonly asExpectTransactStatus: StagingXcmV3MaybeErrorCode; + readonly isQueryPallet: boolean; + readonly asQueryPallet: { + readonly moduleName: Bytes; + readonly responseInfo: StagingXcmV3QueryResponseInfo; + } & Struct; + readonly isExpectPallet: boolean; + readonly asExpectPallet: { + readonly index: Compact; + readonly name: Bytes; + readonly moduleName: Bytes; + readonly crateMajor: Compact; + readonly minCrateMinor: Compact; + } & Struct; + readonly isReportTransactStatus: boolean; + readonly asReportTransactStatus: StagingXcmV3QueryResponseInfo; + readonly isClearTransactStatus: boolean; + readonly isUniversalOrigin: boolean; + readonly asUniversalOrigin: StagingXcmV3Junction; + readonly isExportMessage: boolean; + readonly asExportMessage: { + readonly network: StagingXcmV3JunctionNetworkId; + readonly destination: StagingXcmV3Junctions; + readonly xcm: StagingXcmV3Xcm; + } & Struct; + readonly isLockAsset: boolean; + readonly asLockAsset: { + readonly asset: StagingXcmV3MultiAsset; + readonly unlocker: StagingXcmV3MultiLocation; + } & Struct; + readonly isUnlockAsset: boolean; + readonly asUnlockAsset: { + readonly asset: StagingXcmV3MultiAsset; + readonly target: StagingXcmV3MultiLocation; + } & Struct; + readonly isNoteUnlockable: boolean; + readonly asNoteUnlockable: { + readonly asset: StagingXcmV3MultiAsset; + readonly owner: StagingXcmV3MultiLocation; + } & Struct; + readonly isRequestUnlock: boolean; + readonly asRequestUnlock: { + readonly asset: StagingXcmV3MultiAsset; + readonly locker: StagingXcmV3MultiLocation; + } & Struct; + readonly isSetFeesMode: boolean; + readonly asSetFeesMode: { + readonly jitWithdraw: bool; + } & Struct; + readonly isSetTopic: boolean; + readonly asSetTopic: U8aFixed; + readonly isClearTopic: boolean; + readonly isAliasOrigin: boolean; + readonly asAliasOrigin: StagingXcmV3MultiLocation; + readonly isUnpaidExecution: boolean; + readonly asUnpaidExecution: { + readonly weightLimit: StagingXcmV3WeightLimit; + readonly checkOrigin: Option; + } & Struct; + readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'ReportHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion' | 'BurnAsset' | 'ExpectAsset' | 'ExpectOrigin' | 'ExpectError' | 'ExpectTransactStatus' | 'QueryPallet' | 'ExpectPallet' | 'ReportTransactStatus' | 'ClearTransactStatus' | 'UniversalOrigin' | 'ExportMessage' | 'LockAsset' | 'UnlockAsset' | 'NoteUnlockable' | 'RequestUnlock' | 'SetFeesMode' | 'SetTopic' | 'ClearTopic' | 'AliasOrigin' | 'UnpaidExecution'; + } + + /** @name StagingXcmV3Response (242) */ + interface StagingXcmV3Response extends Enum { + readonly isNull: boolean; + readonly isAssets: boolean; + readonly asAssets: StagingXcmV3MultiassetMultiAssets; + readonly isExecutionResult: boolean; + readonly asExecutionResult: Option>; + readonly isVersion: boolean; + readonly asVersion: u32; + readonly isPalletsInfo: boolean; + readonly asPalletsInfo: Vec; + readonly isDispatchResult: boolean; + readonly asDispatchResult: StagingXcmV3MaybeErrorCode; + readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version' | 'PalletsInfo' | 'DispatchResult'; + } + + /** @name StagingXcmV3TraitsError (245) */ + interface StagingXcmV3TraitsError extends Enum { + readonly isOverflow: boolean; + readonly isUnimplemented: boolean; + readonly isUntrustedReserveLocation: boolean; + readonly isUntrustedTeleportLocation: boolean; + readonly isLocationFull: boolean; + readonly isLocationNotInvertible: boolean; + readonly isBadOrigin: boolean; + readonly isInvalidLocation: boolean; + readonly isAssetNotFound: boolean; + readonly isFailedToTransactAsset: boolean; + readonly isNotWithdrawable: boolean; + readonly isLocationCannotHold: boolean; + readonly isExceedsMaxMessageSize: boolean; + readonly isDestinationUnsupported: boolean; + readonly isTransport: boolean; + readonly isUnroutable: boolean; + readonly isUnknownClaim: boolean; + readonly isFailedToDecode: boolean; + readonly isMaxWeightInvalid: boolean; + readonly isNotHoldingFees: boolean; + readonly isTooExpensive: boolean; + readonly isTrap: boolean; + readonly asTrap: u64; + readonly isExpectationFalse: boolean; + readonly isPalletNotFound: boolean; + readonly isNameMismatch: boolean; + readonly isVersionIncompatible: boolean; + readonly isHoldingWouldOverflow: boolean; + readonly isExportError: boolean; + readonly isReanchorFailed: boolean; + readonly isNoDeal: boolean; + readonly isFeesNotMet: boolean; + readonly isLockError: boolean; + readonly isNoPermission: boolean; + readonly isUnanchored: boolean; + readonly isNotDepositable: boolean; + readonly isUnhandledXcmVersion: boolean; + readonly isWeightLimitReached: boolean; + readonly asWeightLimitReached: SpWeightsWeightV2Weight; + readonly isBarrier: boolean; + readonly isWeightNotComputable: boolean; + readonly isExceedsStackLimit: boolean; + readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'LocationFull' | 'LocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'ExpectationFalse' | 'PalletNotFound' | 'NameMismatch' | 'VersionIncompatible' | 'HoldingWouldOverflow' | 'ExportError' | 'ReanchorFailed' | 'NoDeal' | 'FeesNotMet' | 'LockError' | 'NoPermission' | 'Unanchored' | 'NotDepositable' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable' | 'ExceedsStackLimit'; + } + + /** @name StagingXcmV3PalletInfo (247) */ + interface StagingXcmV3PalletInfo extends Struct { + readonly index: Compact; + readonly name: Bytes; + readonly moduleName: Bytes; + readonly major: Compact; + readonly minor: Compact; + readonly patch: Compact; + } + + /** @name StagingXcmV3MaybeErrorCode (250) */ + interface StagingXcmV3MaybeErrorCode extends Enum { + readonly isSuccess: boolean; + readonly isError: boolean; + readonly asError: Bytes; + readonly isTruncatedError: boolean; + readonly asTruncatedError: Bytes; + readonly type: 'Success' | 'Error' | 'TruncatedError'; + } + + /** @name StagingXcmV3QueryResponseInfo (253) */ + interface StagingXcmV3QueryResponseInfo extends Struct { + readonly destination: StagingXcmV3MultiLocation; + readonly queryId: Compact; + readonly maxWeight: SpWeightsWeightV2Weight; + } + + /** @name StagingXcmV3MultiassetMultiAssetFilter (254) */ + interface StagingXcmV3MultiassetMultiAssetFilter extends Enum { + readonly isDefinite: boolean; + readonly asDefinite: StagingXcmV3MultiassetMultiAssets; + readonly isWild: boolean; + readonly asWild: StagingXcmV3MultiassetWildMultiAsset; + readonly type: 'Definite' | 'Wild'; + } + + /** @name StagingXcmV3MultiassetWildMultiAsset (255) */ + interface StagingXcmV3MultiassetWildMultiAsset extends Enum { + readonly isAll: boolean; + readonly isAllOf: boolean; + readonly asAllOf: { + readonly id: StagingXcmV3MultiassetAssetId; + readonly fun: StagingXcmV3MultiassetWildFungibility; + } & Struct; + readonly isAllCounted: boolean; + readonly asAllCounted: Compact; + readonly isAllOfCounted: boolean; + readonly asAllOfCounted: { + readonly id: StagingXcmV3MultiassetAssetId; + readonly fun: StagingXcmV3MultiassetWildFungibility; + readonly count: Compact; + } & Struct; + readonly type: 'All' | 'AllOf' | 'AllCounted' | 'AllOfCounted'; + } + + /** @name StagingXcmV3MultiassetWildFungibility (256) */ + interface StagingXcmV3MultiassetWildFungibility extends Enum { + readonly isFungible: boolean; + readonly isNonFungible: boolean; + readonly type: 'Fungible' | 'NonFungible'; + } + + /** @name CumulusPalletXcmCall (265) */ + type CumulusPalletXcmCall = Null; + + /** @name CumulusPalletDmpQueueCall (266) */ + interface CumulusPalletDmpQueueCall extends Enum { + readonly isServiceOverweight: boolean; + readonly asServiceOverweight: { + readonly index: u64; + readonly weightLimit: SpWeightsWeightV2Weight; + } & Struct; + readonly type: 'ServiceOverweight'; + } + + /** @name PalletInflationCall (267) */ + interface PalletInflationCall extends Enum { + readonly isStartInflation: boolean; + readonly asStartInflation: { + readonly inflationStartRelayBlock: u32; + } & Struct; + readonly type: 'StartInflation'; + } + + /** @name PalletUniqueCall (268) */ + interface PalletUniqueCall extends Enum { + readonly isCreateCollection: boolean; + readonly asCreateCollection: { + readonly collectionName: Vec; + readonly collectionDescription: Vec; + readonly tokenPrefix: Bytes; + readonly mode: UpDataStructsCollectionMode; + } & Struct; + readonly isCreateCollectionEx: boolean; + readonly asCreateCollectionEx: { + readonly data: UpDataStructsCreateCollectionData; + } & Struct; + readonly isDestroyCollection: boolean; + readonly asDestroyCollection: { + readonly collectionId: u32; + } & Struct; + readonly isAddToAllowList: boolean; + readonly asAddToAllowList: { + readonly collectionId: u32; + readonly address: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isRemoveFromAllowList: boolean; + readonly asRemoveFromAllowList: { + readonly collectionId: u32; + readonly address: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isChangeCollectionOwner: boolean; + readonly asChangeCollectionOwner: { + readonly collectionId: u32; + readonly newOwner: AccountId32; + } & Struct; + readonly isAddCollectionAdmin: boolean; + readonly asAddCollectionAdmin: { + readonly collectionId: u32; + readonly newAdminId: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isRemoveCollectionAdmin: boolean; + readonly asRemoveCollectionAdmin: { + readonly collectionId: u32; + readonly accountId: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isSetCollectionSponsor: boolean; + readonly asSetCollectionSponsor: { + readonly collectionId: u32; + readonly newSponsor: AccountId32; + } & Struct; + readonly isConfirmSponsorship: boolean; + readonly asConfirmSponsorship: { + readonly collectionId: u32; + } & Struct; + readonly isRemoveCollectionSponsor: boolean; + readonly asRemoveCollectionSponsor: { + readonly collectionId: u32; + } & Struct; + readonly isCreateItem: boolean; + readonly asCreateItem: { + readonly collectionId: u32; + readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; + readonly data: UpDataStructsCreateItemData; + } & Struct; + readonly isCreateMultipleItems: boolean; + readonly asCreateMultipleItems: { + readonly collectionId: u32; + readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; + readonly itemsData: Vec; + } & Struct; + readonly isSetCollectionProperties: boolean; + readonly asSetCollectionProperties: { + readonly collectionId: u32; + readonly properties: Vec; + } & Struct; + readonly isDeleteCollectionProperties: boolean; + readonly asDeleteCollectionProperties: { + readonly collectionId: u32; + readonly propertyKeys: Vec; + } & Struct; + readonly isSetTokenProperties: boolean; + readonly asSetTokenProperties: { + readonly collectionId: u32; + readonly tokenId: u32; + readonly properties: Vec; + } & Struct; + readonly isDeleteTokenProperties: boolean; + readonly asDeleteTokenProperties: { + readonly collectionId: u32; + readonly tokenId: u32; + readonly propertyKeys: Vec; + } & Struct; + readonly isSetTokenPropertyPermissions: boolean; + readonly asSetTokenPropertyPermissions: { + readonly collectionId: u32; + readonly propertyPermissions: Vec; + } & Struct; + readonly isCreateMultipleItemsEx: boolean; + readonly asCreateMultipleItemsEx: { + readonly collectionId: u32; + readonly data: UpDataStructsCreateItemExData; + } & Struct; + readonly isSetTransfersEnabledFlag: boolean; + readonly asSetTransfersEnabledFlag: { + readonly collectionId: u32; + readonly value: bool; + } & Struct; + readonly isBurnItem: boolean; + readonly asBurnItem: { + readonly collectionId: u32; + readonly itemId: u32; + readonly value: u128; + } & Struct; + readonly isBurnFrom: boolean; + readonly asBurnFrom: { + readonly collectionId: u32; + readonly from: PalletEvmAccountBasicCrossAccountIdRepr; + readonly itemId: u32; + readonly value: u128; + } & Struct; + readonly isTransfer: boolean; + readonly asTransfer: { + readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr; + readonly collectionId: u32; + readonly itemId: u32; + readonly value: u128; + } & Struct; + readonly isApprove: boolean; + readonly asApprove: { + readonly spender: PalletEvmAccountBasicCrossAccountIdRepr; + readonly collectionId: u32; + readonly itemId: u32; + readonly amount: u128; + } & Struct; + readonly isApproveFrom: boolean; + readonly asApproveFrom: { + readonly from: PalletEvmAccountBasicCrossAccountIdRepr; + readonly to: PalletEvmAccountBasicCrossAccountIdRepr; + readonly collectionId: u32; + readonly itemId: u32; + readonly amount: u128; + } & Struct; + readonly isTransferFrom: boolean; + readonly asTransferFrom: { + readonly from: PalletEvmAccountBasicCrossAccountIdRepr; + readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr; + readonly collectionId: u32; + readonly itemId: u32; + readonly value: u128; + } & Struct; + readonly isSetCollectionLimits: boolean; + readonly asSetCollectionLimits: { + readonly collectionId: u32; + readonly newLimit: UpDataStructsCollectionLimits; + } & Struct; + readonly isSetCollectionPermissions: boolean; + readonly asSetCollectionPermissions: { + readonly collectionId: u32; + readonly newPermission: UpDataStructsCollectionPermissions; + } & Struct; + readonly isRepartition: boolean; + readonly asRepartition: { + readonly collectionId: u32; + readonly tokenId: u32; + readonly amount: u128; + } & Struct; + readonly isSetAllowanceForAll: boolean; + readonly asSetAllowanceForAll: { + readonly collectionId: u32; + readonly operator: PalletEvmAccountBasicCrossAccountIdRepr; + readonly approve: bool; + } & Struct; + readonly isForceRepairCollection: boolean; + readonly asForceRepairCollection: { + readonly collectionId: u32; + } & Struct; + readonly isForceRepairItem: boolean; + readonly asForceRepairItem: { + readonly collectionId: u32; + readonly itemId: u32; + } & Struct; + readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'ApproveFrom' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem'; + } + + /** @name UpDataStructsCollectionMode (273) */ + interface UpDataStructsCollectionMode extends Enum { + readonly isNft: boolean; + readonly isFungible: boolean; + readonly asFungible: u8; + readonly isReFungible: boolean; + readonly type: 'Nft' | 'Fungible' | 'ReFungible'; + } + + /** @name UpDataStructsCreateCollectionData (274) */ + interface UpDataStructsCreateCollectionData extends Struct { + readonly mode: UpDataStructsCollectionMode; + readonly access: Option; + readonly name: Vec; + readonly description: Vec; + readonly tokenPrefix: Bytes; + readonly limits: Option; + readonly permissions: Option; + readonly tokenPropertyPermissions: Vec; + readonly properties: Vec; + readonly adminList: Vec; + readonly pendingSponsor: Option; + readonly flags: U8aFixed; + } + + /** @name PalletEvmAccountBasicCrossAccountIdRepr (275) */ + interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum { + readonly isSubstrate: boolean; + readonly asSubstrate: AccountId32; + readonly isEthereum: boolean; + readonly asEthereum: H160; + readonly type: 'Substrate' | 'Ethereum'; + } + + /** @name UpDataStructsAccessMode (277) */ + interface UpDataStructsAccessMode extends Enum { + readonly isNormal: boolean; + readonly isAllowList: boolean; + readonly type: 'Normal' | 'AllowList'; + } + + /** @name UpDataStructsCollectionLimits (279) */ + interface UpDataStructsCollectionLimits extends Struct { + readonly accountTokenOwnershipLimit: Option; + readonly sponsoredDataSize: Option; + readonly sponsoredDataRateLimit: Option; + readonly tokenLimit: Option; + readonly sponsorTransferTimeout: Option; + readonly sponsorApproveTimeout: Option; + readonly ownerCanTransfer: Option; + readonly ownerCanDestroy: Option; + readonly transfersEnabled: Option; + } + + /** @name UpDataStructsSponsoringRateLimit (281) */ + interface UpDataStructsSponsoringRateLimit extends Enum { + readonly isSponsoringDisabled: boolean; + readonly isBlocks: boolean; + readonly asBlocks: u32; + readonly type: 'SponsoringDisabled' | 'Blocks'; + } + + /** @name UpDataStructsCollectionPermissions (284) */ + interface UpDataStructsCollectionPermissions extends Struct { + readonly access: Option; + readonly mintMode: Option; + readonly nesting: Option; + } + + /** @name UpDataStructsNestingPermissions (286) */ + interface UpDataStructsNestingPermissions extends Struct { + readonly tokenOwner: bool; + readonly collectionAdmin: bool; + readonly restricted: Option; + } + + /** @name UpDataStructsOwnerRestrictedSet (288) */ + interface UpDataStructsOwnerRestrictedSet extends BTreeSet {} + + /** @name UpDataStructsPropertyKeyPermission (294) */ + interface UpDataStructsPropertyKeyPermission extends Struct { + readonly key: Bytes; + readonly permission: UpDataStructsPropertyPermission; + } + + /** @name UpDataStructsPropertyPermission (296) */ + interface UpDataStructsPropertyPermission extends Struct { + readonly mutable: bool; + readonly collectionAdmin: bool; + readonly tokenOwner: bool; + } + + /** @name UpDataStructsProperty (299) */ + interface UpDataStructsProperty extends Struct { + readonly key: Bytes; + readonly value: Bytes; + } + + /** @name UpDataStructsCreateItemData (304) */ + interface UpDataStructsCreateItemData extends Enum { + readonly isNft: boolean; + readonly asNft: UpDataStructsCreateNftData; + readonly isFungible: boolean; + readonly asFungible: UpDataStructsCreateFungibleData; + readonly isReFungible: boolean; + readonly asReFungible: UpDataStructsCreateReFungibleData; + readonly type: 'Nft' | 'Fungible' | 'ReFungible'; + } + + /** @name UpDataStructsCreateNftData (305) */ + interface UpDataStructsCreateNftData extends Struct { + readonly properties: Vec; + } + + /** @name UpDataStructsCreateFungibleData (306) */ + interface UpDataStructsCreateFungibleData extends Struct { + readonly value: u128; + } + + /** @name UpDataStructsCreateReFungibleData (307) */ + interface UpDataStructsCreateReFungibleData extends Struct { + readonly pieces: u128; + readonly properties: Vec; + } + + /** @name UpDataStructsCreateItemExData (311) */ + interface UpDataStructsCreateItemExData extends Enum { + readonly isNft: boolean; + readonly asNft: Vec; + readonly isFungible: boolean; + readonly asFungible: BTreeMap; + readonly isRefungibleMultipleItems: boolean; + readonly asRefungibleMultipleItems: Vec; + readonly isRefungibleMultipleOwners: boolean; + readonly asRefungibleMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners; + readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners'; + } + + /** @name UpDataStructsCreateNftExData (313) */ + interface UpDataStructsCreateNftExData extends Struct { + readonly properties: Vec; + readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; + } + + /** @name UpDataStructsCreateRefungibleExSingleOwner (320) */ + interface UpDataStructsCreateRefungibleExSingleOwner extends Struct { + readonly user: PalletEvmAccountBasicCrossAccountIdRepr; + readonly pieces: u128; + readonly properties: Vec; + } + + /** @name UpDataStructsCreateRefungibleExMultipleOwners (322) */ + interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct { + readonly users: BTreeMap; + readonly properties: Vec; + } + + /** @name PalletConfigurationCall (323) */ + interface PalletConfigurationCall extends Enum { + readonly isSetWeightToFeeCoefficientOverride: boolean; + readonly asSetWeightToFeeCoefficientOverride: { + readonly coeff: Option; + } & Struct; + readonly isSetMinGasPriceOverride: boolean; + readonly asSetMinGasPriceOverride: { + readonly coeff: Option; + } & Struct; + readonly isSetAppPromotionConfigurationOverride: boolean; + readonly asSetAppPromotionConfigurationOverride: { + readonly configuration: PalletConfigurationAppPromotionConfiguration; + } & Struct; + readonly isSetCollatorSelectionDesiredCollators: boolean; + readonly asSetCollatorSelectionDesiredCollators: { + readonly max: Option; + } & Struct; + readonly isSetCollatorSelectionLicenseBond: boolean; + readonly asSetCollatorSelectionLicenseBond: { + readonly amount: Option; + } & Struct; + readonly isSetCollatorSelectionKickThreshold: boolean; + readonly asSetCollatorSelectionKickThreshold: { + readonly threshold: Option; + } & Struct; + readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetAppPromotionConfigurationOverride' | 'SetCollatorSelectionDesiredCollators' | 'SetCollatorSelectionLicenseBond' | 'SetCollatorSelectionKickThreshold'; + } + + /** @name PalletConfigurationAppPromotionConfiguration (325) */ + interface PalletConfigurationAppPromotionConfiguration extends Struct { + readonly recalculationInterval: Option; + readonly pendingInterval: Option; + readonly intervalIncome: Option; + readonly maxStakersPerCalculation: Option; + } + + /** @name PalletStructureCall (330) */ + type PalletStructureCall = Null; + + /** @name PalletAppPromotionCall (331) */ + interface PalletAppPromotionCall extends Enum { + readonly isSetAdminAddress: boolean; + readonly asSetAdminAddress: { + readonly admin: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isStake: boolean; + readonly asStake: { + readonly amount: u128; + } & Struct; + readonly isUnstakeAll: boolean; + readonly isSponsorCollection: boolean; + readonly asSponsorCollection: { + readonly collectionId: u32; + } & Struct; + readonly isStopSponsoringCollection: boolean; + readonly asStopSponsoringCollection: { + readonly collectionId: u32; + } & Struct; + readonly isSponsorContract: boolean; + readonly asSponsorContract: { + readonly contractId: H160; + } & Struct; + readonly isStopSponsoringContract: boolean; + readonly asStopSponsoringContract: { + readonly contractId: H160; + } & Struct; + readonly isPayoutStakers: boolean; + readonly asPayoutStakers: { + readonly stakersNumber: Option; + } & Struct; + readonly isUnstakePartial: boolean; + readonly asUnstakePartial: { + readonly amount: u128; + } & Struct; + readonly isForceUnstake: boolean; + readonly asForceUnstake: { + readonly pendingBlocks: Vec; + } & Struct; + readonly type: 'SetAdminAddress' | 'Stake' | 'UnstakeAll' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers' | 'UnstakePartial' | 'ForceUnstake'; + } + + /** @name PalletForeignAssetsModuleCall (333) */ + interface PalletForeignAssetsModuleCall extends Enum { + readonly isRegisterForeignAsset: boolean; + readonly asRegisterForeignAsset: { + readonly owner: AccountId32; + readonly location: StagingXcmVersionedMultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isUpdateForeignAsset: boolean; + readonly asUpdateForeignAsset: { + readonly foreignAssetId: u32; + readonly location: StagingXcmVersionedMultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset'; + } + + /** @name PalletForeignAssetsModuleAssetMetadata (334) */ + interface PalletForeignAssetsModuleAssetMetadata extends Struct { + readonly name: Bytes; + readonly symbol: Bytes; + readonly decimals: u8; + readonly minimalBalance: u128; + } + + /** @name PalletEvmCall (337) */ + interface PalletEvmCall extends Enum { + readonly isWithdraw: boolean; + readonly asWithdraw: { + readonly address: H160; + readonly value: u128; + } & Struct; + readonly isCall: boolean; + readonly asCall: { + readonly source: H160; + readonly target: H160; + readonly input: Bytes; + readonly value: U256; + readonly gasLimit: u64; + readonly maxFeePerGas: U256; + readonly maxPriorityFeePerGas: Option; + readonly nonce: Option; + readonly accessList: Vec]>>; + } & Struct; + readonly isCreate: boolean; + readonly asCreate: { + readonly source: H160; + readonly init: Bytes; + readonly value: U256; + readonly gasLimit: u64; + readonly maxFeePerGas: U256; + readonly maxPriorityFeePerGas: Option; + readonly nonce: Option; + readonly accessList: Vec]>>; + } & Struct; + readonly isCreate2: boolean; + readonly asCreate2: { + readonly source: H160; + readonly init: Bytes; + readonly salt: H256; + readonly value: U256; + readonly gasLimit: u64; + readonly maxFeePerGas: U256; + readonly maxPriorityFeePerGas: Option; + readonly nonce: Option; + readonly accessList: Vec]>>; + } & Struct; + readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2'; + } + + /** @name PalletEthereumCall (344) */ + interface PalletEthereumCall extends Enum { + readonly isTransact: boolean; + readonly asTransact: { + readonly transaction: EthereumTransactionTransactionV2; + } & Struct; + readonly type: 'Transact'; + } + + /** @name EthereumTransactionTransactionV2 (345) */ + interface EthereumTransactionTransactionV2 extends Enum { + readonly isLegacy: boolean; + readonly asLegacy: EthereumTransactionLegacyTransaction; + readonly isEip2930: boolean; + readonly asEip2930: EthereumTransactionEip2930Transaction; + readonly isEip1559: boolean; + readonly asEip1559: EthereumTransactionEip1559Transaction; + readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; + } + + /** @name EthereumTransactionLegacyTransaction (346) */ + interface EthereumTransactionLegacyTransaction extends Struct { + readonly nonce: U256; + readonly gasPrice: U256; + readonly gasLimit: U256; + readonly action: EthereumTransactionTransactionAction; + readonly value: U256; + readonly input: Bytes; + readonly signature: EthereumTransactionTransactionSignature; + } + + /** @name EthereumTransactionTransactionAction (347) */ + interface EthereumTransactionTransactionAction extends Enum { + readonly isCall: boolean; + readonly asCall: H160; + readonly isCreate: boolean; + readonly type: 'Call' | 'Create'; + } + + /** @name EthereumTransactionTransactionSignature (348) */ + interface EthereumTransactionTransactionSignature extends Struct { + readonly v: u64; + readonly r: H256; + readonly s: H256; + } + + /** @name EthereumTransactionEip2930Transaction (350) */ + interface EthereumTransactionEip2930Transaction extends Struct { + readonly chainId: u64; + readonly nonce: U256; + readonly gasPrice: U256; + readonly gasLimit: U256; + readonly action: EthereumTransactionTransactionAction; + readonly value: U256; + readonly input: Bytes; + readonly accessList: Vec; + readonly oddYParity: bool; + readonly r: H256; + readonly s: H256; + } + + /** @name EthereumTransactionAccessListItem (352) */ + interface EthereumTransactionAccessListItem extends Struct { + readonly address: H160; + readonly storageKeys: Vec; + } + + /** @name EthereumTransactionEip1559Transaction (353) */ + interface EthereumTransactionEip1559Transaction extends Struct { + readonly chainId: u64; + readonly nonce: U256; + readonly maxPriorityFeePerGas: U256; + readonly maxFeePerGas: U256; + readonly gasLimit: U256; + readonly action: EthereumTransactionTransactionAction; + readonly value: U256; + readonly input: Bytes; + readonly accessList: Vec; + readonly oddYParity: bool; + readonly r: H256; + readonly s: H256; + } + + /** @name PalletEvmContractHelpersCall (354) */ + interface PalletEvmContractHelpersCall extends Enum { + readonly isMigrateFromSelfSponsoring: boolean; + readonly asMigrateFromSelfSponsoring: { + readonly addresses: Vec; + } & Struct; + readonly type: 'MigrateFromSelfSponsoring'; + } + + /** @name PalletEvmMigrationCall (356) */ + interface PalletEvmMigrationCall extends Enum { + readonly isBegin: boolean; + readonly asBegin: { + readonly address: H160; + } & Struct; + readonly isSetData: boolean; + readonly asSetData: { + readonly address: H160; + readonly data: Vec>; + } & Struct; + readonly isFinish: boolean; + readonly asFinish: { + readonly address: H160; + readonly code: Bytes; + } & Struct; + readonly isInsertEthLogs: boolean; + readonly asInsertEthLogs: { + readonly logs: Vec; + } & Struct; + readonly isInsertEvents: boolean; + readonly asInsertEvents: { + readonly events: Vec; + } & Struct; + readonly isRemoveRmrkData: boolean; + readonly type: 'Begin' | 'SetData' | 'Finish' | 'InsertEthLogs' | 'InsertEvents' | 'RemoveRmrkData'; + } + + /** @name EthereumLog (360) */ + interface EthereumLog extends Struct { + readonly address: H160; + readonly topics: Vec; + readonly data: Bytes; + } + + /** @name PalletMaintenanceCall (361) */ + interface PalletMaintenanceCall extends Enum { + readonly isEnable: boolean; + readonly isDisable: boolean; + readonly type: 'Enable' | 'Disable'; + } + + /** @name PalletUtilityCall (362) */ + interface PalletUtilityCall extends Enum { + readonly isBatch: boolean; + readonly asBatch: { + readonly calls: Vec; + } & Struct; + readonly isAsDerivative: boolean; + readonly asAsDerivative: { + readonly index: u16; + readonly call: Call; + } & Struct; + readonly isBatchAll: boolean; + readonly asBatchAll: { + readonly calls: Vec; + } & Struct; + readonly isDispatchAs: boolean; + readonly asDispatchAs: { + readonly asOrigin: OpalRuntimeOriginCaller; + readonly call: Call; + } & Struct; + readonly isForceBatch: boolean; + readonly asForceBatch: { + readonly calls: Vec; + } & Struct; + readonly isWithWeight: boolean; + readonly asWithWeight: { + readonly call: Call; + readonly weight: SpWeightsWeightV2Weight; + } & Struct; + readonly type: 'Batch' | 'AsDerivative' | 'BatchAll' | 'DispatchAs' | 'ForceBatch' | 'WithWeight'; + } + + /** @name PalletTestUtilsCall (364) */ + interface PalletTestUtilsCall extends Enum { + readonly isEnable: boolean; + readonly isSetTestValue: boolean; + readonly asSetTestValue: { + readonly value: u32; + } & Struct; + readonly isSetTestValueAndRollback: boolean; + readonly asSetTestValueAndRollback: { + readonly value: u32; + } & Struct; + readonly isIncTestValue: boolean; + readonly isJustTakeFee: boolean; + readonly isBatchAll: boolean; + readonly asBatchAll: { + readonly calls: Vec; + } & Struct; + readonly type: 'Enable' | 'SetTestValue' | 'SetTestValueAndRollback' | 'IncTestValue' | 'JustTakeFee' | 'BatchAll'; + } + + /** @name PalletSchedulerEvent (366) */ + interface PalletSchedulerEvent extends Enum { + readonly isScheduled: boolean; + readonly asScheduled: { + readonly when: u32; + readonly index: u32; + } & Struct; + readonly isCanceled: boolean; + readonly asCanceled: { + readonly when: u32; + readonly index: u32; + } & Struct; + readonly isDispatched: boolean; + readonly asDispatched: { + readonly task: ITuple<[u32, u32]>; + readonly id: Option; + readonly result: Result; + } & Struct; + readonly isCallUnavailable: boolean; + readonly asCallUnavailable: { + readonly task: ITuple<[u32, u32]>; + readonly id: Option; + } & Struct; + readonly isPeriodicFailed: boolean; + readonly asPeriodicFailed: { + readonly task: ITuple<[u32, u32]>; + readonly id: Option; + } & Struct; + readonly isPermanentlyOverweight: boolean; + readonly asPermanentlyOverweight: { + readonly task: ITuple<[u32, u32]>; + readonly id: Option; + } & Struct; + readonly type: 'Scheduled' | 'Canceled' | 'Dispatched' | 'CallUnavailable' | 'PeriodicFailed' | 'PermanentlyOverweight'; + } + + /** @name CumulusPalletXcmpQueueEvent (367) */ + interface CumulusPalletXcmpQueueEvent extends Enum { + readonly isSuccess: boolean; + readonly asSuccess: { + readonly messageHash: U8aFixed; + readonly messageId: U8aFixed; + readonly weight: SpWeightsWeightV2Weight; + } & Struct; + readonly isFail: boolean; + readonly asFail: { + readonly messageHash: U8aFixed; + readonly messageId: U8aFixed; + readonly error: StagingXcmV3TraitsError; + readonly weight: SpWeightsWeightV2Weight; + } & Struct; + readonly isBadVersion: boolean; + readonly asBadVersion: { + readonly messageHash: U8aFixed; + } & Struct; + readonly isBadFormat: boolean; + readonly asBadFormat: { + readonly messageHash: U8aFixed; + } & Struct; + readonly isXcmpMessageSent: boolean; + readonly asXcmpMessageSent: { + readonly messageHash: U8aFixed; + } & Struct; + readonly isOverweightEnqueued: boolean; + readonly asOverweightEnqueued: { + readonly sender: u32; + readonly sentAt: u32; + readonly index: u64; + readonly required: SpWeightsWeightV2Weight; + } & Struct; + readonly isOverweightServiced: boolean; + readonly asOverweightServiced: { + readonly index: u64; + readonly used: SpWeightsWeightV2Weight; + } & Struct; + readonly type: 'Success' | 'Fail' | 'BadVersion' | 'BadFormat' | 'XcmpMessageSent' | 'OverweightEnqueued' | 'OverweightServiced'; + } + + /** @name PalletXcmEvent (368) */ + interface PalletXcmEvent extends Enum { + readonly isAttempted: boolean; + readonly asAttempted: { + readonly outcome: StagingXcmV3TraitsOutcome; + } & Struct; + readonly isSent: boolean; + readonly asSent: { + readonly origin: StagingXcmV3MultiLocation; + readonly destination: StagingXcmV3MultiLocation; + readonly message: StagingXcmV3Xcm; + readonly messageId: U8aFixed; + } & Struct; + readonly isUnexpectedResponse: boolean; + readonly asUnexpectedResponse: { + readonly origin: StagingXcmV3MultiLocation; + readonly queryId: u64; + } & Struct; + readonly isResponseReady: boolean; + readonly asResponseReady: { + readonly queryId: u64; + readonly response: StagingXcmV3Response; + } & Struct; + readonly isNotified: boolean; + readonly asNotified: { + readonly queryId: u64; + readonly palletIndex: u8; + readonly callIndex: u8; + } & Struct; + readonly isNotifyOverweight: boolean; + readonly asNotifyOverweight: { + readonly queryId: u64; + readonly palletIndex: u8; + readonly callIndex: u8; + readonly actualWeight: SpWeightsWeightV2Weight; + readonly maxBudgetedWeight: SpWeightsWeightV2Weight; + } & Struct; + readonly isNotifyDispatchError: boolean; + readonly asNotifyDispatchError: { + readonly queryId: u64; + readonly palletIndex: u8; + readonly callIndex: u8; + } & Struct; + readonly isNotifyDecodeFailed: boolean; + readonly asNotifyDecodeFailed: { + readonly queryId: u64; + readonly palletIndex: u8; + readonly callIndex: u8; + } & Struct; + readonly isInvalidResponder: boolean; + readonly asInvalidResponder: { + readonly origin: StagingXcmV3MultiLocation; + readonly queryId: u64; + readonly expectedLocation: Option; + } & Struct; + readonly isInvalidResponderVersion: boolean; + readonly asInvalidResponderVersion: { + readonly origin: StagingXcmV3MultiLocation; + readonly queryId: u64; + } & Struct; + readonly isResponseTaken: boolean; + readonly asResponseTaken: { + readonly queryId: u64; + } & Struct; + readonly isAssetsTrapped: boolean; + readonly asAssetsTrapped: { + readonly hash_: H256; + readonly origin: StagingXcmV3MultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + } & Struct; + readonly isVersionChangeNotified: boolean; + readonly asVersionChangeNotified: { + readonly destination: StagingXcmV3MultiLocation; + readonly result: u32; + readonly cost: StagingXcmV3MultiassetMultiAssets; + readonly messageId: U8aFixed; + } & Struct; + readonly isSupportedVersionChanged: boolean; + readonly asSupportedVersionChanged: { + readonly location: StagingXcmV3MultiLocation; + readonly version: u32; + } & Struct; + readonly isNotifyTargetSendFail: boolean; + readonly asNotifyTargetSendFail: { + readonly location: StagingXcmV3MultiLocation; + readonly queryId: u64; + readonly error: StagingXcmV3TraitsError; + } & Struct; + readonly isNotifyTargetMigrationFail: boolean; + readonly asNotifyTargetMigrationFail: { + readonly location: StagingXcmVersionedMultiLocation; + readonly queryId: u64; + } & Struct; + readonly isInvalidQuerierVersion: boolean; + readonly asInvalidQuerierVersion: { + readonly origin: StagingXcmV3MultiLocation; + readonly queryId: u64; + } & Struct; + readonly isInvalidQuerier: boolean; + readonly asInvalidQuerier: { + readonly origin: StagingXcmV3MultiLocation; + readonly queryId: u64; + readonly expectedQuerier: StagingXcmV3MultiLocation; + readonly maybeActualQuerier: Option; + } & Struct; + readonly isVersionNotifyStarted: boolean; + readonly asVersionNotifyStarted: { + readonly destination: StagingXcmV3MultiLocation; + readonly cost: StagingXcmV3MultiassetMultiAssets; + readonly messageId: U8aFixed; + } & Struct; + readonly isVersionNotifyRequested: boolean; + readonly asVersionNotifyRequested: { + readonly destination: StagingXcmV3MultiLocation; + readonly cost: StagingXcmV3MultiassetMultiAssets; + readonly messageId: U8aFixed; + } & Struct; + readonly isVersionNotifyUnrequested: boolean; + readonly asVersionNotifyUnrequested: { + readonly destination: StagingXcmV3MultiLocation; + readonly cost: StagingXcmV3MultiassetMultiAssets; + readonly messageId: U8aFixed; + } & Struct; + readonly isFeesPaid: boolean; + readonly asFeesPaid: { + readonly paying: StagingXcmV3MultiLocation; + readonly fees: StagingXcmV3MultiassetMultiAssets; + } & Struct; + readonly isAssetsClaimed: boolean; + readonly asAssetsClaimed: { + readonly hash_: H256; + readonly origin: StagingXcmV3MultiLocation; + readonly assets: StagingXcmVersionedMultiAssets; + } & Struct; + readonly type: 'Attempted' | 'Sent' | 'UnexpectedResponse' | 'ResponseReady' | 'Notified' | 'NotifyOverweight' | 'NotifyDispatchError' | 'NotifyDecodeFailed' | 'InvalidResponder' | 'InvalidResponderVersion' | 'ResponseTaken' | 'AssetsTrapped' | 'VersionChangeNotified' | 'SupportedVersionChanged' | 'NotifyTargetSendFail' | 'NotifyTargetMigrationFail' | 'InvalidQuerierVersion' | 'InvalidQuerier' | 'VersionNotifyStarted' | 'VersionNotifyRequested' | 'VersionNotifyUnrequested' | 'FeesPaid' | 'AssetsClaimed'; + } + + /** @name StagingXcmV3TraitsOutcome (369) */ + interface StagingXcmV3TraitsOutcome extends Enum { + readonly isComplete: boolean; + readonly asComplete: SpWeightsWeightV2Weight; + readonly isIncomplete: boolean; + readonly asIncomplete: ITuple<[SpWeightsWeightV2Weight, StagingXcmV3TraitsError]>; + readonly isError: boolean; + readonly asError: StagingXcmV3TraitsError; + readonly type: 'Complete' | 'Incomplete' | 'Error'; + } + + /** @name CumulusPalletXcmEvent (370) */ + interface CumulusPalletXcmEvent extends Enum { + readonly isInvalidFormat: boolean; + readonly asInvalidFormat: U8aFixed; + readonly isUnsupportedVersion: boolean; + readonly asUnsupportedVersion: U8aFixed; + readonly isExecutedDownward: boolean; + readonly asExecutedDownward: ITuple<[U8aFixed, StagingXcmV3TraitsOutcome]>; + readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward'; + } + + /** @name CumulusPalletDmpQueueEvent (371) */ + interface CumulusPalletDmpQueueEvent extends Enum { + readonly isInvalidFormat: boolean; + readonly asInvalidFormat: { + readonly messageHash: U8aFixed; + } & Struct; + readonly isUnsupportedVersion: boolean; + readonly asUnsupportedVersion: { + readonly messageHash: U8aFixed; + } & Struct; + readonly isExecutedDownward: boolean; + readonly asExecutedDownward: { + readonly messageHash: U8aFixed; + readonly messageId: U8aFixed; + readonly outcome: StagingXcmV3TraitsOutcome; + } & Struct; + readonly isWeightExhausted: boolean; + readonly asWeightExhausted: { + readonly messageHash: U8aFixed; + readonly messageId: U8aFixed; + readonly remainingWeight: SpWeightsWeightV2Weight; + readonly requiredWeight: SpWeightsWeightV2Weight; + } & Struct; + readonly isOverweightEnqueued: boolean; + readonly asOverweightEnqueued: { + readonly messageHash: U8aFixed; + readonly messageId: U8aFixed; + readonly overweightIndex: u64; + readonly requiredWeight: SpWeightsWeightV2Weight; + } & Struct; + readonly isOverweightServiced: boolean; + readonly asOverweightServiced: { + readonly overweightIndex: u64; + readonly weightUsed: SpWeightsWeightV2Weight; + } & Struct; + readonly isMaxMessagesExhausted: boolean; + readonly asMaxMessagesExhausted: { + readonly messageHash: U8aFixed; + } & Struct; + readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced' | 'MaxMessagesExhausted'; + } + + /** @name PalletConfigurationEvent (372) */ + interface PalletConfigurationEvent extends Enum { + readonly isNewDesiredCollators: boolean; + readonly asNewDesiredCollators: { + readonly desiredCollators: Option; + } & Struct; + readonly isNewCollatorLicenseBond: boolean; + readonly asNewCollatorLicenseBond: { + readonly bondCost: Option; + } & Struct; + readonly isNewCollatorKickThreshold: boolean; + readonly asNewCollatorKickThreshold: { + readonly lengthInBlocks: Option; + } & Struct; + readonly type: 'NewDesiredCollators' | 'NewCollatorLicenseBond' | 'NewCollatorKickThreshold'; + } + + /** @name PalletCommonEvent (373) */ + interface PalletCommonEvent extends Enum { + readonly isCollectionCreated: boolean; + readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>; + readonly isCollectionDestroyed: boolean; + readonly asCollectionDestroyed: u32; + readonly isItemCreated: boolean; + readonly asItemCreated: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; + readonly isItemDestroyed: boolean; + readonly asItemDestroyed: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; + readonly isTransfer: boolean; + readonly asTransfer: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; + readonly isApproved: boolean; + readonly asApproved: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>; + readonly isApprovedForAll: boolean; + readonly asApprovedForAll: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>; + readonly isCollectionPropertySet: boolean; + readonly asCollectionPropertySet: ITuple<[u32, Bytes]>; + readonly isCollectionPropertyDeleted: boolean; + readonly asCollectionPropertyDeleted: ITuple<[u32, Bytes]>; + readonly isTokenPropertySet: boolean; + readonly asTokenPropertySet: ITuple<[u32, u32, Bytes]>; + readonly isTokenPropertyDeleted: boolean; + readonly asTokenPropertyDeleted: ITuple<[u32, u32, Bytes]>; + readonly isPropertyPermissionSet: boolean; + readonly asPropertyPermissionSet: ITuple<[u32, Bytes]>; + readonly isAllowListAddressAdded: boolean; + readonly asAllowListAddressAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + readonly isAllowListAddressRemoved: boolean; + readonly asAllowListAddressRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + readonly isCollectionAdminAdded: boolean; + readonly asCollectionAdminAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + readonly isCollectionAdminRemoved: boolean; + readonly asCollectionAdminRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + readonly isCollectionLimitSet: boolean; + readonly asCollectionLimitSet: u32; + readonly isCollectionOwnerChanged: boolean; + readonly asCollectionOwnerChanged: ITuple<[u32, AccountId32]>; + readonly isCollectionPermissionSet: boolean; + readonly asCollectionPermissionSet: u32; + readonly isCollectionSponsorSet: boolean; + readonly asCollectionSponsorSet: ITuple<[u32, AccountId32]>; + readonly isSponsorshipConfirmed: boolean; + readonly asSponsorshipConfirmed: ITuple<[u32, AccountId32]>; + readonly isCollectionSponsorRemoved: boolean; + readonly asCollectionSponsorRemoved: u32; + readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet' | 'AllowListAddressAdded' | 'AllowListAddressRemoved' | 'CollectionAdminAdded' | 'CollectionAdminRemoved' | 'CollectionLimitSet' | 'CollectionOwnerChanged' | 'CollectionPermissionSet' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionSponsorRemoved'; + } + + /** @name PalletStructureEvent (374) */ + interface PalletStructureEvent extends Enum { + readonly isExecuted: boolean; + readonly asExecuted: Result; + readonly type: 'Executed'; + } + + /** @name PalletAppPromotionEvent (375) */ + interface PalletAppPromotionEvent extends Enum { + readonly isStakingRecalculation: boolean; + readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>; + readonly isStake: boolean; + readonly asStake: ITuple<[AccountId32, u128]>; + readonly isUnstake: boolean; + readonly asUnstake: ITuple<[AccountId32, u128]>; + readonly isSetAdmin: boolean; + readonly asSetAdmin: AccountId32; + readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin'; + } + + /** @name PalletForeignAssetsModuleEvent (376) */ + interface PalletForeignAssetsModuleEvent extends Enum { + readonly isForeignAssetRegistered: boolean; + readonly asForeignAssetRegistered: { + readonly assetId: u32; + readonly assetAddress: StagingXcmV3MultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isForeignAssetUpdated: boolean; + readonly asForeignAssetUpdated: { + readonly assetId: u32; + readonly assetAddress: StagingXcmV3MultiLocation; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isAssetRegistered: boolean; + readonly asAssetRegistered: { + readonly assetId: PalletForeignAssetsAssetId; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly isAssetUpdated: boolean; + readonly asAssetUpdated: { + readonly assetId: PalletForeignAssetsAssetId; + readonly metadata: PalletForeignAssetsModuleAssetMetadata; + } & Struct; + readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated'; + } + + /** @name PalletEvmEvent (377) */ + interface PalletEvmEvent extends Enum { + readonly isLog: boolean; + readonly asLog: { + readonly log: EthereumLog; + } & Struct; + readonly isCreated: boolean; + readonly asCreated: { + readonly address: H160; + } & Struct; + readonly isCreatedFailed: boolean; + readonly asCreatedFailed: { + readonly address: H160; + } & Struct; + readonly isExecuted: boolean; + readonly asExecuted: { + readonly address: H160; + } & Struct; + readonly isExecutedFailed: boolean; + readonly asExecutedFailed: { + readonly address: H160; + } & Struct; + readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed'; + } + + /** @name PalletEthereumEvent (378) */ + interface PalletEthereumEvent extends Enum { + readonly isExecuted: boolean; + readonly asExecuted: { + readonly from: H160; + readonly to: H160; + readonly transactionHash: H256; + readonly exitReason: EvmCoreErrorExitReason; + readonly extraData: Bytes; + } & Struct; + readonly type: 'Executed'; + } + + /** @name EvmCoreErrorExitReason (379) */ + interface EvmCoreErrorExitReason extends Enum { + readonly isSucceed: boolean; + readonly asSucceed: EvmCoreErrorExitSucceed; + readonly isError: boolean; + readonly asError: EvmCoreErrorExitError; + readonly isRevert: boolean; + readonly asRevert: EvmCoreErrorExitRevert; + readonly isFatal: boolean; + readonly asFatal: EvmCoreErrorExitFatal; + readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal'; + } + + /** @name EvmCoreErrorExitSucceed (380) */ + interface EvmCoreErrorExitSucceed extends Enum { + readonly isStopped: boolean; + readonly isReturned: boolean; + readonly isSuicided: boolean; + readonly type: 'Stopped' | 'Returned' | 'Suicided'; + } + + /** @name EvmCoreErrorExitError (381) */ + interface EvmCoreErrorExitError extends Enum { + readonly isStackUnderflow: boolean; + readonly isStackOverflow: boolean; + readonly isInvalidJump: boolean; + readonly isInvalidRange: boolean; + readonly isDesignatedInvalid: boolean; + readonly isCallTooDeep: boolean; + readonly isCreateCollision: boolean; + readonly isCreateContractLimit: boolean; + readonly isOutOfOffset: boolean; + readonly isOutOfGas: boolean; + readonly isOutOfFund: boolean; + readonly isPcUnderflow: boolean; + readonly isCreateEmpty: boolean; + readonly isOther: boolean; + readonly asOther: Text; + readonly isMaxNonce: boolean; + readonly isInvalidCode: boolean; + readonly asInvalidCode: u8; + readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'MaxNonce' | 'InvalidCode'; + } + + /** @name EvmCoreErrorExitRevert (385) */ + interface EvmCoreErrorExitRevert extends Enum { + readonly isReverted: boolean; + readonly type: 'Reverted'; + } + + /** @name EvmCoreErrorExitFatal (386) */ + interface EvmCoreErrorExitFatal extends Enum { + readonly isNotSupported: boolean; + readonly isUnhandledInterrupt: boolean; + readonly isCallErrorAsFatal: boolean; + readonly asCallErrorAsFatal: EvmCoreErrorExitError; + readonly isOther: boolean; + readonly asOther: Text; + readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other'; + } + + /** @name PalletEvmContractHelpersEvent (387) */ + interface PalletEvmContractHelpersEvent extends Enum { + readonly isContractSponsorSet: boolean; + readonly asContractSponsorSet: ITuple<[H160, AccountId32]>; + readonly isContractSponsorshipConfirmed: boolean; + readonly asContractSponsorshipConfirmed: ITuple<[H160, AccountId32]>; + readonly isContractSponsorRemoved: boolean; + readonly asContractSponsorRemoved: H160; + readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved'; + } + + /** @name PalletEvmMigrationEvent (388) */ + interface PalletEvmMigrationEvent extends Enum { + readonly isTestEvent: boolean; + readonly type: 'TestEvent'; + } + + /** @name PalletMaintenanceEvent (389) */ + interface PalletMaintenanceEvent extends Enum { + readonly isMaintenanceEnabled: boolean; + readonly isMaintenanceDisabled: boolean; + readonly type: 'MaintenanceEnabled' | 'MaintenanceDisabled'; + } + + /** @name PalletUtilityEvent (390) */ + interface PalletUtilityEvent extends Enum { + readonly isBatchInterrupted: boolean; + readonly asBatchInterrupted: { + readonly index: u32; + readonly error: SpRuntimeDispatchError; + } & Struct; + readonly isBatchCompleted: boolean; + readonly isBatchCompletedWithErrors: boolean; + readonly isItemCompleted: boolean; + readonly isItemFailed: boolean; + readonly asItemFailed: { + readonly error: SpRuntimeDispatchError; + } & Struct; + readonly isDispatchedAs: boolean; + readonly asDispatchedAs: { + readonly result: Result; + } & Struct; + readonly type: 'BatchInterrupted' | 'BatchCompleted' | 'BatchCompletedWithErrors' | 'ItemCompleted' | 'ItemFailed' | 'DispatchedAs'; + } + + /** @name PalletTestUtilsEvent (391) */ + interface PalletTestUtilsEvent extends Enum { + readonly isValueIsSet: boolean; + readonly isShouldRollback: boolean; + readonly isBatchCompleted: boolean; + readonly type: 'ValueIsSet' | 'ShouldRollback' | 'BatchCompleted'; + } + + /** @name FrameSystemPhase (392) */ + interface FrameSystemPhase extends Enum { + readonly isApplyExtrinsic: boolean; + readonly asApplyExtrinsic: u32; + readonly isFinalization: boolean; + readonly isInitialization: boolean; + readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization'; + } + + /** @name FrameSystemLastRuntimeUpgradeInfo (394) */ + interface FrameSystemLastRuntimeUpgradeInfo extends Struct { + readonly specVersion: Compact; + readonly specName: Text; + } + + /** @name FrameSystemLimitsBlockWeights (395) */ + interface FrameSystemLimitsBlockWeights extends Struct { + readonly baseBlock: SpWeightsWeightV2Weight; + readonly maxBlock: SpWeightsWeightV2Weight; + readonly perClass: FrameSupportDispatchPerDispatchClassWeightsPerClass; + } + + /** @name FrameSupportDispatchPerDispatchClassWeightsPerClass (396) */ + interface FrameSupportDispatchPerDispatchClassWeightsPerClass extends Struct { + readonly normal: FrameSystemLimitsWeightsPerClass; + readonly operational: FrameSystemLimitsWeightsPerClass; + readonly mandatory: FrameSystemLimitsWeightsPerClass; + } + + /** @name FrameSystemLimitsWeightsPerClass (397) */ + interface FrameSystemLimitsWeightsPerClass extends Struct { + readonly baseExtrinsic: SpWeightsWeightV2Weight; + readonly maxExtrinsic: Option; + readonly maxTotal: Option; + readonly reserved: Option; + } + + /** @name FrameSystemLimitsBlockLength (399) */ + interface FrameSystemLimitsBlockLength extends Struct { + readonly max: FrameSupportDispatchPerDispatchClassU32; + } + + /** @name FrameSupportDispatchPerDispatchClassU32 (400) */ + interface FrameSupportDispatchPerDispatchClassU32 extends Struct { + readonly normal: u32; + readonly operational: u32; + readonly mandatory: u32; + } + + /** @name SpWeightsRuntimeDbWeight (401) */ + interface SpWeightsRuntimeDbWeight extends Struct { + readonly read: u64; + readonly write: u64; + } + + /** @name SpVersionRuntimeVersion (402) */ + interface SpVersionRuntimeVersion extends Struct { + readonly specName: Text; + readonly implName: Text; + readonly authoringVersion: u32; + readonly specVersion: u32; + readonly implVersion: u32; + readonly apis: Vec>; + readonly transactionVersion: u32; + readonly stateVersion: u8; + } + + /** @name FrameSystemError (406) */ + interface FrameSystemError extends Enum { + readonly isInvalidSpecName: boolean; + readonly isSpecVersionNeedsToIncrease: boolean; + readonly isFailedToExtractRuntimeVersion: boolean; + readonly isNonDefaultComposite: boolean; + readonly isNonZeroRefCount: boolean; + readonly isCallFiltered: boolean; + readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered'; + } + + /** @name CumulusPalletParachainSystemUnincludedSegmentAncestor (408) */ + interface CumulusPalletParachainSystemUnincludedSegmentAncestor extends Struct { + readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth; + readonly paraHeadHash: Option; + readonly consumedGoAheadSignal: Option; + } + + /** @name CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth (409) */ + interface CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth extends Struct { + readonly umpMsgCount: u32; + readonly umpTotalBytes: u32; + readonly hrmpOutgoing: BTreeMap; + } + + /** @name CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate (411) */ + interface CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate extends Struct { + readonly msgCount: u32; + readonly totalBytes: u32; + } + + /** @name PolkadotPrimitivesV5UpgradeGoAhead (415) */ + interface PolkadotPrimitivesV5UpgradeGoAhead extends Enum { + readonly isAbort: boolean; + readonly isGoAhead: boolean; + readonly type: 'Abort' | 'GoAhead'; + } + + /** @name CumulusPalletParachainSystemUnincludedSegmentSegmentTracker (416) */ + interface CumulusPalletParachainSystemUnincludedSegmentSegmentTracker extends Struct { + readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth; + readonly hrmpWatermark: Option; + readonly consumedGoAheadSignal: Option; + } + + /** @name PolkadotPrimitivesV5UpgradeRestriction (418) */ + interface PolkadotPrimitivesV5UpgradeRestriction extends Enum { + readonly isPresent: boolean; + readonly type: 'Present'; + } + + /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (419) */ + interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct { + readonly dmqMqcHead: H256; + readonly relayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity; + readonly ingressChannels: Vec>; + readonly egressChannels: Vec>; + } + + /** @name CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity (420) */ + interface CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity extends Struct { + readonly remainingCount: u32; + readonly remainingSize: u32; + } + + /** @name PolkadotPrimitivesV5AbridgedHrmpChannel (423) */ + interface PolkadotPrimitivesV5AbridgedHrmpChannel extends Struct { + readonly maxCapacity: u32; + readonly maxTotalSize: u32; + readonly maxMessageSize: u32; + readonly msgCount: u32; + readonly totalSize: u32; + readonly mqcHead: Option; + } + + /** @name PolkadotPrimitivesV5AbridgedHostConfiguration (424) */ + interface PolkadotPrimitivesV5AbridgedHostConfiguration extends Struct { + readonly maxCodeSize: u32; + readonly maxHeadDataSize: u32; + readonly maxUpwardQueueCount: u32; + readonly maxUpwardQueueSize: u32; + readonly maxUpwardMessageSize: u32; + readonly maxUpwardMessageNumPerCandidate: u32; + readonly hrmpMaxMessageNumPerCandidate: u32; + readonly validationUpgradeCooldown: u32; + readonly validationUpgradeDelay: u32; + readonly asyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams; + } + + /** @name PolkadotPrimitivesVstagingAsyncBackingParams (425) */ + interface PolkadotPrimitivesVstagingAsyncBackingParams extends Struct { + readonly maxCandidateDepth: u32; + readonly allowedAncestryLen: u32; + } + + /** @name PolkadotCorePrimitivesOutboundHrmpMessage (431) */ + interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct { + readonly recipient: u32; + readonly data: Bytes; + } + + /** @name CumulusPalletParachainSystemCodeUpgradeAuthorization (432) */ + interface CumulusPalletParachainSystemCodeUpgradeAuthorization extends Struct { + readonly codeHash: H256; + readonly checkVersion: bool; + } + + /** @name CumulusPalletParachainSystemError (433) */ + interface CumulusPalletParachainSystemError extends Enum { + readonly isOverlappingUpgrades: boolean; + readonly isProhibitedByPolkadot: boolean; + readonly isTooBig: boolean; + readonly isValidationDataNotAvailable: boolean; + readonly isHostConfigurationNotAvailable: boolean; + readonly isNotScheduled: boolean; + readonly isNothingAuthorized: boolean; + readonly isUnauthorized: boolean; + readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized'; + } + + /** @name PalletCollatorSelectionError (435) */ + interface PalletCollatorSelectionError extends Enum { + readonly isTooManyCandidates: boolean; + readonly isUnknown: boolean; + readonly isPermission: boolean; + readonly isAlreadyHoldingLicense: boolean; + readonly isNoLicense: boolean; + readonly isAlreadyCandidate: boolean; + readonly isNotCandidate: boolean; + readonly isTooManyInvulnerables: boolean; + readonly isTooFewInvulnerables: boolean; + readonly isAlreadyInvulnerable: boolean; + readonly isNotInvulnerable: boolean; + readonly isNoAssociatedValidatorId: boolean; + readonly isValidatorNotRegistered: boolean; + readonly type: 'TooManyCandidates' | 'Unknown' | 'Permission' | 'AlreadyHoldingLicense' | 'NoLicense' | 'AlreadyCandidate' | 'NotCandidate' | 'TooManyInvulnerables' | 'TooFewInvulnerables' | 'AlreadyInvulnerable' | 'NotInvulnerable' | 'NoAssociatedValidatorId' | 'ValidatorNotRegistered'; + } + + /** @name SpCoreCryptoKeyTypeId (439) */ + interface SpCoreCryptoKeyTypeId extends U8aFixed {} + + /** @name PalletSessionError (440) */ + interface PalletSessionError extends Enum { + readonly isInvalidProof: boolean; + readonly isNoAssociatedValidatorId: boolean; + readonly isDuplicatedKey: boolean; + readonly isNoKeys: boolean; + readonly isNoAccount: boolean; + readonly type: 'InvalidProof' | 'NoAssociatedValidatorId' | 'DuplicatedKey' | 'NoKeys' | 'NoAccount'; + } + + /** @name PalletBalancesBalanceLock (446) */ + interface PalletBalancesBalanceLock extends Struct { + readonly id: U8aFixed; + readonly amount: u128; + readonly reasons: PalletBalancesReasons; + } + + /** @name PalletBalancesReasons (447) */ + interface PalletBalancesReasons extends Enum { + readonly isFee: boolean; + readonly isMisc: boolean; + readonly isAll: boolean; + readonly type: 'Fee' | 'Misc' | 'All'; + } + + /** @name PalletBalancesReserveData (450) */ + interface PalletBalancesReserveData extends Struct { + readonly id: U8aFixed; + readonly amount: u128; + } + + /** @name OpalRuntimeRuntimeHoldReason (454) */ + interface OpalRuntimeRuntimeHoldReason extends Enum { + readonly isCollatorSelection: boolean; + readonly asCollatorSelection: PalletCollatorSelectionHoldReason; + readonly type: 'CollatorSelection'; + } + + /** @name PalletCollatorSelectionHoldReason (455) */ + interface PalletCollatorSelectionHoldReason extends Enum { + readonly isLicenseBond: boolean; + readonly type: 'LicenseBond'; + } + + /** @name PalletBalancesIdAmount (458) */ + interface PalletBalancesIdAmount extends Struct { + readonly id: U8aFixed; + readonly amount: u128; + } + + /** @name PalletBalancesError (460) */ + interface PalletBalancesError extends Enum { + readonly isVestingBalance: boolean; + readonly isLiquidityRestrictions: boolean; + readonly isInsufficientBalance: boolean; + readonly isExistentialDeposit: boolean; + readonly isExpendability: boolean; + readonly isExistingVestingSchedule: boolean; + readonly isDeadAccount: boolean; + readonly isTooManyReserves: boolean; + readonly isTooManyHolds: boolean; + readonly isTooManyFreezes: boolean; + readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'Expendability' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves' | 'TooManyHolds' | 'TooManyFreezes'; + } + + /** @name PalletTransactionPaymentReleases (462) */ + interface PalletTransactionPaymentReleases extends Enum { + readonly isV1Ancient: boolean; + readonly isV2: boolean; + readonly type: 'V1Ancient' | 'V2'; + } + + /** @name PalletTreasuryProposal (463) */ + interface PalletTreasuryProposal extends Struct { + readonly proposer: AccountId32; + readonly value: u128; + readonly beneficiary: AccountId32; + readonly bond: u128; + } + + /** @name FrameSupportPalletId (466) */ + interface FrameSupportPalletId extends U8aFixed {} + + /** @name PalletTreasuryError (467) */ + interface PalletTreasuryError extends Enum { + readonly isInsufficientProposersBalance: boolean; + readonly isInvalidIndex: boolean; + readonly isTooManyApprovals: boolean; + readonly isInsufficientPermission: boolean; + readonly isProposalNotApproved: boolean; + readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved'; + } + + /** @name PalletSudoError (468) */ + interface PalletSudoError extends Enum { + readonly isRequireSudo: boolean; + readonly type: 'RequireSudo'; + } + + /** @name OrmlVestingModuleError (470) */ + interface OrmlVestingModuleError extends Enum { + readonly isZeroVestingPeriod: boolean; + readonly isZeroVestingPeriodCount: boolean; + readonly isInsufficientBalanceToLock: boolean; + readonly isTooManyVestingSchedules: boolean; + readonly isAmountLow: boolean; + readonly isMaxVestingSchedulesExceeded: boolean; + readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded'; + } + + /** @name OrmlXtokensModuleError (471) */ + interface OrmlXtokensModuleError extends Enum { + readonly isAssetHasNoReserve: boolean; + readonly isNotCrossChainTransfer: boolean; + readonly isInvalidDest: boolean; + readonly isNotCrossChainTransferableCurrency: boolean; + readonly isUnweighableMessage: boolean; + readonly isXcmExecutionFailed: boolean; + readonly isCannotReanchor: boolean; + readonly isInvalidAncestry: boolean; + readonly isInvalidAsset: boolean; + readonly isDestinationNotInvertible: boolean; + readonly isBadVersion: boolean; + readonly isDistinctReserveForAssetAndFee: boolean; + readonly isZeroFee: boolean; + readonly isZeroAmount: boolean; + readonly isTooManyAssetsBeingSent: boolean; + readonly isAssetIndexNonExistent: boolean; + readonly isFeeNotEnough: boolean; + readonly isNotSupportedMultiLocation: boolean; + readonly isMinXcmFeeNotDefined: boolean; + readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined'; + } + + /** @name OrmlTokensBalanceLock (474) */ + interface OrmlTokensBalanceLock extends Struct { + readonly id: U8aFixed; + readonly amount: u128; + } + + /** @name OrmlTokensAccountData (476) */ + interface OrmlTokensAccountData extends Struct { + readonly free: u128; + readonly reserved: u128; + readonly frozen: u128; + } + + /** @name OrmlTokensReserveData (478) */ + interface OrmlTokensReserveData extends Struct { + readonly id: Null; + readonly amount: u128; + } + + /** @name OrmlTokensModuleError (480) */ + interface OrmlTokensModuleError extends Enum { + readonly isBalanceTooLow: boolean; + readonly isAmountIntoBalanceFailed: boolean; + readonly isLiquidityRestrictions: boolean; + readonly isMaxLocksExceeded: boolean; + readonly isKeepAlive: boolean; + readonly isExistentialDeposit: boolean; + readonly isDeadAccount: boolean; + readonly isTooManyReserves: boolean; + readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves'; + } + + /** @name PalletIdentityRegistrarInfo (485) */ + interface PalletIdentityRegistrarInfo extends Struct { + readonly account: AccountId32; + readonly fee: u128; + readonly fields: PalletIdentityBitFlags; + } + + /** @name PalletIdentityError (487) */ + interface PalletIdentityError extends Enum { + readonly isTooManySubAccounts: boolean; + readonly isNotFound: boolean; + readonly isNotNamed: boolean; + readonly isEmptyIndex: boolean; + readonly isFeeChanged: boolean; + readonly isNoIdentity: boolean; + readonly isStickyJudgement: boolean; + readonly isJudgementGiven: boolean; + readonly isInvalidJudgement: boolean; + readonly isInvalidIndex: boolean; + readonly isInvalidTarget: boolean; + readonly isTooManyFields: boolean; + readonly isTooManyRegistrars: boolean; + readonly isAlreadyClaimed: boolean; + readonly isNotSub: boolean; + readonly isNotOwned: boolean; + readonly isJudgementForDifferentIdentity: boolean; + readonly isJudgementPaymentFailed: boolean; + readonly type: 'TooManySubAccounts' | 'NotFound' | 'NotNamed' | 'EmptyIndex' | 'FeeChanged' | 'NoIdentity' | 'StickyJudgement' | 'JudgementGiven' | 'InvalidJudgement' | 'InvalidIndex' | 'InvalidTarget' | 'TooManyFields' | 'TooManyRegistrars' | 'AlreadyClaimed' | 'NotSub' | 'NotOwned' | 'JudgementForDifferentIdentity' | 'JudgementPaymentFailed'; + } + + /** @name PalletPreimageRequestStatus (488) */ + interface PalletPreimageRequestStatus extends Enum { + readonly isUnrequested: boolean; + readonly asUnrequested: { + readonly deposit: ITuple<[AccountId32, u128]>; + readonly len: u32; + } & Struct; + readonly isRequested: boolean; + readonly asRequested: { + readonly deposit: Option>; + readonly count: u32; + readonly len: Option; + } & Struct; + readonly type: 'Unrequested' | 'Requested'; + } + + /** @name PalletPreimageError (493) */ + interface PalletPreimageError extends Enum { + readonly isTooBig: boolean; + readonly isAlreadyNoted: boolean; + readonly isNotAuthorized: boolean; + readonly isNotNoted: boolean; + readonly isRequested: boolean; + readonly isNotRequested: boolean; + readonly type: 'TooBig' | 'AlreadyNoted' | 'NotAuthorized' | 'NotNoted' | 'Requested' | 'NotRequested'; + } + + /** @name PalletDemocracyReferendumInfo (499) */ + interface PalletDemocracyReferendumInfo extends Enum { + readonly isOngoing: boolean; + readonly asOngoing: PalletDemocracyReferendumStatus; + readonly isFinished: boolean; + readonly asFinished: { + readonly approved: bool; + readonly end: u32; + } & Struct; + readonly type: 'Ongoing' | 'Finished'; + } + + /** @name PalletDemocracyReferendumStatus (500) */ + interface PalletDemocracyReferendumStatus extends Struct { + readonly end: u32; + readonly proposal: FrameSupportPreimagesBounded; + readonly threshold: PalletDemocracyVoteThreshold; + readonly delay: u32; + readonly tally: PalletDemocracyTally; + } + + /** @name PalletDemocracyTally (501) */ + interface PalletDemocracyTally extends Struct { + readonly ayes: u128; + readonly nays: u128; + readonly turnout: u128; + } + + /** @name PalletDemocracyVoteVoting (502) */ + interface PalletDemocracyVoteVoting extends Enum { + readonly isDirect: boolean; + readonly asDirect: { + readonly votes: Vec>; + readonly delegations: PalletDemocracyDelegations; + readonly prior: PalletDemocracyVotePriorLock; + } & Struct; + readonly isDelegating: boolean; + readonly asDelegating: { + readonly balance: u128; + readonly target: AccountId32; + readonly conviction: PalletDemocracyConviction; + readonly delegations: PalletDemocracyDelegations; + readonly prior: PalletDemocracyVotePriorLock; + } & Struct; + readonly type: 'Direct' | 'Delegating'; + } + + /** @name PalletDemocracyDelegations (506) */ + interface PalletDemocracyDelegations extends Struct { + readonly votes: u128; + readonly capital: u128; + } + + /** @name PalletDemocracyVotePriorLock (507) */ + interface PalletDemocracyVotePriorLock extends ITuple<[u32, u128]> {} + + /** @name PalletDemocracyError (510) */ + interface PalletDemocracyError extends Enum { + readonly isValueLow: boolean; + readonly isProposalMissing: boolean; + readonly isAlreadyCanceled: boolean; + readonly isDuplicateProposal: boolean; + readonly isProposalBlacklisted: boolean; + readonly isNotSimpleMajority: boolean; + readonly isInvalidHash: boolean; + readonly isNoProposal: boolean; + readonly isAlreadyVetoed: boolean; + readonly isReferendumInvalid: boolean; + readonly isNoneWaiting: boolean; + readonly isNotVoter: boolean; + readonly isNoPermission: boolean; + readonly isAlreadyDelegating: boolean; + readonly isInsufficientFunds: boolean; + readonly isNotDelegating: boolean; + readonly isVotesExist: boolean; + readonly isInstantNotAllowed: boolean; + readonly isNonsense: boolean; + readonly isWrongUpperBound: boolean; + readonly isMaxVotesReached: boolean; + readonly isTooMany: boolean; + readonly isVotingPeriodLow: boolean; + readonly isPreimageNotExist: boolean; + readonly type: 'ValueLow' | 'ProposalMissing' | 'AlreadyCanceled' | 'DuplicateProposal' | 'ProposalBlacklisted' | 'NotSimpleMajority' | 'InvalidHash' | 'NoProposal' | 'AlreadyVetoed' | 'ReferendumInvalid' | 'NoneWaiting' | 'NotVoter' | 'NoPermission' | 'AlreadyDelegating' | 'InsufficientFunds' | 'NotDelegating' | 'VotesExist' | 'InstantNotAllowed' | 'Nonsense' | 'WrongUpperBound' | 'MaxVotesReached' | 'TooMany' | 'VotingPeriodLow' | 'PreimageNotExist'; + } + + /** @name PalletCollectiveVotes (512) */ + interface PalletCollectiveVotes extends Struct { + readonly index: u32; + readonly threshold: u32; + readonly ayes: Vec; + readonly nays: Vec; + readonly end: u32; + } + + /** @name PalletCollectiveError (513) */ + interface PalletCollectiveError extends Enum { + readonly isNotMember: boolean; + readonly isDuplicateProposal: boolean; + readonly isProposalMissing: boolean; + readonly isWrongIndex: boolean; + readonly isDuplicateVote: boolean; + readonly isAlreadyInitialized: boolean; + readonly isTooEarly: boolean; + readonly isTooManyProposals: boolean; + readonly isWrongProposalWeight: boolean; + readonly isWrongProposalLength: boolean; + readonly isPrimeAccountNotMember: boolean; + readonly type: 'NotMember' | 'DuplicateProposal' | 'ProposalMissing' | 'WrongIndex' | 'DuplicateVote' | 'AlreadyInitialized' | 'TooEarly' | 'TooManyProposals' | 'WrongProposalWeight' | 'WrongProposalLength' | 'PrimeAccountNotMember'; + } + + /** @name PalletMembershipError (517) */ + interface PalletMembershipError extends Enum { + readonly isAlreadyMember: boolean; + readonly isNotMember: boolean; + readonly isTooManyMembers: boolean; + readonly type: 'AlreadyMember' | 'NotMember' | 'TooManyMembers'; + } + + /** @name PalletRankedCollectiveMemberRecord (520) */ + interface PalletRankedCollectiveMemberRecord extends Struct { + readonly rank: u16; + } + + /** @name PalletRankedCollectiveError (525) */ + interface PalletRankedCollectiveError extends Enum { + readonly isAlreadyMember: boolean; + readonly isNotMember: boolean; + readonly isNotPolling: boolean; + readonly isOngoing: boolean; + readonly isNoneRemaining: boolean; + readonly isCorruption: boolean; + readonly isRankTooLow: boolean; + readonly isInvalidWitness: boolean; + readonly isNoPermission: boolean; + readonly type: 'AlreadyMember' | 'NotMember' | 'NotPolling' | 'Ongoing' | 'NoneRemaining' | 'Corruption' | 'RankTooLow' | 'InvalidWitness' | 'NoPermission'; + } + + /** @name PalletReferendaReferendumInfo (526) */ + interface PalletReferendaReferendumInfo extends Enum { + readonly isOngoing: boolean; + readonly asOngoing: PalletReferendaReferendumStatus; + readonly isApproved: boolean; + readonly asApproved: ITuple<[u32, Option, Option]>; + readonly isRejected: boolean; + readonly asRejected: ITuple<[u32, Option, Option]>; + readonly isCancelled: boolean; + readonly asCancelled: ITuple<[u32, Option, Option]>; + readonly isTimedOut: boolean; + readonly asTimedOut: ITuple<[u32, Option, Option]>; + readonly isKilled: boolean; + readonly asKilled: u32; + readonly type: 'Ongoing' | 'Approved' | 'Rejected' | 'Cancelled' | 'TimedOut' | 'Killed'; + } + + /** @name PalletReferendaReferendumStatus (527) */ + interface PalletReferendaReferendumStatus extends Struct { + readonly track: u16; + readonly origin: OpalRuntimeOriginCaller; + readonly proposal: FrameSupportPreimagesBounded; + readonly enactment: FrameSupportScheduleDispatchTime; + readonly submitted: u32; + readonly submissionDeposit: PalletReferendaDeposit; + readonly decisionDeposit: Option; + readonly deciding: Option; + readonly tally: PalletRankedCollectiveTally; + readonly inQueue: bool; + readonly alarm: Option]>>; + } + + /** @name PalletReferendaDeposit (528) */ + interface PalletReferendaDeposit extends Struct { + readonly who: AccountId32; + readonly amount: u128; + } + + /** @name PalletReferendaDecidingStatus (531) */ + interface PalletReferendaDecidingStatus extends Struct { + readonly since: u32; + readonly confirming: Option; + } + + /** @name PalletReferendaTrackInfo (537) */ + interface PalletReferendaTrackInfo extends Struct { + readonly name: Text; + readonly maxDeciding: u32; + readonly decisionDeposit: u128; + readonly preparePeriod: u32; + readonly decisionPeriod: u32; + readonly confirmPeriod: u32; + readonly minEnactmentPeriod: u32; + readonly minApproval: PalletReferendaCurve; + readonly minSupport: PalletReferendaCurve; + } + + /** @name PalletReferendaCurve (538) */ + interface PalletReferendaCurve extends Enum { + readonly isLinearDecreasing: boolean; + readonly asLinearDecreasing: { + readonly length: Perbill; + readonly floor: Perbill; + readonly ceil: Perbill; + } & Struct; + readonly isSteppedDecreasing: boolean; + readonly asSteppedDecreasing: { + readonly begin: Perbill; + readonly end: Perbill; + readonly step: Perbill; + readonly period: Perbill; + } & Struct; + readonly isReciprocal: boolean; + readonly asReciprocal: { + readonly factor: i64; + readonly xOffset: i64; + readonly yOffset: i64; + } & Struct; + readonly type: 'LinearDecreasing' | 'SteppedDecreasing' | 'Reciprocal'; + } + + /** @name PalletReferendaError (541) */ + interface PalletReferendaError extends Enum { + readonly isNotOngoing: boolean; + readonly isHasDeposit: boolean; + readonly isBadTrack: boolean; + readonly isFull: boolean; + readonly isQueueEmpty: boolean; + readonly isBadReferendum: boolean; + readonly isNothingToDo: boolean; + readonly isNoTrack: boolean; + readonly isUnfinished: boolean; + readonly isNoPermission: boolean; + readonly isNoDeposit: boolean; + readonly isBadStatus: boolean; + readonly isPreimageNotExist: boolean; + readonly type: 'NotOngoing' | 'HasDeposit' | 'BadTrack' | 'Full' | 'QueueEmpty' | 'BadReferendum' | 'NothingToDo' | 'NoTrack' | 'Unfinished' | 'NoPermission' | 'NoDeposit' | 'BadStatus' | 'PreimageNotExist'; + } + + /** @name PalletSchedulerScheduled (544) */ + interface PalletSchedulerScheduled extends Struct { + readonly maybeId: Option; + readonly priority: u8; + readonly call: FrameSupportPreimagesBounded; + readonly maybePeriodic: Option>; + readonly origin: OpalRuntimeOriginCaller; + } + + /** @name PalletSchedulerError (546) */ + interface PalletSchedulerError extends Enum { + readonly isFailedToSchedule: boolean; + readonly isNotFound: boolean; + readonly isTargetBlockNumberInPast: boolean; + readonly isRescheduleNoChange: boolean; + readonly isNamed: boolean; + readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange' | 'Named'; + } + + /** @name CumulusPalletXcmpQueueInboundChannelDetails (548) */ + interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct { + readonly sender: u32; + readonly state: CumulusPalletXcmpQueueInboundState; + readonly messageMetadata: Vec>; + } + + /** @name CumulusPalletXcmpQueueInboundState (549) */ + interface CumulusPalletXcmpQueueInboundState extends Enum { + readonly isOk: boolean; + readonly isSuspended: boolean; + readonly type: 'Ok' | 'Suspended'; + } + + /** @name PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat (552) */ + interface PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat extends Enum { + readonly isConcatenatedVersionedXcm: boolean; + readonly isConcatenatedEncodedBlob: boolean; + readonly isSignals: boolean; + readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals'; + } + + /** @name CumulusPalletXcmpQueueOutboundChannelDetails (555) */ + interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct { + readonly recipient: u32; + readonly state: CumulusPalletXcmpQueueOutboundState; + readonly signalsExist: bool; + readonly firstIndex: u16; + readonly lastIndex: u16; + } + + /** @name CumulusPalletXcmpQueueOutboundState (556) */ + interface CumulusPalletXcmpQueueOutboundState extends Enum { + readonly isOk: boolean; + readonly isSuspended: boolean; + readonly type: 'Ok' | 'Suspended'; + } + + /** @name CumulusPalletXcmpQueueQueueConfigData (558) */ + interface CumulusPalletXcmpQueueQueueConfigData extends Struct { + readonly suspendThreshold: u32; + readonly dropThreshold: u32; + readonly resumeThreshold: u32; + readonly thresholdWeight: SpWeightsWeightV2Weight; + readonly weightRestrictDecay: SpWeightsWeightV2Weight; + readonly xcmpMaxIndividualWeight: SpWeightsWeightV2Weight; + } + + /** @name CumulusPalletXcmpQueueError (560) */ + interface CumulusPalletXcmpQueueError extends Enum { + readonly isFailedToSend: boolean; + readonly isBadXcmOrigin: boolean; + readonly isBadXcm: boolean; + readonly isBadOverweightIndex: boolean; + readonly isWeightOverLimit: boolean; + readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit'; + } + + /** @name PalletXcmQueryStatus (561) */ + interface PalletXcmQueryStatus extends Enum { + readonly isPending: boolean; + readonly asPending: { + readonly responder: StagingXcmVersionedMultiLocation; + readonly maybeMatchQuerier: Option; + readonly maybeNotify: Option>; + readonly timeout: u32; + } & Struct; + readonly isVersionNotifier: boolean; + readonly asVersionNotifier: { + readonly origin: StagingXcmVersionedMultiLocation; + readonly isActive: bool; + } & Struct; + readonly isReady: boolean; + readonly asReady: { + readonly response: StagingXcmVersionedResponse; + readonly at: u32; + } & Struct; + readonly type: 'Pending' | 'VersionNotifier' | 'Ready'; + } + + /** @name StagingXcmVersionedResponse (565) */ + interface StagingXcmVersionedResponse extends Enum { + readonly isV2: boolean; + readonly asV2: StagingXcmV2Response; + readonly isV3: boolean; + readonly asV3: StagingXcmV3Response; + readonly type: 'V2' | 'V3'; + } + + /** @name PalletXcmVersionMigrationStage (571) */ + interface PalletXcmVersionMigrationStage extends Enum { + readonly isMigrateSupportedVersion: boolean; + readonly isMigrateVersionNotifiers: boolean; + readonly isNotifyCurrentTargets: boolean; + readonly asNotifyCurrentTargets: Option; + readonly isMigrateAndNotifyOldTargets: boolean; + readonly type: 'MigrateSupportedVersion' | 'MigrateVersionNotifiers' | 'NotifyCurrentTargets' | 'MigrateAndNotifyOldTargets'; + } + + /** @name StagingXcmVersionedAssetId (574) */ + interface StagingXcmVersionedAssetId extends Enum { + readonly isV3: boolean; + readonly asV3: StagingXcmV3MultiassetAssetId; + readonly type: 'V3'; + } + + /** @name PalletXcmRemoteLockedFungibleRecord (575) */ + interface PalletXcmRemoteLockedFungibleRecord extends Struct { + readonly amount: u128; + readonly owner: StagingXcmVersionedMultiLocation; + readonly locker: StagingXcmVersionedMultiLocation; + readonly consumers: Vec>; + } + + /** @name PalletXcmError (582) */ + interface PalletXcmError extends Enum { + readonly isUnreachable: boolean; + readonly isSendFailure: boolean; + readonly isFiltered: boolean; + readonly isUnweighableMessage: boolean; + readonly isDestinationNotInvertible: boolean; + readonly isEmpty: boolean; + readonly isCannotReanchor: boolean; + readonly isTooManyAssets: boolean; + readonly isInvalidOrigin: boolean; + readonly isBadVersion: boolean; + readonly isBadLocation: boolean; + readonly isNoSubscription: boolean; + readonly isAlreadySubscribed: boolean; + readonly isInvalidAsset: boolean; + readonly isLowBalance: boolean; + readonly isTooManyLocks: boolean; + readonly isAccountNotSovereign: boolean; + readonly isFeesNotMet: boolean; + readonly isLockNotFound: boolean; + readonly isInUse: boolean; + readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed' | 'InvalidAsset' | 'LowBalance' | 'TooManyLocks' | 'AccountNotSovereign' | 'FeesNotMet' | 'LockNotFound' | 'InUse'; + } + + /** @name CumulusPalletXcmError (583) */ + type CumulusPalletXcmError = Null; + + /** @name CumulusPalletDmpQueueConfigData (584) */ + interface CumulusPalletDmpQueueConfigData extends Struct { + readonly maxIndividual: SpWeightsWeightV2Weight; + } + + /** @name CumulusPalletDmpQueuePageIndexData (585) */ + interface CumulusPalletDmpQueuePageIndexData extends Struct { + readonly beginUsed: u32; + readonly endUsed: u32; + readonly overweightCount: u64; + } + + /** @name CumulusPalletDmpQueueError (588) */ + interface CumulusPalletDmpQueueError extends Enum { + readonly isUnknown: boolean; + readonly isOverLimit: boolean; + readonly type: 'Unknown' | 'OverLimit'; + } + + /** @name PalletUniqueError (592) */ + interface PalletUniqueError extends Enum { + readonly isCollectionDecimalPointLimitExceeded: boolean; + readonly isEmptyArgument: boolean; + readonly isRepartitionCalledOnNonRefungibleCollection: boolean; + readonly type: 'CollectionDecimalPointLimitExceeded' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection'; + } + + /** @name PalletConfigurationError (593) */ + interface PalletConfigurationError extends Enum { + readonly isInconsistentConfiguration: boolean; + readonly type: 'InconsistentConfiguration'; + } + + /** @name UpDataStructsCollection (594) */ + interface UpDataStructsCollection extends Struct { + readonly owner: AccountId32; + readonly mode: UpDataStructsCollectionMode; + readonly name: Vec; + readonly description: Vec; + readonly tokenPrefix: Bytes; + readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; + readonly limits: UpDataStructsCollectionLimits; + readonly permissions: UpDataStructsCollectionPermissions; + readonly flags: U8aFixed; + } + + /** @name UpDataStructsSponsorshipStateAccountId32 (595) */ + interface UpDataStructsSponsorshipStateAccountId32 extends Enum { + readonly isDisabled: boolean; + readonly isUnconfirmed: boolean; + readonly asUnconfirmed: AccountId32; + readonly isConfirmed: boolean; + readonly asConfirmed: AccountId32; + readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; + } + + /** @name UpDataStructsProperties (596) */ + interface UpDataStructsProperties extends Struct { + readonly map: UpDataStructsPropertiesMapBoundedVec; + readonly consumedSpace: u32; + readonly reserved: u32; + } + + /** @name UpDataStructsPropertiesMapBoundedVec (597) */ + interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap {} + + /** @name UpDataStructsPropertiesMapPropertyPermission (602) */ + interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap {} + + /** @name UpDataStructsCollectionStats (609) */ + interface UpDataStructsCollectionStats extends Struct { + readonly created: u32; + readonly destroyed: u32; + readonly alive: u32; + } + + /** @name UpDataStructsTokenChild (610) */ + interface UpDataStructsTokenChild extends Struct { + readonly token: u32; + readonly collection: u32; + } + + /** @name PhantomTypeUpDataStructs (611) */ + interface PhantomTypeUpDataStructs extends Vec> {} + + /** @name UpDataStructsTokenData (613) */ + interface UpDataStructsTokenData extends Struct { + readonly properties: Vec; + readonly owner: Option; + readonly pieces: u128; + } + + /** @name UpDataStructsRpcCollection (614) */ + interface UpDataStructsRpcCollection extends Struct { + readonly owner: AccountId32; + readonly mode: UpDataStructsCollectionMode; + readonly name: Vec; + readonly description: Vec; + readonly tokenPrefix: Bytes; + readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; + readonly limits: UpDataStructsCollectionLimits; + readonly permissions: UpDataStructsCollectionPermissions; + readonly tokenPropertyPermissions: Vec; + readonly properties: Vec; + readonly readOnly: bool; + readonly flags: UpDataStructsRpcCollectionFlags; + } + + /** @name UpDataStructsRpcCollectionFlags (615) */ + interface UpDataStructsRpcCollectionFlags extends Struct { + readonly foreign: bool; + readonly erc721metadata: bool; + } + + /** @name UpPovEstimateRpcPovInfo (616) */ + interface UpPovEstimateRpcPovInfo extends Struct { + readonly proofSize: u64; + readonly compactProofSize: u64; + readonly compressedProofSize: u64; + readonly results: Vec, SpRuntimeTransactionValidityTransactionValidityError>>; + readonly keyValues: Vec; + } + + /** @name SpRuntimeTransactionValidityTransactionValidityError (619) */ + interface SpRuntimeTransactionValidityTransactionValidityError extends Enum { + readonly isInvalid: boolean; + readonly asInvalid: SpRuntimeTransactionValidityInvalidTransaction; + readonly isUnknown: boolean; + readonly asUnknown: SpRuntimeTransactionValidityUnknownTransaction; + readonly type: 'Invalid' | 'Unknown'; + } + + /** @name SpRuntimeTransactionValidityInvalidTransaction (620) */ + interface SpRuntimeTransactionValidityInvalidTransaction extends Enum { + readonly isCall: boolean; + readonly isPayment: boolean; + readonly isFuture: boolean; + readonly isStale: boolean; + readonly isBadProof: boolean; + readonly isAncientBirthBlock: boolean; + readonly isExhaustsResources: boolean; + readonly isCustom: boolean; + readonly asCustom: u8; + readonly isBadMandatory: boolean; + readonly isMandatoryValidation: boolean; + readonly isBadSigner: boolean; + readonly type: 'Call' | 'Payment' | 'Future' | 'Stale' | 'BadProof' | 'AncientBirthBlock' | 'ExhaustsResources' | 'Custom' | 'BadMandatory' | 'MandatoryValidation' | 'BadSigner'; + } + + /** @name SpRuntimeTransactionValidityUnknownTransaction (621) */ + interface SpRuntimeTransactionValidityUnknownTransaction extends Enum { + readonly isCannotLookup: boolean; + readonly isNoUnsignedValidator: boolean; + readonly isCustom: boolean; + readonly asCustom: u8; + readonly type: 'CannotLookup' | 'NoUnsignedValidator' | 'Custom'; + } + + /** @name UpPovEstimateRpcTrieKeyValue (623) */ + interface UpPovEstimateRpcTrieKeyValue extends Struct { + readonly key: Bytes; + readonly value: Bytes; + } + + /** @name PalletCommonError (625) */ + interface PalletCommonError extends Enum { + readonly isCollectionNotFound: boolean; + readonly isMustBeTokenOwner: boolean; + readonly isNoPermission: boolean; + readonly isCantDestroyNotEmptyCollection: boolean; + readonly isPublicMintingNotAllowed: boolean; + readonly isAddressNotInAllowlist: boolean; + readonly isCollectionNameLimitExceeded: boolean; + readonly isCollectionDescriptionLimitExceeded: boolean; + readonly isCollectionTokenPrefixLimitExceeded: boolean; + readonly isTotalCollectionsLimitExceeded: boolean; + readonly isCollectionAdminCountExceeded: boolean; + readonly isCollectionLimitBoundsExceeded: boolean; + readonly isOwnerPermissionsCantBeReverted: boolean; + readonly isTransferNotAllowed: boolean; + readonly isAccountTokenLimitExceeded: boolean; + readonly isCollectionTokenLimitExceeded: boolean; + readonly isMetadataFlagFrozen: boolean; + readonly isTokenNotFound: boolean; + readonly isTokenValueTooLow: boolean; + readonly isApprovedValueTooLow: boolean; + readonly isCantApproveMoreThanOwned: boolean; + readonly isAddressIsNotEthMirror: boolean; + readonly isAddressIsZero: boolean; + readonly isUnsupportedOperation: boolean; + readonly isNotSufficientFounds: boolean; + readonly isUserIsNotAllowedToNest: boolean; + readonly isSourceCollectionIsNotAllowedToNest: boolean; + readonly isCollectionFieldSizeExceeded: boolean; + readonly isNoSpaceForProperty: boolean; + readonly isPropertyLimitReached: boolean; + readonly isPropertyKeyIsTooLong: boolean; + readonly isInvalidCharacterInPropertyKey: boolean; + readonly isEmptyPropertyKey: boolean; + readonly isCollectionIsExternal: boolean; + readonly isCollectionIsInternal: boolean; + readonly isConfirmSponsorshipFail: boolean; + readonly isUserIsNotCollectionAdmin: boolean; + readonly isFungibleItemsHaveNoId: boolean; + readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsNotEthMirror' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal' | 'ConfirmSponsorshipFail' | 'UserIsNotCollectionAdmin' | 'FungibleItemsHaveNoId'; + } + + /** @name PalletFungibleError (627) */ + interface PalletFungibleError extends Enum { + readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean; + readonly isFungibleItemsDontHaveData: boolean; + readonly isFungibleDisallowsNesting: boolean; + readonly isSettingPropertiesNotAllowed: boolean; + readonly isSettingAllowanceForAllNotAllowed: boolean; + readonly isFungibleTokensAreAlwaysValid: boolean; + readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingAllowanceForAllNotAllowed' | 'FungibleTokensAreAlwaysValid'; + } + + /** @name PalletRefungibleError (632) */ + interface PalletRefungibleError extends Enum { + readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean; + readonly isWrongRefungiblePieces: boolean; + readonly isRepartitionWhileNotOwningAllPieces: boolean; + readonly isRefungibleDisallowsNesting: boolean; + readonly isSettingPropertiesNotAllowed: boolean; + readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; + } + + /** @name PalletNonfungibleItemData (633) */ + interface PalletNonfungibleItemData extends Struct { + readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; + } + + /** @name UpDataStructsPropertyScope (635) */ + interface UpDataStructsPropertyScope extends Enum { + readonly isNone: boolean; + readonly isRmrk: boolean; + readonly type: 'None' | 'Rmrk'; + } + + /** @name PalletNonfungibleError (638) */ + interface PalletNonfungibleError extends Enum { + readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean; + readonly isNonfungibleItemsHaveNoAmount: boolean; + readonly isCantBurnNftWithChildren: boolean; + readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren'; + } + + /** @name PalletStructureError (639) */ + interface PalletStructureError extends Enum { + readonly isOuroborosDetected: boolean; + readonly isDepthLimit: boolean; + readonly isBreadthLimit: boolean; + readonly isTokenNotFound: boolean; + readonly isCantNestTokenUnderCollection: boolean; + readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound' | 'CantNestTokenUnderCollection'; + } + + /** @name PalletAppPromotionError (644) */ + interface PalletAppPromotionError extends Enum { + readonly isAdminNotSet: boolean; + readonly isNoPermission: boolean; + readonly isNotSufficientFunds: boolean; + readonly isPendingForBlockOverflow: boolean; + readonly isSponsorNotSet: boolean; + readonly isInsufficientStakedBalance: boolean; + readonly isInconsistencyState: boolean; + readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'InsufficientStakedBalance' | 'InconsistencyState'; + } + + /** @name PalletForeignAssetsModuleError (645) */ + interface PalletForeignAssetsModuleError extends Enum { + readonly isBadLocation: boolean; + readonly isMultiLocationExisted: boolean; + readonly isAssetIdNotExists: boolean; + readonly isAssetIdExisted: boolean; + readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted'; + } + + /** @name PalletEvmCodeMetadata (646) */ + interface PalletEvmCodeMetadata extends Struct { + readonly size_: u64; + readonly hash_: H256; + } + + /** @name PalletEvmError (648) */ + interface PalletEvmError extends Enum { + readonly isBalanceLow: boolean; + readonly isFeeOverflow: boolean; + readonly isPaymentOverflow: boolean; + readonly isWithdrawFailed: boolean; + readonly isGasPriceTooLow: boolean; + readonly isInvalidNonce: boolean; + readonly isGasLimitTooLow: boolean; + readonly isGasLimitTooHigh: boolean; + readonly isUndefined: boolean; + readonly isReentrancy: boolean; + readonly isTransactionMustComeFromEOA: boolean; + readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce' | 'GasLimitTooLow' | 'GasLimitTooHigh' | 'Undefined' | 'Reentrancy' | 'TransactionMustComeFromEOA'; + } + + /** @name FpRpcTransactionStatus (651) */ + interface FpRpcTransactionStatus extends Struct { + readonly transactionHash: H256; + readonly transactionIndex: u32; + readonly from: H160; + readonly to: Option; + readonly contractAddress: Option; + readonly logs: Vec; + readonly logsBloom: EthbloomBloom; + } + + /** @name EthbloomBloom (653) */ + interface EthbloomBloom extends U8aFixed {} + + /** @name EthereumReceiptReceiptV3 (655) */ + interface EthereumReceiptReceiptV3 extends Enum { + readonly isLegacy: boolean; + readonly asLegacy: EthereumReceiptEip658ReceiptData; + readonly isEip2930: boolean; + readonly asEip2930: EthereumReceiptEip658ReceiptData; + readonly isEip1559: boolean; + readonly asEip1559: EthereumReceiptEip658ReceiptData; + readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; + } + + /** @name EthereumReceiptEip658ReceiptData (656) */ + interface EthereumReceiptEip658ReceiptData extends Struct { + readonly statusCode: u8; + readonly usedGas: U256; + readonly logsBloom: EthbloomBloom; + readonly logs: Vec; + } + + /** @name EthereumBlock (657) */ + interface EthereumBlock extends Struct { + readonly header: EthereumHeader; + readonly transactions: Vec; + readonly ommers: Vec; + } + + /** @name EthereumHeader (658) */ + interface EthereumHeader extends Struct { + readonly parentHash: H256; + readonly ommersHash: H256; + readonly beneficiary: H160; + readonly stateRoot: H256; + readonly transactionsRoot: H256; + readonly receiptsRoot: H256; + readonly logsBloom: EthbloomBloom; + readonly difficulty: U256; + readonly number: U256; + readonly gasLimit: U256; + readonly gasUsed: U256; + readonly timestamp: u64; + readonly extraData: Bytes; + readonly mixHash: H256; + readonly nonce: EthereumTypesHashH64; + } + + /** @name EthereumTypesHashH64 (659) */ + interface EthereumTypesHashH64 extends U8aFixed {} + + /** @name PalletEthereumError (664) */ + interface PalletEthereumError extends Enum { + readonly isInvalidSignature: boolean; + readonly isPreLogExists: boolean; + readonly type: 'InvalidSignature' | 'PreLogExists'; + } + + /** @name PalletEvmCoderSubstrateError (665) */ + interface PalletEvmCoderSubstrateError extends Enum { + readonly isOutOfGas: boolean; + readonly isOutOfFund: boolean; + readonly type: 'OutOfGas' | 'OutOfFund'; + } + + /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (666) */ + interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum { + readonly isDisabled: boolean; + readonly isUnconfirmed: boolean; + readonly asUnconfirmed: PalletEvmAccountBasicCrossAccountIdRepr; + readonly isConfirmed: boolean; + readonly asConfirmed: PalletEvmAccountBasicCrossAccountIdRepr; + readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; + } + + /** @name PalletEvmContractHelpersSponsoringModeT (667) */ + interface PalletEvmContractHelpersSponsoringModeT extends Enum { + readonly isDisabled: boolean; + readonly isAllowlisted: boolean; + readonly isGenerous: boolean; + readonly type: 'Disabled' | 'Allowlisted' | 'Generous'; + } + + /** @name PalletEvmContractHelpersError (673) */ + interface PalletEvmContractHelpersError extends Enum { + readonly isNoPermission: boolean; + readonly isNoPendingSponsor: boolean; + readonly isTooManyMethodsHaveSponsoredLimit: boolean; + readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit'; + } + + /** @name PalletEvmMigrationError (674) */ + interface PalletEvmMigrationError extends Enum { + readonly isAccountNotEmpty: boolean; + readonly isAccountIsNotMigrating: boolean; + readonly isBadEvent: boolean; + readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating' | 'BadEvent'; + } + + /** @name PalletMaintenanceError (675) */ + type PalletMaintenanceError = Null; + + /** @name PalletUtilityError (676) */ + interface PalletUtilityError extends Enum { + readonly isTooManyCalls: boolean; + readonly type: 'TooManyCalls'; + } + + /** @name PalletTestUtilsError (677) */ + interface PalletTestUtilsError extends Enum { + readonly isTestPalletDisabled: boolean; + readonly isTriggerRollback: boolean; + readonly type: 'TestPalletDisabled' | 'TriggerRollback'; + } + + /** @name SpRuntimeMultiSignature (679) */ + interface SpRuntimeMultiSignature extends Enum { + readonly isEd25519: boolean; + readonly asEd25519: SpCoreEd25519Signature; + readonly isSr25519: boolean; + readonly asSr25519: SpCoreSr25519Signature; + readonly isEcdsa: boolean; + readonly asEcdsa: SpCoreEcdsaSignature; + readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa'; + } + + /** @name SpCoreEd25519Signature (680) */ + interface SpCoreEd25519Signature extends U8aFixed {} + + /** @name SpCoreSr25519Signature (682) */ + interface SpCoreSr25519Signature extends U8aFixed {} + + /** @name SpCoreEcdsaSignature (683) */ + interface SpCoreEcdsaSignature extends U8aFixed {} + + /** @name FrameSystemExtensionsCheckSpecVersion (686) */ + type FrameSystemExtensionsCheckSpecVersion = Null; + + /** @name FrameSystemExtensionsCheckTxVersion (687) */ + type FrameSystemExtensionsCheckTxVersion = Null; + + /** @name FrameSystemExtensionsCheckGenesis (688) */ + type FrameSystemExtensionsCheckGenesis = Null; + + /** @name FrameSystemExtensionsCheckNonce (691) */ + interface FrameSystemExtensionsCheckNonce extends Compact {} + + /** @name FrameSystemExtensionsCheckWeight (692) */ + type FrameSystemExtensionsCheckWeight = Null; + + /** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance (693) */ + type OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance = Null; + + /** @name OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls (694) */ + type OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls = Null; + + /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (695) */ + interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact {} + + /** @name OpalRuntimeRuntime (696) */ + type OpalRuntimeRuntime = Null; + + /** @name PalletEthereumFakeTransactionFinalizer (697) */ + type PalletEthereumFakeTransactionFinalizer = Null; + +} // declare module --- /dev/null +++ b/js-packages/types/types.ts @@ -0,0 +1,7 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +export * from './appPromotion/types.js'; +export * from './default/types.js'; +export * from './povinfo/types.js'; +export * from './unique/types.js'; --- /dev/null +++ b/js-packages/types/unique/definitions.ts @@ -0,0 +1,184 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +type RpcParam = { + name: string; + type: string; + isOptional?: true; +}; + +const CROSS_ACCOUNT_ID_TYPE = 'PalletEvmAccountBasicCrossAccountIdRepr'; + +const collectionParam = {name: 'collection', type: 'u32'}; +const tokenParam = {name: 'tokenId', type: 'u32'}; +const propertyKeysParam = {name: 'propertyKeys', type: 'Option>', isOptional: true}; +const crossAccountParam = (name = 'account') => ({name, type: CROSS_ACCOUNT_ID_TYPE}); +const atParam = {name: 'at', type: 'Hash', isOptional: true}; + +const fun = (description: string, params: RpcParam[], type: string) => ({ + description, + params: [...params, atParam], + type, +}); + +export default { + types: {}, + rpc: { + accountTokens: fun( + 'Get tokens owned by an account in a collection', + [collectionParam, crossAccountParam()], + 'Vec', + ), + collectionTokens: fun( + 'Get tokens contained within a collection', + [collectionParam], + 'Vec', + ), + tokenExists: fun( + 'Check if the token exists', + [collectionParam, tokenParam], + 'bool', + ), + + tokenOwner: fun( + 'Get the token owner', + [collectionParam, tokenParam], + `Option<${CROSS_ACCOUNT_ID_TYPE}>`, + ), + topmostTokenOwner: fun( + 'Get the topmost token owner in the hierarchy of a possibly nested token', + [collectionParam, tokenParam], + `Option<${CROSS_ACCOUNT_ID_TYPE}>`, + ), + tokenOwners: fun( + 'Returns 10 tokens owners in no particular order', + [collectionParam, tokenParam], + `Vec<${CROSS_ACCOUNT_ID_TYPE}>`, + ), + tokenChildren: fun( + 'Get tokens nested directly into the token', + [collectionParam, tokenParam], + 'Vec', + ), + + collectionProperties: fun( + 'Get collection properties, optionally limited to the provided keys', + [collectionParam, propertyKeysParam], + 'Vec', + ), + tokenProperties: fun( + 'Get token properties, optionally limited to the provided keys', + [collectionParam, tokenParam, propertyKeysParam], + 'Vec', + ), + propertyPermissions: fun( + 'Get property permissions, optionally limited to the provided keys', + [collectionParam, propertyKeysParam], + 'Vec', + ), + + constMetadata: fun( + 'Get token constant metadata', + [collectionParam, tokenParam], + 'Vec', + ), + variableMetadata: fun( + 'Get token variable metadata', + [collectionParam, tokenParam], + 'Vec', + ), + + tokenData: fun( + 'Get token data, including properties, optionally limited to the provided keys, and total pieces for an RFT', + [collectionParam, tokenParam, propertyKeysParam], + 'UpDataStructsTokenData', + ), + totalSupply: fun( + 'Get the amount of distinctive tokens present in a collection', + [collectionParam], + 'u32', + ), + + accountBalance: fun( + 'Get the amount of any user tokens owned by an account', + [collectionParam, crossAccountParam()], + 'u32', + ), + balance: fun( + 'Get the amount of a specific token owned by an account', + [collectionParam, crossAccountParam(), tokenParam], + 'u128', + ), + allowance: fun( + 'Get the amount of currently possible sponsored transactions on a token for the fee to be taken off a sponsor', + [collectionParam, crossAccountParam('sender'), crossAccountParam('spender'), tokenParam], + 'u128', + ), + + adminlist: fun( + 'Get the list of admin accounts of a collection', + [collectionParam], + 'Vec', + ), + allowlist: fun( + 'Get the list of accounts allowed to operate within a collection', + [collectionParam], + 'Vec', + ), + allowed: fun( + 'Check if a user is allowed to operate within a collection', + [collectionParam, crossAccountParam()], + 'bool', + ), + + lastTokenId: fun( + 'Get the last token ID created in a collection', + [collectionParam], + 'u32', + ), + collectionById: fun( + 'Get a collection by the specified ID', + [collectionParam], + 'Option', + ), + collectionStats: fun( + 'Get chain stats about collections', + [], + 'UpDataStructsCollectionStats', + ), + + nextSponsored: fun( + 'Get the number of blocks until sponsoring a transaction is available', + [collectionParam, crossAccountParam(), tokenParam], + 'Option', + ), + effectiveCollectionLimits: fun( + 'Get effective collection limits', + [collectionParam], + 'Option', + ), + totalPieces: fun( + 'Get the total amount of pieces of an RFT', + [collectionParam, tokenParam], + 'Option', + ), + allowanceForAll: fun( + 'Tells whether the given `owner` approves the `operator`.', + [collectionParam, crossAccountParam('owner'), crossAccountParam('operator')], + 'Option', + ), + }, +}; --- /dev/null +++ b/js-packages/types/unique/index.ts @@ -0,0 +1,4 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +export * from './types.js'; --- /dev/null +++ b/js-packages/types/unique/types.ts @@ -0,0 +1,4 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +export type PHANTOM_UNIQUE = 'unique'; --- a/js-packages/yarn.lock +++ b/js-packages/yarn.lock @@ -595,6 +595,35 @@ languageName: node linkType: hard +"@polkadot/typegen@npm:^10.10.1": + version: 10.10.1 + resolution: "@polkadot/typegen@npm:10.10.1" + dependencies: + "@polkadot/api": 10.10.1 + "@polkadot/api-augment": 10.10.1 + "@polkadot/rpc-augment": 10.10.1 + "@polkadot/rpc-provider": 10.10.1 + "@polkadot/types": 10.10.1 + "@polkadot/types-augment": 10.10.1 + "@polkadot/types-codec": 10.10.1 + "@polkadot/types-create": 10.10.1 + "@polkadot/types-support": 10.10.1 + "@polkadot/util": ^12.5.1 + "@polkadot/util-crypto": ^12.5.1 + "@polkadot/x-ws": ^12.5.1 + handlebars: ^4.7.8 + tslib: ^2.6.2 + yargs: ^17.7.2 + bin: + polkadot-types-chain-info: scripts/polkadot-types-chain-info.mjs + polkadot-types-from-chain: scripts/polkadot-types-from-chain.mjs + polkadot-types-from-defs: scripts/polkadot-types-from-defs.mjs + polkadot-types-internal-interfaces: scripts/polkadot-types-internal-interfaces.mjs + polkadot-types-internal-metadata: scripts/polkadot-types-internal-metadata.mjs + checksum: 9c167fffc476b59524ef864472b688884739c8f2deccc24b09713d7ce4247e43e352be05261ff21072dc1afbbbd2da750a11f42ae657de481d9b402ef858904b + languageName: node + linkType: hard + "@polkadot/types-augment@npm:10.10.1": version: 10.10.1 resolution: "@polkadot/types-augment@npm:10.10.1" @@ -1217,8 +1246,7 @@ version: 0.0.0-use.local resolution: "@unique/opal-types@workspace:types" dependencies: - rxjs: ^7.8.1 - tslib: ^2.6.2 + "@polkadot/typegen": ^10.10.1 languageName: unknown linkType: soft @@ -1965,6 +1993,17 @@ languageName: node linkType: hard +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: ^4.2.0 + strip-ansi: ^6.0.1 + wrap-ansi: ^7.0.0 + checksum: 79648b3b0045f2e285b76fb2e24e207c6db44323581e421c3acbd0e86454cba1b37aea976ab50195a49e7384b871e6dfb2247ad7dec53c02454ac6497394cb56 + languageName: node + linkType: hard + "clone-response@npm:^1.0.2": version: 1.0.3 resolution: "clone-response@npm:1.0.3" @@ -3376,6 +3415,24 @@ languageName: node linkType: hard +"handlebars@npm:^4.7.8": + version: 4.7.8 + resolution: "handlebars@npm:4.7.8" + dependencies: + minimist: ^1.2.5 + neo-async: ^2.6.2 + source-map: ^0.6.1 + uglify-js: ^3.1.4 + wordwrap: ^1.0.0 + dependenciesMeta: + uglify-js: + optional: true + bin: + handlebars: bin/handlebars + checksum: 00e68bb5c183fd7b8b63322e6234b5ac8fbb960d712cb3f25587d559c2951d9642df83c04a1172c918c41bcfc81bfbd7a7718bbce93b893e0135fc99edea93ff + languageName: node + linkType: hard + "har-schema@npm:^2.0.0": version: 2.0.0 resolution: "har-schema@npm:2.0.0" @@ -4199,7 +4256,7 @@ languageName: node linkType: hard -"minimist@npm:^1.2.6": +"minimist@npm:^1.2.5, minimist@npm:^1.2.6": version: 1.2.8 resolution: "minimist@npm:1.2.8" checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0 @@ -4494,6 +4551,13 @@ languageName: node linkType: hard +"neo-async@npm:^2.6.2": + version: 2.6.2 + resolution: "neo-async@npm:2.6.2" + checksum: deac9f8d00eda7b2e5cd1b2549e26e10a0faa70adaa6fdadca701cc55f49ee9018e427f424bac0c790b7c7e2d3068db97f3093f1093975f2acb8f8818b936ed9 + languageName: node + linkType: hard + "next-tick@npm:^1.1.0": version: 1.1.0 resolution: "next-tick@npm:1.1.0" @@ -5422,6 +5486,13 @@ languageName: node linkType: hard +"source-map@npm:^0.6.1": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 + languageName: node + linkType: hard + "sshpk@npm:^1.7.0": version: 1.18.0 resolution: "sshpk@npm:1.18.0" @@ -5473,7 +5544,7 @@ languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -5886,6 +5957,15 @@ languageName: node linkType: hard +"uglify-js@npm:^3.1.4": + version: 3.17.4 + resolution: "uglify-js@npm:3.17.4" + bin: + uglifyjs: bin/uglifyjs + checksum: 7b3897df38b6fc7d7d9f4dcd658599d81aa2b1fb0d074829dd4e5290f7318dbca1f4af2f45acb833b95b1fe0ed4698662ab61b87e94328eb4c0a0d3435baf924 + languageName: node + linkType: hard + "ultron@npm:~1.1.0": version: 1.1.1 resolution: "ultron@npm:1.1.1" @@ -5942,6 +6022,7 @@ eslint-plugin-mocha: ^10.2.0 lossless-json: ^3.0.1 solc: ^0.8.22 + ts-node: ^10.9.1 typechain: ^8.3.2 typescript: ^5.2.2 web3: 1.10.0 @@ -6416,6 +6497,13 @@ languageName: node linkType: hard +"wordwrap@npm:^1.0.0": + version: 1.0.0 + resolution: "wordwrap@npm:1.0.0" + checksum: 2a44b2788165d0a3de71fd517d4880a8e20ea3a82c080ce46e294f0b68b69a2e49cff5f99c600e275c698a90d12c5ea32aff06c311f0db2eb3f1201f3e7b2a04 + languageName: node + linkType: hard + "wordwrapjs@npm:^4.0.0": version: 4.0.1 resolution: "wordwrapjs@npm:4.0.1" @@ -6573,6 +6661,13 @@ languageName: node linkType: hard +"yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c + languageName: node + linkType: hard + "yargs-unparser@npm:2.0.0": version: 2.0.0 resolution: "yargs-unparser@npm:2.0.0" @@ -6600,6 +6695,21 @@ languageName: node linkType: hard +"yargs@npm:^17.7.2": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: ^8.0.1 + escalade: ^3.1.1 + get-caller-file: ^2.0.5 + require-directory: ^2.1.1 + string-width: ^4.2.3 + y18n: ^5.0.5 + yargs-parser: ^21.1.1 + checksum: 73b572e863aa4a8cbef323dd911d79d193b772defd5a51aab0aca2d446655216f5002c42c5306033968193bdbf892a7a4c110b0d77954a7fdf563e653967b56a + languageName: node + linkType: hard + "yn@npm:3.1.1": version: 3.1.1 resolution: "yn@npm:3.1.1" -- gitstuff