1234567891011121314151617import '../interfaces/augment-api-rpc';18import '../interfaces/augment-api-query';19import {ApiPromise} from '@polkadot/api';20import type {AccountId, EventRecord, Event} from '@polkadot/types/interfaces';21import type {GenericEventData} from '@polkadot/types';22import {AnyTuple, IEvent, IKeyringPair} from '@polkadot/types/types';23import {evmToAddress} from '@polkadot/util-crypto';24import BN from 'bn.js';25import chai from 'chai';26import chaiAsPromised from 'chai-as-promised';27import {default as usingApi, executeTransaction, submitTransactionAsync, submitTransactionExpectFailAsync} from '../substrate/substrate-api';28import {hexToStr, strToUTF16, utf16ToStr} from './util';29import {UpDataStructsRpcCollection, UpDataStructsCreateItemData, UpDataStructsProperty} from '@polkadot/types/lookup';30import {UpDataStructsTokenChild} from '../interfaces';3132chai.use(chaiAsPromised);33const expect = chai.expect;3435export type CrossAccountId = {36 Substrate: string,37} | {38 Ethereum: string,39};404142export enum Pallets {43 Inflation = 'inflation',44 RmrkCore = 'rmrkcore',45 ReFungible = 'refungible',46 Fungible = 'fungible',47 NFT = 'nonfungible',48}4950export async function isUnique(): Promise<boolean> {51 return usingApi(async api => {52 const chain = await api.rpc.system.chain();5354 return chain.eq('UNIQUE');55 });56}5758export async function isQuartz(): Promise<boolean> {59 return usingApi(async api => {60 const chain = await api.rpc.system.chain();61 62 return chain.eq('QUARTZ');63 });64}6566let modulesNames: any;67export function getModuleNames(api: ApiPromise): string[] {68 if (typeof modulesNames === 'undefined') 69 modulesNames = api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());70 return modulesNames;71}7273export function normalizeAccountId(input: string | AccountId | CrossAccountId | IKeyringPair): CrossAccountId {74 if (typeof input === 'string') {75 if (input.length >= 47) {76 return {Substrate: input};77 } else if (input.length === 42 && input.startsWith('0x')) {78 return {Ethereum: input.toLowerCase()};79 } else if (input.length === 40 && !input.startsWith('0x')) {80 return {Ethereum: '0x' + input.toLowerCase()};81 } else {82 throw new Error(`Unknown address format: "${input}"`);83 }84 }85 if ('address' in input) {86 return {Substrate: input.address};87 }88 if ('Ethereum' in input) {89 return {90 Ethereum: input.Ethereum.toLowerCase(),91 };92 } else if ('ethereum' in input) {93 return {94 Ethereum: (input as any).ethereum.toLowerCase(),95 };96 } else if ('Substrate' in input) {97 return input;98 } else if ('substrate' in input) {99 return {100 Substrate: (input as any).substrate,101 };102 }103104 105 return {Substrate: input.toString()};106}107export function toSubstrateAddress(input: string | CrossAccountId | IKeyringPair): string {108 input = normalizeAccountId(input);109 if ('Substrate' in input) {110 return input.Substrate;111 } else {112 return evmToAddress(input.Ethereum);113 }114}115116export const U128_MAX = (1n << 128n) - 1n;117118const MICROUNIQUE = 1_000_000_000_000n;119const MILLIUNIQUE = 1_000n * MICROUNIQUE;120const CENTIUNIQUE = 10n * MILLIUNIQUE;121export const UNIQUE = 100n * CENTIUNIQUE;122123interface GenericResult<T> {124 success: boolean;125 data: T | null;126}127128interface CreateCollectionResult {129 success: boolean;130 collectionId: number;131}132133interface CreateItemResult {134 success: boolean;135 collectionId: number;136 itemId: number;137 recipient?: CrossAccountId;138 amount?: number;139}140141interface DestroyItemResult {142 success: boolean;143 collectionId: number;144 itemId: number;145 owner: CrossAccountId;146 amount: number;147}148149interface TransferResult {150 collectionId: number;151 itemId: number;152 sender?: CrossAccountId;153 recipient?: CrossAccountId;154 value: bigint;155}156157interface IReFungibleOwner {158 fraction: BN;159 owner: number[];160}161162interface IGetMessage {163 checkMsgUnqMethod: string;164 checkMsgTrsMethod: string;165 checkMsgSysMethod: string;166}167168export interface IFungibleTokenDataType {169 value: number;170}171172export interface IChainLimits {173 collectionNumbersLimit: number;174 accountTokenOwnershipLimit: number;175 collectionsAdminsLimit: number;176 customDataLimit: number;177 nftSponsorTransferTimeout: number;178 fungibleSponsorTransferTimeout: number;179 refungibleSponsorTransferTimeout: number;180 181 182}183184export interface IReFungibleTokenDataType {185 owner: IReFungibleOwner[];186}187188export function uniqueEventMessage(events: EventRecord[]): IGetMessage {189 let checkMsgUnqMethod = '';190 let checkMsgTrsMethod = '';191 let checkMsgSysMethod = '';192 events.forEach(({event: {method, section}}) => {193 if (section === 'common') {194 checkMsgUnqMethod = method;195 } else if (section === 'treasury') {196 checkMsgTrsMethod = method;197 } else if (section === 'system') {198 checkMsgSysMethod = method;199 } else { return null; }200 });201 const result: IGetMessage = {202 checkMsgUnqMethod,203 checkMsgTrsMethod,204 checkMsgSysMethod,205 };206 return result;207}208209export function getEvent<T extends Event>(events: EventRecord[], check: (event: IEvent<AnyTuple>) => event is T): T | undefined {210 const event = events.find(r => check(r.event));211 if (!event) return;212 return event.event as T;213}214215export function getGenericResult<T>(events: EventRecord[]): GenericResult<T>;216export function getGenericResult<T>(217 events: EventRecord[],218 expectSection: string,219 expectMethod: string,220 extractAction: (data: GenericEventData) => T221): GenericResult<T>;222223export function getGenericResult<T>(224 events: EventRecord[],225 expectSection?: string,226 expectMethod?: string,227 extractAction?: (data: GenericEventData) => T,228): GenericResult<T> {229 let success = false;230 let successData = null;231232 events.forEach(({event: {data, method, section}}) => {233 234 if (method === 'ExtrinsicSuccess') {235 success = true;236 } else if ((expectSection == section) && (expectMethod == method)) {237 successData = extractAction!(data as any);238 }239 });240241 const result: GenericResult<T> = {242 success,243 data: successData,244 };245 return result;246}247248export function getCreateCollectionResult(events: EventRecord[]): CreateCollectionResult {249 const genericResult = getGenericResult(events, 'common', 'CollectionCreated', (data) => parseInt(data[0].toString(), 10));250 const result: CreateCollectionResult = {251 success: genericResult.success,252 collectionId: genericResult.data ?? 0,253 };254 return result;255}256257export function getCreateItemsResult(events: EventRecord[]): CreateItemResult[] {258 const results: CreateItemResult[] = [];259 260 const genericResult = getGenericResult<CreateItemResult[]>(events, 'common', 'ItemCreated', (data) => {261 const collectionId = parseInt(data[0].toString(), 10);262 const itemId = parseInt(data[1].toString(), 10);263 const recipient = normalizeAccountId(data[2].toJSON() as any);264 const amount = parseInt(data[3].toString(), 10);265266 const itemRes: CreateItemResult = {267 success: true,268 collectionId,269 itemId,270 recipient,271 amount,272 };273274 results.push(itemRes);275 return results;276 });277278 if (!genericResult.success) return [];279 return results;280}281282export function getCreateItemResult(events: EventRecord[]): CreateItemResult {283 const genericResult = getGenericResult(events, 'common', 'ItemCreated', (data) => data.map(function(value) { return value.toJSON(); }));284 285 if (genericResult.data == null) 286 return {287 success: genericResult.success,288 collectionId: 0,289 itemId: 0,290 amount: 0,291 };292 else 293 return {294 success: genericResult.success,295 collectionId: genericResult.data[0] as number,296 itemId: genericResult.data[1] as number,297 recipient: normalizeAccountId(genericResult.data![2] as any),298 amount: genericResult.data[3] as number,299 };300}301302export function getDestroyItemsResult(events: EventRecord[]): DestroyItemResult[] {303 const results: DestroyItemResult[] = [];304 305 const genericResult = getGenericResult<DestroyItemResult[]>(events, 'common', 'ItemDestroyed', (data) => {306 const collectionId = parseInt(data[0].toString(), 10);307 const itemId = parseInt(data[1].toString(), 10);308 const owner = normalizeAccountId(data[2].toJSON() as any);309 const amount = parseInt(data[3].toString(), 10);310311 const itemRes: DestroyItemResult = {312 success: true,313 collectionId,314 itemId,315 owner,316 amount,317 };318319 results.push(itemRes);320 return results;321 });322323 if (!genericResult.success) return [];324 return results;325}326327export function getTransferResult(api: ApiPromise, events: EventRecord[]): TransferResult {328 for (const {event} of events) {329 if (api.events.common.Transfer.is(event)) {330 const [collection, token, sender, recipient, value] = event.data;331 return {332 collectionId: collection.toNumber(),333 itemId: token.toNumber(),334 sender: normalizeAccountId(sender.toJSON() as any),335 recipient: normalizeAccountId(recipient.toJSON() as any),336 value: value.toBigInt(),337 };338 }339 }340 throw new Error('no transfer event');341}342343interface Nft {344 type: 'NFT';345}346347interface Fungible {348 type: 'Fungible';349 decimalPoints: number;350}351352interface ReFungible {353 type: 'ReFungible';354}355356export type CollectionMode = Nft | Fungible | ReFungible;357358export type Property = {359 key: any,360 value: any,361};362363type Permission = {364 mutable: boolean;365 collectionAdmin: boolean;366 tokenOwner: boolean;367}368369type PropertyPermission = {370 key: any;371 permission: Permission;372}373374export type CreateCollectionParams = {375 mode: CollectionMode,376 name: string,377 description: string,378 tokenPrefix: string,379 properties?: Array<Property>,380 propPerm?: Array<PropertyPermission>381};382383const defaultCreateCollectionParams: CreateCollectionParams = {384 description: 'description',385 mode: {type: 'NFT'},386 name: 'name',387 tokenPrefix: 'prefix',388};389390export async function391createCollection(392 api: ApiPromise,393 sender: IKeyringPair,394 params: Partial<CreateCollectionParams> = {},395): Promise<CreateCollectionResult> {396 const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};397398 let modeprm = {};399 if (mode.type === 'NFT') {400 modeprm = {nft: null};401 } else if (mode.type === 'Fungible') {402 modeprm = {fungible: mode.decimalPoints};403 } else if (mode.type === 'ReFungible') {404 modeprm = {refungible: null};405 }406407 const tx = api.tx.unique.createCollectionEx({408 name: strToUTF16(name),409 description: strToUTF16(description),410 tokenPrefix: strToUTF16(tokenPrefix),411 mode: modeprm as any,412 });413 const events = await submitTransactionAsync(sender, tx);414 return getCreateCollectionResult(events);415}416417export async function createCollectionExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {418 const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};419420 let collectionId = 0;421 await usingApi(async (api, privateKeyWrapper) => {422 423 const collectionCountBefore = await getCreatedCollectionCount(api);424425 426 const alicePrivateKey = privateKeyWrapper('//Alice');427428 const result = await createCollection(api, alicePrivateKey, params);429430 431 const collectionCountAfter = await getCreatedCollectionCount(api);432433 434 const collection = await queryCollectionExpectSuccess(api, result.collectionId);435436 437 438 expect(result.success).to.be.true;439 expect(result.collectionId).to.be.equal(collectionCountAfter);440 441 expect(collection).to.be.not.null;442 expect(collectionCountAfter).to.be.equal(collectionCountBefore + 1, 'Error: NFT collection NOT created.');443 expect(collection.owner.toString()).to.be.equal(toSubstrateAddress(alicePrivateKey));444 expect(utf16ToStr(collection.name.toJSON() as any)).to.be.equal(name);445 expect(utf16ToStr(collection.description.toJSON() as any)).to.be.equal(description);446 expect(hexToStr(collection.tokenPrefix.toJSON())).to.be.equal(tokenPrefix);447448 collectionId = result.collectionId;449 });450451 return collectionId;452}453454export async function createCollectionWithPropsExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {455 const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};456457 let collectionId = 0;458 await usingApi(async (api, privateKeyWrapper) => {459 460 const collectionCountBefore = await getCreatedCollectionCount(api);461462 463 const alicePrivateKey = privateKeyWrapper('//Alice');464465 let modeprm = {};466 if (mode.type === 'NFT') {467 modeprm = {nft: null};468 } else if (mode.type === 'Fungible') {469 modeprm = {fungible: mode.decimalPoints};470 } else if (mode.type === 'ReFungible') {471 modeprm = {refungible: null};472 }473474 const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any, properties: params.properties, tokenPropertyPermissions: params.propPerm});475 const events = await submitTransactionAsync(alicePrivateKey, tx);476 const result = getCreateCollectionResult(events);477478 479 const collectionCountAfter = await getCreatedCollectionCount(api);480481 482 const collection = await queryCollectionExpectSuccess(api, result.collectionId);483484 485 486 expect(result.success).to.be.true;487 expect(result.collectionId).to.be.equal(collectionCountAfter);488 489 expect(collection).to.be.not.null;490 expect(collectionCountAfter).to.be.equal(collectionCountBefore + 1, 'Error: NFT collection NOT created.');491 expect(collection.owner.toString()).to.be.equal(toSubstrateAddress(alicePrivateKey));492 expect(utf16ToStr(collection.name.toJSON() as any)).to.be.equal(name);493 expect(utf16ToStr(collection.description.toJSON() as any)).to.be.equal(description);494 expect(hexToStr(collection.tokenPrefix.toJSON())).to.be.equal(tokenPrefix);495496497 collectionId = result.collectionId;498 });499500 return collectionId;501}502503export async function createCollectionWithPropsExpectFailure(params: Partial<CreateCollectionParams> = {}) {504 const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};505506 await usingApi(async (api, privateKeyWrapper) => {507 508 const collectionCountBefore = await getCreatedCollectionCount(api);509510 511 const alicePrivateKey = privateKeyWrapper('//Alice');512513 let modeprm = {};514 if (mode.type === 'NFT') {515 modeprm = {nft: null};516 } else if (mode.type === 'Fungible') {517 modeprm = {fungible: mode.decimalPoints};518 } else if (mode.type === 'ReFungible') {519 modeprm = {refungible: null};520 }521522 const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any, properties: params.properties, tokenPropertyPermissions: params.propPerm});523 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;524525526 527 const collectionCountAfter = await getCreatedCollectionCount(api);528529 expect(collectionCountAfter).to.be.equal(collectionCountBefore, 'Error: Collection with incorrect data created.');530 });531}532533export async function createCollectionExpectFailure(params: Partial<CreateCollectionParams> = {}) {534 const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};535536 let modeprm = {};537 if (mode.type === 'NFT') {538 modeprm = {nft: null};539 } else if (mode.type === 'Fungible') {540 modeprm = {fungible: mode.decimalPoints};541 } else if (mode.type === 'ReFungible') {542 modeprm = {refungible: null};543 }544545 await usingApi(async (api, privateKeyWrapper) => {546 547 const collectionCountBefore = await getCreatedCollectionCount(api);548549 550 const alicePrivateKey = privateKeyWrapper('//Alice');551 const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any});552 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;553554 555 const collectionCountAfter = await getCreatedCollectionCount(api);556557 558 expect(collectionCountAfter).to.be.equal(collectionCountBefore, 'Error: Collection with incorrect data created.');559 });560}561562export async function findUnusedAddress(api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair, seedAddition = ''): Promise<IKeyringPair> {563 let bal = 0n;564 let unused;565 do {566 const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;567 unused = privateKeyWrapper(`//${randomSeed}`);568 bal = (await api.query.system.account(unused.address)).data.free.toBigInt();569 } while (bal !== 0n);570 return unused;571}572573export async function getAllowance(api: ApiPromise, collectionId: number, owner: CrossAccountId | string | IKeyringPair, approved: CrossAccountId | string | IKeyringPair, tokenId: number) {574 return (await api.rpc.unique.allowance(collectionId, normalizeAccountId(owner), normalizeAccountId(approved), tokenId)).toBigInt();575}576577export function findUnusedAddresses(api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair, amount: number): Promise<IKeyringPair[]> {578 return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(api, privateKeyWrapper, '_' + Date.now())));579}580581export async function findNotExistingCollection(api: ApiPromise): Promise<number> {582 const totalNumber = await getCreatedCollectionCount(api);583 const newCollection: number = totalNumber + 1;584 return newCollection;585}586587function getDestroyResult(events: EventRecord[]): boolean {588 let success = false;589 events.forEach(({event: {method}}) => {590 if (method == 'ExtrinsicSuccess') {591 success = true;592 }593 });594 return success;595}596597export async function destroyCollectionExpectFailure(collectionId: number, senderSeed = '//Alice') {598 await usingApi(async (api, privateKeyWrapper) => {599 600 const alicePrivateKey = privateKeyWrapper(senderSeed);601 const tx = api.tx.unique.destroyCollection(collectionId);602 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;603 });604}605606export async function destroyCollectionExpectSuccess(collectionId: number, senderSeed = '//Alice') {607 await usingApi(async (api, privateKeyWrapper) => {608 609 const alicePrivateKey = privateKeyWrapper(senderSeed);610 const tx = api.tx.unique.destroyCollection(collectionId);611 const events = await submitTransactionAsync(alicePrivateKey, tx);612 const result = getDestroyResult(events);613 expect(result).to.be.true;614615 616 expect(await getDetailedCollectionInfo(api, collectionId)).to.be.null;617 });618}619620export async function setCollectionLimitsExpectSuccess(sender: IKeyringPair, collectionId: number, limits: any) {621 await usingApi(async (api) => {622 const tx = api.tx.unique.setCollectionLimits(collectionId, limits);623 const events = await submitTransactionAsync(sender, tx);624 const result = getGenericResult(events);625626 expect(result.success).to.be.true;627 });628}629630export const setCollectionPermissionsExpectSuccess = async (sender: IKeyringPair, collectionId: number, permissions: any) => {631 await usingApi(async(api) => {632 const tx = api.tx.unique.setCollectionPermissions(collectionId, permissions);633 const events = await submitTransactionAsync(sender, tx);634 const result = getGenericResult(events);635636 expect(result.success).to.be.true;637 });638};639640export async function setCollectionLimitsExpectFailure(sender: IKeyringPair, collectionId: number, limits: any) {641 await usingApi(async (api) => {642 const tx = api.tx.unique.setCollectionLimits(collectionId, limits);643 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;644 const result = getGenericResult(events);645646 expect(result.success).to.be.false;647 });648}649650export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string, sender = '//Alice') {651 await usingApi(async (api, privateKeyWrapper) => {652653 654 const senderPrivateKey = privateKeyWrapper(sender);655 const tx = api.tx.unique.setCollectionSponsor(collectionId, sponsor);656 const events = await submitTransactionAsync(senderPrivateKey, tx);657 const result = getGenericResult(events);658659 660 const collection = await queryCollectionExpectSuccess(api, collectionId);661662 663 expect(result.success).to.be.true;664 expect(collection.sponsorship.toJSON()).to.deep.equal({665 unconfirmed: sponsor,666 });667 });668}669670export async function removeCollectionSponsorExpectSuccess(collectionId: number, sender = '//Alice') {671 await usingApi(async (api, privateKeyWrapper) => {672673 674 const alicePrivateKey = privateKeyWrapper(sender);675 const tx = api.tx.unique.removeCollectionSponsor(collectionId);676 const events = await submitTransactionAsync(alicePrivateKey, tx);677 const result = getGenericResult(events);678679 680 const collection = await queryCollectionExpectSuccess(api, collectionId);681682 683 expect(result.success).to.be.true;684 expect(collection.sponsorship.toJSON()).to.be.deep.equal({disabled: null});685 });686}687688export async function removeCollectionSponsorExpectFailure(collectionId: number, senderSeed = '//Alice') {689 await usingApi(async (api, privateKeyWrapper) => {690691 692 const alicePrivateKey = privateKeyWrapper(senderSeed);693 const tx = api.tx.unique.removeCollectionSponsor(collectionId);694 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;695 });696}697698export async function setCollectionSponsorExpectFailure(collectionId: number, sponsor: string, senderSeed = '//Alice') {699 await usingApi(async (api, privateKeyWrapper) => {700701 702 const alicePrivateKey = privateKeyWrapper(senderSeed);703 const tx = api.tx.unique.setCollectionSponsor(collectionId, sponsor);704 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;705 });706}707708export async function confirmSponsorshipExpectSuccess(collectionId: number, senderSeed = '//Alice') {709 await usingApi(async (api, privateKeyWrapper) => {710711 712 const sender = privateKeyWrapper(senderSeed);713 await confirmSponsorshipByKeyExpectSuccess(collectionId, sender);714 });715}716717export async function confirmSponsorshipByKeyExpectSuccess(collectionId: number, sender: IKeyringPair) {718 await usingApi(async (api, privateKeyWrapper) => {719720 721 const tx = api.tx.unique.confirmSponsorship(collectionId);722 const events = await submitTransactionAsync(sender, tx);723 const result = getGenericResult(events);724725 726 const collection = await queryCollectionExpectSuccess(api, collectionId);727728 729 expect(result.success).to.be.true;730 expect(collection.sponsorship.toJSON()).to.be.deep.equal({731 confirmed: sender.address,732 });733 });734}735736737export async function confirmSponsorshipExpectFailure(collectionId: number, senderSeed = '//Alice') {738 await usingApi(async (api, privateKeyWrapper) => {739740 741 const sender = privateKeyWrapper(senderSeed);742 const tx = api.tx.unique.confirmSponsorship(collectionId);743 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;744 });745}746747export async function enableContractSponsoringExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {748 await usingApi(async (api) => {749 const tx = api.tx.unique.enableContractSponsoring(contractAddress, enable);750 const events = await submitTransactionAsync(sender, tx);751 const result = getGenericResult(events);752753 expect(result.success).to.be.true;754 });755}756757export async function enableContractSponsoringExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {758 await usingApi(async (api) => {759 const tx = api.tx.unique.enableContractSponsoring(contractAddress, enable);760 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;761 const result = getGenericResult(events);762763 expect(result.success).to.be.false;764 });765}766767export async function setTransferFlagExpectSuccess(sender: IKeyringPair, collectionId: number, enabled: boolean) {768769 await usingApi(async (api) => {770771 const tx = api.tx.unique.setTransfersEnabledFlag(collectionId, enabled);772 const events = await submitTransactionAsync(sender, tx);773 const result = getGenericResult(events);774775 expect(result.success).to.be.true;776 });777}778779export async function setTransferFlagExpectFailure(sender: IKeyringPair, collectionId: number, enabled: boolean) {780781 await usingApi(async (api) => {782783 const tx = api.tx.unique.setTransfersEnabledFlag(collectionId, enabled);784 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;785 const result = getGenericResult(events);786787 expect(result.success).to.be.false;788 });789}790791export async function setContractSponsoringRateLimitExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {792 await usingApi(async (api) => {793 const tx = api.tx.unique.setContractSponsoringRateLimit(contractAddress, rateLimit);794 const events = await submitTransactionAsync(sender, tx);795 const result = getGenericResult(events);796797 expect(result.success).to.be.true;798 });799}800801export async function setContractSponsoringRateLimitExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {802 await usingApi(async (api) => {803 const tx = api.tx.unique.setContractSponsoringRateLimit(contractAddress, rateLimit);804 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;805 const result = getGenericResult(events);806807 expect(result.success).to.be.false;808 });809}810811export async function getNextSponsored(812 api: ApiPromise,813 collectionId: number,814 account: string | CrossAccountId,815 tokenId: number,816): Promise<number> {817 return Number((await api.rpc.unique.nextSponsored(collectionId, account, tokenId)).unwrapOr(-1));818}819820export async function toggleContractAllowlistExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, value = true) {821 await usingApi(async (api) => {822 const tx = api.tx.unique.toggleContractAllowList(contractAddress, value);823 const events = await submitTransactionAsync(sender, tx);824 const result = getGenericResult(events);825826 expect(result.success).to.be.true;827 });828}829830export async function isAllowlistedInContract(contractAddress: AccountId | string, user: string) {831 let allowlisted = false;832 await usingApi(async (api) => {833 allowlisted = (await api.query.unique.contractAllowList(contractAddress, user)).toJSON() as boolean;834 });835 return allowlisted;836}837838export async function addToContractAllowListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {839 await usingApi(async (api) => {840 const tx = api.tx.unique.addToContractAllowList(contractAddress.toString(), user.toString());841 const events = await submitTransactionAsync(sender, tx);842 const result = getGenericResult(events);843844 expect(result.success).to.be.true;845 });846}847848export async function removeFromContractAllowListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {849 await usingApi(async (api) => {850 const tx = api.tx.unique.removeFromContractAllowList(contractAddress.toString(), user.toString());851 const events = await submitTransactionAsync(sender, tx);852 const result = getGenericResult(events);853854 expect(result.success).to.be.true;855 });856}857858export async function removeFromContractAllowListExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {859 await usingApi(async (api) => {860 const tx = api.tx.unique.removeFromContractAllowList(contractAddress.toString(), user.toString());861 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;862 const result = getGenericResult(events);863864 expect(result.success).to.be.false;865 });866}867868export interface CreateFungibleData {869 readonly Value: bigint;870}871872export interface CreateReFungibleData { }873export interface CreateNftData { }874875export type CreateItemData = {876 NFT: CreateNftData;877} | {878 Fungible: CreateFungibleData;879} | {880 ReFungible: CreateReFungibleData;881};882883export async function burnItem(api: ApiPromise, sender: IKeyringPair, collectionId: number, tokenId: number, value: number | bigint) : Promise<boolean> {884 const tx = api.tx.unique.burnItem(collectionId, tokenId, value);885 const events = await submitTransactionAsync(sender, tx);886 return getGenericResult(events).success;887}888889export async function burnItemExpectSuccess(sender: IKeyringPair, collectionId: number, tokenId: number, value: number | bigint = 1) {890 await usingApi(async (api) => {891 const balanceBefore = await getBalance(api, collectionId, normalizeAccountId(sender), tokenId);892 893 expect(balanceBefore >= BigInt(value)).to.be.true;894895 expect(await burnItem(api, sender, collectionId, tokenId, value)).to.be.true;896897 const balanceAfter = await getBalance(api, collectionId, normalizeAccountId(sender), tokenId);898 expect(balanceAfter + BigInt(value)).to.be.equal(balanceBefore);899 });900}901902export async function burnItemExpectFailure(sender: IKeyringPair, collectionId: number, tokenId: number, value: number | bigint = 1) {903 await usingApi(async (api) => {904 const tx = api.tx.unique.burnItem(collectionId, tokenId, value);905906 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;907 const result = getCreateCollectionResult(events);908 909 expect(result.success).to.be.false;910 });911}912913export async function burnFromExpectSuccess(sender: IKeyringPair, from: IKeyringPair | CrossAccountId, collectionId: number, tokenId: number, value: number | bigint = 1) {914 await usingApi(async (api) => {915 const tx = api.tx.unique.burnFrom(collectionId, normalizeAccountId(from), tokenId, value);916 const events = await submitTransactionAsync(sender, tx);917 return getGenericResult(events).success;918 });919}920921export async function922approve(923 api: ApiPromise,924 collectionId: number,925 tokenId: number, owner: IKeyringPair, approved: CrossAccountId | string | IKeyringPair, amount: number | bigint,926) {927 const approveUniqueTx = api.tx.unique.approve(normalizeAccountId(approved), collectionId, tokenId, amount);928 const events = await submitTransactionAsync(owner, approveUniqueTx);929 return getGenericResult(events).success;930}931932export async function933approveExpectSuccess(934 collectionId: number,935 tokenId: number, owner: IKeyringPair, approved: CrossAccountId | string, amount: number | bigint = 1,936) {937 await usingApi(async (api: ApiPromise) => {938 const result = await approve(api, collectionId, tokenId, owner, approved, amount);939 expect(result).to.be.true;940941 expect(await getAllowance(api, collectionId, owner, approved, tokenId)).to.be.equal(BigInt(amount));942 });943}944945export async function adminApproveFromExpectSuccess(946 collectionId: number,947 tokenId: number, admin: IKeyringPair, owner: CrossAccountId | string, approved: CrossAccountId | string, amount: number | bigint = 1,948) {949 await usingApi(async (api: ApiPromise) => {950 const approveUniqueTx = api.tx.unique.approve(normalizeAccountId(approved), collectionId, tokenId, amount);951 const events = await submitTransactionAsync(admin, approveUniqueTx);952 const result = getGenericResult(events);953 expect(result.success).to.be.true;954955 expect(await getAllowance(api, collectionId, owner, approved, tokenId)).to.be.equal(BigInt(amount));956 });957}958959export async function960transferFrom(961 api: ApiPromise,962 collectionId: number,963 tokenId: number,964 accountApproved: IKeyringPair,965 accountFrom: IKeyringPair | CrossAccountId,966 accountTo: IKeyringPair | CrossAccountId,967 value: number | bigint,968) {969 const from = normalizeAccountId(accountFrom);970 const to = normalizeAccountId(accountTo);971 const transferFromTx = api.tx.unique.transferFrom(from, to, collectionId, tokenId, value);972 const events = await submitTransactionAsync(accountApproved, transferFromTx);973 return getGenericResult(events).success;974}975976export async function977transferFromExpectSuccess(978 collectionId: number,979 tokenId: number,980 accountApproved: IKeyringPair,981 accountFrom: IKeyringPair | CrossAccountId,982 accountTo: IKeyringPair | CrossAccountId,983 value: number | bigint = 1,984 type = 'NFT',985) {986 await usingApi(async (api: ApiPromise) => {987 const from = normalizeAccountId(accountFrom);988 const to = normalizeAccountId(accountTo);989 let balanceBefore = 0n;990 if (type === 'Fungible' || type === 'ReFungible') {991 balanceBefore = await getBalance(api, collectionId, to, tokenId);992 }993 expect(await transferFrom(api, collectionId, tokenId, accountApproved, accountFrom, accountTo, value)).to.be.true;994 if (type === 'NFT') {995 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(to);996 }997 if (type === 'Fungible') {998 const balanceAfter = await getBalance(api, collectionId, to, tokenId);999 if (JSON.stringify(to) !== JSON.stringify(from)) {1000 expect(balanceAfter - balanceBefore).to.be.equal(BigInt(value));1001 } else {1002 expect(balanceAfter).to.be.equal(balanceBefore);1003 }1004 }1005 if (type === 'ReFungible') {1006 expect(await getBalance(api, collectionId, to, tokenId)).to.be.equal(balanceBefore + BigInt(value));1007 }1008 });1009}10101011export async function1012transferFromExpectFail(1013 collectionId: number,1014 tokenId: number,1015 accountApproved: IKeyringPair,1016 accountFrom: IKeyringPair,1017 accountTo: IKeyringPair,1018 value: number | bigint = 1,1019) {1020 await usingApi(async (api: ApiPromise) => {1021 const transferFromTx = api.tx.unique.transferFrom(normalizeAccountId(accountFrom.address), normalizeAccountId(accountTo.address), collectionId, tokenId, value);1022 const events = await expect(submitTransactionExpectFailAsync(accountApproved, transferFromTx)).to.be.rejected;1023 const result = getCreateCollectionResult(events);1024 1025 expect(result.success).to.be.false;1026 });1027}102810291030export async function getBlockNumber(api: ApiPromise): Promise<number> {1031 return new Promise<number>(async (resolve) => {1032 const unsubscribe = await api.rpc.chain.subscribeNewHeads((head) => {1033 unsubscribe();1034 resolve(head.number.toNumber());1035 });1036 });1037}10381039export async function addCollectionAdminExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | CrossAccountId) {1040 await usingApi(async (api) => {1041 const changeAdminTx = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(address));1042 const events = await submitTransactionAsync(sender, changeAdminTx);1043 const result = getCreateCollectionResult(events);1044 expect(result.success).to.be.true;1045 });1046}10471048export async function adminApproveFromExpectFail(1049 collectionId: number,1050 tokenId: number, admin: IKeyringPair, owner: CrossAccountId | string, approved: CrossAccountId | string, amount: number | bigint = 1,1051) {1052 await usingApi(async (api: ApiPromise) => {1053 const approveUniqueTx = api.tx.unique.approve(normalizeAccountId(approved), collectionId, tokenId, amount);1054 const events = await expect(submitTransactionAsync(admin, approveUniqueTx)).to.be.rejected;1055 const result = getGenericResult(events);1056 expect(result.success).to.be.false;1057 });1058}10591060export async function1061getFreeBalance(account: IKeyringPair): Promise<bigint> {1062 let balance = 0n;1063 await usingApi(async (api) => {1064 balance = BigInt((await api.query.system.account(account.address)).data.free.toString());1065 });10661067 return balance;1068}10691070export async function transferBalanceTo(api: ApiPromise, source: IKeyringPair, target: string, amount = 1000n * UNIQUE) {1071 const tx = api.tx.balances.transfer(target, amount);1072 const events = await submitTransactionAsync(source, tx);1073 const result = getGenericResult(events);1074 expect(result.success).to.be.true;1075}10761077export async function1078scheduleExpectSuccess(1079 operationTx: any,1080 sender: IKeyringPair,1081 blockSchedule: number,1082 scheduledId: string,1083 period = 1,1084 repetitions = 1,1085) {1086 await usingApi(async (api: ApiPromise) => {1087 const blockNumber: number | undefined = await getBlockNumber(api);1088 const expectedBlockNumber = blockNumber + blockSchedule;10891090 expect(blockNumber).to.be.greaterThan(0);1091 const scheduleTx = api.tx.scheduler.scheduleNamed( 1092 scheduledId,1093 expectedBlockNumber, 1094 repetitions > 1 ? [period, repetitions] : null, 1095 0, 1096 {Value: operationTx as any},1097 );10981099 const events = await submitTransactionAsync(sender, scheduleTx);1100 expect(getGenericResult(events).success).to.be.true;1101 });1102}11031104export async function1105scheduleExpectFailure(1106 operationTx: any,1107 sender: IKeyringPair,1108 blockSchedule: number,1109 scheduledId: string,1110 period = 1,1111 repetitions = 1,1112) {1113 await usingApi(async (api: ApiPromise) => {1114 const blockNumber: number | undefined = await getBlockNumber(api);1115 const expectedBlockNumber = blockNumber + blockSchedule;11161117 expect(blockNumber).to.be.greaterThan(0);1118 const scheduleTx = api.tx.scheduler.scheduleNamed( 1119 scheduledId,1120 expectedBlockNumber, 1121 repetitions <= 1 ? null : [period, repetitions], 1122 0, 1123 {Value: operationTx as any},1124 );11251126 1127 await expect(submitTransactionExpectFailAsync(sender, scheduleTx)).to.be.rejected;1128 1129 });1130}11311132export async function1133scheduleTransferAndWaitExpectSuccess(1134 collectionId: number,1135 tokenId: number,1136 sender: IKeyringPair,1137 recipient: IKeyringPair,1138 value: number | bigint = 1,1139 blockSchedule: number,1140 scheduledId: string,1141) {1142 await usingApi(async (api: ApiPromise) => {1143 await scheduleTransferExpectSuccess(collectionId, tokenId, sender, recipient, value, blockSchedule, scheduledId);11441145 const recipientBalanceBefore = (await api.query.system.account(recipient.address)).data.free.toBigInt();11461147 1148 await waitNewBlocks(blockSchedule + 1);11491150 const recipientBalanceAfter = (await api.query.system.account(recipient.address)).data.free.toBigInt();11511152 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(recipient.address));1153 expect(recipientBalanceAfter).to.be.equal(recipientBalanceBefore);1154 });1155}11561157export async function1158scheduleTransferExpectSuccess(1159 collectionId: number,1160 tokenId: number,1161 sender: IKeyringPair,1162 recipient: IKeyringPair,1163 value: number | bigint = 1,1164 blockSchedule: number,1165 scheduledId: string,1166) {1167 await usingApi(async (api: ApiPromise) => {1168 const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);11691170 await scheduleExpectSuccess(transferTx, sender, blockSchedule, scheduledId);11711172 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(sender.address));1173 });1174}11751176export async function1177scheduleTransferFundsPeriodicExpectSuccess(1178 amount: bigint,1179 sender: IKeyringPair,1180 recipient: IKeyringPair,1181 blockSchedule: number,1182 scheduledId: string,1183 period: number,1184 repetitions: number,1185) {1186 await usingApi(async (api: ApiPromise) => {1187 const transferTx = api.tx.balances.transfer(recipient.address, amount);11881189 const balanceBefore = await getFreeBalance(recipient);1190 1191 await scheduleExpectSuccess(transferTx, sender, blockSchedule, scheduledId, period, repetitions);11921193 expect(await getFreeBalance(recipient)).to.be.equal(balanceBefore);1194 });1195}11961197export async function1198transfer(1199 api: ApiPromise,1200 collectionId: number,1201 tokenId: number,1202 sender: IKeyringPair,1203 recipient: IKeyringPair | CrossAccountId,1204 value: number | bigint,1205) : Promise<boolean> {1206 const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient), collectionId, tokenId, value);1207 const events = await executeTransaction(api, sender, transferTx);1208 return getGenericResult(events).success;1209}12101211export async function1212transferExpectSuccess(1213 collectionId: number,1214 tokenId: number,1215 sender: IKeyringPair,1216 recipient: IKeyringPair | CrossAccountId,1217 value: number | bigint = 1,1218 type = 'NFT',1219) {1220 await usingApi(async (api: ApiPromise) => {1221 const from = normalizeAccountId(sender);1222 const to = normalizeAccountId(recipient);12231224 let balanceBefore = 0n;1225 if (type === 'Fungible' || type === 'ReFungible') {1226 balanceBefore = await getBalance(api, collectionId, to, tokenId);1227 }12281229 const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient), collectionId, tokenId, value);1230 const events = await executeTransaction(api, sender, transferTx);1231 const result = getTransferResult(api, events);12321233 expect(result.collectionId).to.be.equal(collectionId);1234 expect(result.itemId).to.be.equal(tokenId);1235 expect(result.sender).to.be.deep.equal(normalizeAccountId(sender.address));1236 expect(result.recipient).to.be.deep.equal(to);1237 expect(result.value).to.be.equal(BigInt(value));12381239 if (type === 'NFT') {1240 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(to);1241 }1242 if (type === 'Fungible' || type === 'ReFungible') {1243 const balanceAfter = await getBalance(api, collectionId, to, tokenId);1244 if (JSON.stringify(to) !== JSON.stringify(from)) {1245 expect(balanceAfter - balanceBefore).to.be.equal(BigInt(value));1246 } else {1247 expect(balanceAfter).to.be.equal(balanceBefore);1248 }1249 }1250 });1251}12521253export async function1254transferExpectFailure(1255 collectionId: number,1256 tokenId: number,1257 sender: IKeyringPair,1258 recipient: IKeyringPair | CrossAccountId,1259 value: number | bigint = 1,1260) {1261 await usingApi(async (api: ApiPromise) => {1262 const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient), collectionId, tokenId, value);1263 const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected;1264 const result = getGenericResult(events);1265 1266 1267 1268 expect(result.success).to.be.false;1269 1270 });1271}12721273export async function1274approveExpectFail(1275 collectionId: number,1276 tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number | bigint = 1,1277) {1278 await usingApi(async (api: ApiPromise) => {1279 const approveUniqueTx = api.tx.unique.approve(normalizeAccountId(approved.address), collectionId, tokenId, amount);1280 const events = await expect(submitTransactionExpectFailAsync(owner, approveUniqueTx)).to.be.rejected;1281 const result = getCreateCollectionResult(events);1282 1283 expect(result.success).to.be.false;1284 });1285}12861287export async function getBalance(1288 api: ApiPromise,1289 collectionId: number,1290 owner: string | CrossAccountId | IKeyringPair,1291 token: number,1292): Promise<bigint> {1293 return (await api.rpc.unique.balance(collectionId, normalizeAccountId(owner), token)).toBigInt();1294}1295export async function getTokenOwner(1296 api: ApiPromise,1297 collectionId: number,1298 token: number,1299): Promise<CrossAccountId> {1300 const owner = (await api.rpc.unique.tokenOwner(collectionId, token)).toJSON() as any;1301 if (owner == null) throw new Error('owner == null');1302 return normalizeAccountId(owner);1303}1304export async function getTopmostTokenOwner(1305 api: ApiPromise,1306 collectionId: number,1307 token: number,1308): Promise<CrossAccountId> {1309 const owner = (await api.rpc.unique.topmostTokenOwner(collectionId, token)).toJSON() as any;1310 if (owner == null) throw new Error('owner == null');1311 return normalizeAccountId(owner);1312}1313export async function getTokenChildren(1314 api: ApiPromise,1315 collectionId: number,1316 tokenId: number,1317): Promise<UpDataStructsTokenChild[]> {1318 return (await api.rpc.unique.tokenChildren(collectionId, tokenId)).toJSON() as any;1319}1320export async function isTokenExists(1321 api: ApiPromise,1322 collectionId: number,1323 token: number,1324): Promise<boolean> {1325 return (await api.rpc.unique.tokenExists(collectionId, token)).toJSON();1326}1327export async function getLastTokenId(1328 api: ApiPromise,1329 collectionId: number,1330): Promise<number> {1331 return (await api.rpc.unique.lastTokenId(collectionId)).toJSON();1332}1333export async function getAdminList(1334 api: ApiPromise,1335 collectionId: number,1336): Promise<string[]> {1337 return (await api.rpc.unique.adminlist(collectionId)).toHuman() as any;1338}1339export async function getTokenProperties(1340 api: ApiPromise,1341 collectionId: number,1342 tokenId: number,1343 propertyKeys: string[],1344): Promise<UpDataStructsProperty[]> {1345 return (await api.rpc.unique.tokenProperties(collectionId, tokenId, propertyKeys)).toHuman() as any;1346}13471348export async function createFungibleItemExpectSuccess(1349 sender: IKeyringPair,1350 collectionId: number,1351 data: CreateFungibleData,1352 owner: CrossAccountId | string = sender.address,1353) {1354 return await usingApi(async (api) => {1355 const tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), {Fungible: data});13561357 const events = await submitTransactionAsync(sender, tx);1358 const result = getCreateItemResult(events);13591360 expect(result.success).to.be.true;1361 return result.itemId;1362 });1363}13641365export async function createMultipleItemsExpectSuccess(sender: IKeyringPair, collectionId: number, itemsData: any, owner: CrossAccountId | string = sender.address) {1366 await usingApi(async (api) => {1367 const to = normalizeAccountId(owner);1368 const tx = api.tx.unique.createMultipleItems(collectionId, to, itemsData);13691370 const events = await submitTransactionAsync(sender, tx);1371 expect(getGenericResult(events).success).to.be.true;1372 });1373}13741375export async function createMultipleItemsWithPropsExpectSuccess(sender: IKeyringPair, collectionId: number, itemsData: any, owner: CrossAccountId | string = sender.address) {1376 await usingApi(async (api) => {1377 const to = normalizeAccountId(owner);1378 const tx = api.tx.unique.createMultipleItems(collectionId, to, itemsData);13791380 const events = await submitTransactionAsync(sender, tx);1381 const result = getCreateItemsResult(events);13821383 for (const res of result) {1384 expect(await api.rpc.unique.tokenProperties(collectionId, res.itemId)).not.to.be.empty;1385 }1386 });1387}13881389export async function createMultipleItemsExWithPropsExpectSuccess(sender: IKeyringPair, collectionId: number, itemsData: any) {1390 await usingApi(async (api) => {1391 const tx = api.tx.unique.createMultipleItemsEx(collectionId, itemsData);13921393 const events = await submitTransactionAsync(sender, tx);1394 const result = getCreateItemsResult(events);13951396 for (const res of result) {1397 expect(await api.rpc.unique.tokenProperties(collectionId, res.itemId)).not.to.be.empty;1398 }1399 });1400}14011402export async function createItemWithPropsExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, props: Array<Property>, owner: CrossAccountId | string = sender.address) {1403 let newItemId = 0;1404 await usingApi(async (api) => {1405 const to = normalizeAccountId(owner);1406 const itemCountBefore = await getLastTokenId(api, collectionId);1407 const itemBalanceBefore = await getBalance(api, collectionId, to, newItemId);14081409 let tx;1410 if (createMode === 'Fungible') {1411 const createData = {fungible: {value: 10}};1412 tx = api.tx.unique.createItem(collectionId, to, createData as any);1413 } else if (createMode === 'ReFungible') {1414 const createData = {refungible: {pieces: 100}};1415 tx = api.tx.unique.createItem(collectionId, to, createData as any);1416 } else {1417 const data = api.createType('UpDataStructsCreateItemData', {NFT: {properties: props}});1418 tx = api.tx.unique.createItem(collectionId, to, data as UpDataStructsCreateItemData);1419 }14201421 const events = await submitTransactionAsync(sender, tx);1422 const result = getCreateItemResult(events);14231424 const itemCountAfter = await getLastTokenId(api, collectionId);1425 const itemBalanceAfter = await getBalance(api, collectionId, to, newItemId);14261427 if (createMode === 'NFT') {1428 expect(await api.rpc.unique.tokenProperties(collectionId, result.itemId)).not.to.be.empty;1429 }14301431 1432 1433 expect(result.success).to.be.true;1434 if (createMode === 'Fungible') {1435 expect(itemBalanceAfter - itemBalanceBefore).to.be.equal(10n);1436 } else {1437 expect(itemCountAfter).to.be.equal(itemCountBefore + 1);1438 }1439 expect(collectionId).to.be.equal(result.collectionId);1440 expect(itemCountAfter.toString()).to.be.equal(result.itemId.toString());1441 expect(to).to.be.deep.equal(result.recipient);1442 newItemId = result.itemId;1443 });1444 return newItemId;1445}14461447export async function createItemWithPropsExpectFailure(sender: IKeyringPair, collectionId: number, createMode: string, props: Array<Property>, owner: CrossAccountId | string = sender.address) {1448 await usingApi(async (api) => {14491450 let tx;1451 if (createMode === 'NFT') {1452 const data = api.createType('UpDataStructsCreateItemData', {NFT: {properties: props}}) as UpDataStructsCreateItemData;1453 tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), data);1454 } else {1455 tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), createMode);1456 }145714581459 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1460 if(events.message && events.message.toString().indexOf('1002: Verification Error') > -1) return;1461 const result = getCreateItemResult(events);14621463 expect(result.success).to.be.false;1464 });1465}14661467export async function createItemExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, owner: CrossAccountId | string = sender.address) {1468 let newItemId = 0;1469 await usingApi(async (api) => {1470 const to = normalizeAccountId(owner);1471 const itemCountBefore = await getLastTokenId(api, collectionId);1472 const itemBalanceBefore = await getBalance(api, collectionId, to, newItemId);14731474 let tx;1475 if (createMode === 'Fungible') {1476 const createData = {fungible: {value: 10}};1477 tx = api.tx.unique.createItem(collectionId, to, createData as any);1478 } else if (createMode === 'ReFungible') {1479 const createData = {refungible: {pieces: 100}};1480 tx = api.tx.unique.createItem(collectionId, to, createData as any);1481 } else {1482 const createData = {nft: {}};1483 tx = api.tx.unique.createItem(collectionId, to, createData as any);1484 }14851486 const events = await executeTransaction(api, sender, tx);1487 const result = getCreateItemResult(events);14881489 const itemCountAfter = await getLastTokenId(api, collectionId);1490 const itemBalanceAfter = await getBalance(api, collectionId, to, newItemId);14911492 1493 1494 expect(result.success).to.be.true;1495 if (createMode === 'Fungible') {1496 expect(itemBalanceAfter - itemBalanceBefore).to.be.equal(10n);1497 } else {1498 expect(itemCountAfter).to.be.equal(itemCountBefore + 1);1499 }1500 expect(collectionId).to.be.equal(result.collectionId);1501 expect(itemCountAfter.toString()).to.be.equal(result.itemId.toString());1502 expect(to).to.be.deep.equal(result.recipient);1503 newItemId = result.itemId;1504 });1505 return newItemId;1506}15071508export async function createRefungibleToken(api: ApiPromise, sender: IKeyringPair, collectionId: number, amount: bigint, owner: CrossAccountId | IKeyringPair | string = sender.address) : Promise<CreateItemResult> {1509 const createData = {refungible: {pieces: amount}};1510 const tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), createData as any);15111512 const events = await submitTransactionAsync(sender, tx);1513 return getCreateItemResult(events);1514}15151516export async function createItemExpectFailure(sender: IKeyringPair, collectionId: number, createMode: string, owner: CrossAccountId | string = sender.address) {1517 await usingApi(async (api) => {1518 const tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), createMode);15191520 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1521 const result = getCreateItemResult(events);15221523 expect(result.success).to.be.false;1524 });1525}15261527export async function setPublicAccessModeExpectSuccess(1528 sender: IKeyringPair, collectionId: number,1529 accessMode: 'Normal' | 'AllowList',1530) {1531 await usingApi(async (api) => {15321533 1534 const tx = api.tx.unique.setCollectionPermissions(collectionId, {access: accessMode});1535 const events = await submitTransactionAsync(sender, tx);1536 const result = getGenericResult(events);15371538 1539 const collection = await queryCollectionExpectSuccess(api, collectionId);15401541 1542 1543 expect(result.success).to.be.true;1544 expect(collection.permissions.access.toHuman()).to.be.equal(accessMode);1545 });1546}15471548export async function setPublicAccessModeExpectFail(1549 sender: IKeyringPair, collectionId: number,1550 accessMode: 'Normal' | 'AllowList',1551) {1552 await usingApi(async (api) => {15531554 1555 const tx = api.tx.unique.setCollectionPermissions(collectionId, {access: accessMode});1556 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1557 const result = getGenericResult(events);15581559 1560 1561 expect(result.success).to.be.false;1562 });1563}15641565export async function enableAllowListExpectSuccess(sender: IKeyringPair, collectionId: number) {1566 await setPublicAccessModeExpectSuccess(sender, collectionId, 'AllowList');1567}15681569export async function enableAllowListExpectFail(sender: IKeyringPair, collectionId: number) {1570 await setPublicAccessModeExpectFail(sender, collectionId, 'AllowList');1571}15721573export async function disableAllowListExpectSuccess(sender: IKeyringPair, collectionId: number) {1574 await setPublicAccessModeExpectSuccess(sender, collectionId, 'Normal');1575}15761577export async function setMintPermissionExpectSuccess(sender: IKeyringPair, collectionId: number, enabled: boolean) {1578 await usingApi(async (api) => {15791580 1581 const tx = api.tx.unique.setCollectionPermissions(collectionId, {mintMode: enabled});1582 const events = await submitTransactionAsync(sender, tx);1583 const result = getGenericResult(events);1584 expect(result.success).to.be.true;15851586 1587 const collection = await queryCollectionExpectSuccess(api, collectionId);15881589 expect(collection.permissions.mintMode.toHuman()).to.be.equal(enabled);1590 });1591}15921593export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {1594 await setMintPermissionExpectSuccess(sender, collectionId, true);1595}15961597export async function setMintPermissionExpectFailure(sender: IKeyringPair, collectionId: number, enabled: boolean) {1598 await usingApi(async (api) => {1599 1600 const tx = api.tx.unique.setCollectionPermissions(collectionId, {mintMode: enabled});1601 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1602 const result = getCreateCollectionResult(events);1603 1604 expect(result.success).to.be.false;1605 });1606}16071608export async function setChainLimitsExpectFailure(sender: IKeyringPair, limits: IChainLimits) {1609 await usingApi(async (api) => {1610 1611 const tx = api.tx.unique.setChainLimits(limits);1612 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1613 const result = getCreateCollectionResult(events);1614 1615 expect(result.success).to.be.false;1616 });1617}16181619export async function isAllowlisted(api: ApiPromise, collectionId: number, address: string | CrossAccountId) {1620 return (await api.rpc.unique.allowed(collectionId, normalizeAccountId(address))).toJSON();1621}16221623export async function addToAllowListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId | CrossAccountId) {1624 await usingApi(async (api) => {1625 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.false;16261627 1628 const tx = api.tx.unique.addToAllowList(collectionId, normalizeAccountId(address));1629 const events = await submitTransactionAsync(sender, tx);1630 const result = getGenericResult(events);1631 expect(result.success).to.be.true;16321633 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;1634 });1635}16361637export async function addToAllowListAgainExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId) {1638 await usingApi(async (api) => {16391640 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;16411642 1643 const tx = api.tx.unique.addToAllowList(collectionId, normalizeAccountId(address));1644 const events = await submitTransactionAsync(sender, tx);1645 const result = getGenericResult(events);1646 expect(result.success).to.be.true;16471648 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;1649 });1650}16511652export async function addToAllowListExpectFail(sender: IKeyringPair, collectionId: number, address: string | AccountId) {1653 await usingApi(async (api) => {16541655 1656 const tx = api.tx.unique.addToAllowList(collectionId, normalizeAccountId(address));1657 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1658 const result = getGenericResult(events);16591660 1661 1662 expect(result.success).to.be.false;1663 });1664}16651666export async function removeFromAllowListExpectSuccess(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1667 await usingApi(async (api) => {1668 1669 const tx = api.tx.unique.removeFromAllowList(collectionId, normalizeAccountId(address));1670 const events = await submitTransactionAsync(sender, tx);1671 const result = getGenericResult(events);16721673 1674 1675 expect(result.success).to.be.true;1676 });1677}16781679export async function removeFromAllowListExpectFailure(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1680 await usingApi(async (api) => {1681 1682 const tx = api.tx.unique.removeFromAllowList(collectionId, normalizeAccountId(address));1683 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1684 const result = getGenericResult(events);16851686 1687 1688 expect(result.success).to.be.false;1689 });1690}16911692export const getDetailedCollectionInfo = async (api: ApiPromise, collectionId: number)1693 : Promise<UpDataStructsRpcCollection | null> => {1694 return (await api.rpc.unique.collectionById(collectionId)).unwrapOr(null);1695};16961697export const getCreatedCollectionCount = async (api: ApiPromise): Promise<number> => {1698 1699 return (await api.rpc.unique.collectionStats()).created.toNumber();1700};17011702export async function queryCollectionExpectSuccess(api: ApiPromise, collectionId: number): Promise<UpDataStructsRpcCollection> {1703 return (await api.rpc.unique.collectionById(collectionId)).unwrap();1704}17051706export async function waitNewBlocks(blocksCount = 1): Promise<void> {1707 await usingApi(async (api) => {1708 const promise = new Promise<void>(async (resolve) => {1709 const unsubscribe = await api.rpc.chain.subscribeNewHeads(() => {1710 if (blocksCount > 0) {1711 blocksCount--;1712 } else {1713 unsubscribe();1714 resolve();1715 }1716 });1717 });1718 return promise;1719 });1720}17211722export async function repartitionRFT(1723 api: ApiPromise,1724 collectionId: number,1725 sender: IKeyringPair,1726 tokenId: number,1727 amount: bigint,1728): Promise<boolean> {1729 const tx = api.tx.unique.repartition(collectionId, tokenId, amount);1730 const events = await submitTransactionAsync(sender, tx);1731 const result = getGenericResult(events);17321733 return result.success;1734}17351736export async function itApi(name: string, cb: (apis: { api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair }) => any, opts: { only?: boolean, skip?: boolean } = {}) {1737 let i: any = it;1738 if (opts.only) i = i.only;1739 else if (opts.skip) i = i.skip;1740 i(name, async () => {1741 await usingApi(async (api, privateKeyWrapper) => {1742 await cb({api, privateKeyWrapper});1743 });1744 });1745}17461747itApi.only = (name: string, cb: (apis: { api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair }) => any) => itApi(name, cb, {only: true});1748itApi.skip = (name: string, cb: (apis: { api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair }) => any) => itApi(name, cb, {skip: true});