import { InfinityMintProjectAsset, InfinityMintProjectPath, } from 'infinitymint/dist/app/interfaces'; import { Project } from './project'; import { InfinityMintObject } from 'infinitymint/dist/typechain-types/InfinityMintStorage'; import { unpackColours } from '../utils/helpers'; import { Dictionary } from 'infinitymint/dist/app/helpers'; export class Token { public tokenId: number; public pathId: number = 0; public raw: InfinityMintObject.InfinityObjectStructOutput; public path: InfinityMintProjectPath; public project: Project; public loadError: Error; private pathBlobs: { [key: string]: Blob } = {}; private assetBlobs: Dictionary<{ [key: string]: Blob }> = {}; constructor(project: Project, tokenId: number) { this.project = project; this.tokenId = tokenId; } public async isApprovedOrOwner(address: string) { return await this.project .erc721() .isApprovedOrOwner(address, this.tokenId); } public async setApprovalForAll(address: string, value = true) { return await this.project .erc721(null, false) .setApprovalForAll(address, value); } public async transfer(to: string) { return await this.project .erc721(null, false) .transferBatch([this.tokenId], to); } public async approve(address: string) { return await this.project .erc721(null, false) .approve(address, this.tokenId); } public async isApprovedForAll(operator: string, owner?: string) { owner = owner || this.project.client.getCurrentAddress(); return await this.project .erc721(null, false) .isApprovedForAll(owner, operator); } public async getApproved() { return await this.project.erc721().getApproved(this.tokenId); } public async getOption(option: string) { return await this.project .storage() .getOption(this.project.client.getCurrentAddress(), option); } public getAssets() { let assets: InfinityMintProjectAsset[] = []; this.raw.assets.forEach((assetId) => { if (assetId === 0 || !assetId) assets.push(null); else assets.push(this.project.getAsset(assetId)); }); return assets; } public getPathBlob(key: string = 'path') { if (!this.pathBlobs[key]) throw new Error( `Export ${key} does not exist on this token. Maybe it has not been loaded?` ); return this.pathBlobs[key]; } public getAssetBlob(assetId?: number, key: string = 'path') { if (!this.assetBlobs?.[assetId]?.[key]) throw new Error( `Export ${key} does not exist on this token. Maybe it has not been loaded?` ); return this.assetBlobs[assetId][key]; } public async getBase64EncodedAsset(assetId: number, type: string = 'path') { if (!this.assetBlobs?.[assetId]?.[type]) await this.fetchAssetExport(assetId, type); let blob = this.getAssetBlob(assetId, type); return ( `data:${this.project.getAssetMimeType(assetId, type)};base64,` + Buffer.from(await blob.arrayBuffer()).toString('base64') ); } public async getBase64EncodedPath(type: string = 'path') { if (!this.pathBlobs[type]) await this.fetchPathExport(type); let blob = this.getPathBlob(type); return ( `data:${this.getMimeType(type)};base64,` + Buffer.from(await blob.arrayBuffer()).toString('base64') ); } public getColours() { let colours = this.raw.colours; return unpackColours(colours); } public getMimeType(type: string = 'path') { return this.project.getPathMimeType(this.pathId, type); } public getExtension(type: string = 'path') { return this.project.getPathExportExtension(this.pathId, type); } public getName() { return this.raw.names.join(' '); } public getPathName() { return this.path.name; } public getPathSettings() { return this.path.settings; } public getNames() { return this.raw.names; } public isOwner(address?: string) { address = address || this.project.client.getCurrentAddress(); return this.raw.owner === address; } public async isFlagTrue(flag: string) { try { return ( (await this.project.storage().tokenFlag(this.tokenId, flag)) === true ); } catch (error) { return false; } } public hasLoaded() { return !!this.raw; } public hasFetchedExport(key: string = 'path') { return !!this.pathBlobs[key]; } public async loadFromData( object: InfinityMintObject.InfinityObjectStructOutput, fetchExport = true, abortController?: AbortController ) { this.loadError = null; this.pathId = object.pathId; this.path = this.project.getPath(this.pathId); this.raw = object; if (fetchExport) await this.fetchPathExport('path', abortController, 'any', true); await Promise.all( this.raw.assets.map((assetId) => this.fetchAssetExport(assetId, 'path', abortController) ) ); return true; } public async load(fetchExport = true, abortController?: AbortController) { try { this.loadError = null; this.loadFromData( await this.project.storage().get(this.tokenId), fetchExport, abortController ); } catch (error) { console.warn( `Failed to load token ${this.tokenId} from project ${this.project.name}` ); this.loadError = error; console.warn(error); return false; } } public async fetchAssetExport( assetId?: number, type: string = 'path', abortController?: AbortController, source: 'ipfs' | 'public' | 'api' | 'any' = 'any', force?: boolean ) { if (!force && this.assetBlobs[assetId]?.[type]) return this.assetBlobs[assetId][type]; if (!this.assetBlobs[assetId]) this.assetBlobs[assetId] = {}; this.assetBlobs[assetId][type] = await this.project.fetchAssetExport( assetId, type, abortController, source ); return this.assetBlobs[assetId][type]; } public async fetchPathExport( type: string = 'path', abortController?: AbortController, source: 'ipfs' | 'public' | 'api' | 'any' = 'any', force?: boolean ) { if (!force && this.pathBlobs[type]) return this.pathBlobs[type]; this.pathBlobs[type] = await this.project.fetchPathExport( this.pathId, type, abortController, source ); return this.pathBlobs[type]; } } export const createToken = async ( project: Project, tokenId?: number, pathId?: number, assets?: number[], colours?: number[] ) => { let newPathId = pathId === undefined || pathId === null ? Math.floor(Math.random() * project.deployedProject.paths.length) : pathId; let newAssets = assets || []; let newColours = colours || []; let fakeToken: InfinityMintObject.InfinityObjectStructOutput = { currentTokenId: tokenId || 0, pathId: newPathId, pathSize: 0, names: [], colours: newColours, assets: newAssets, owner: project.client.getCurrentAddress(), destinations: [], mintData: '', } as any; let newToken = new Token(project, tokenId || 0); if (!(await newToken.loadFromData(fakeToken))) throw new Error('Failed to create token'); return newToken; };