All files / rmrk1.0.0/classes nft.ts

55.32% Statements 26/47
15% Branches 3/20
36.36% Functions 4/11
56.52% Lines 26/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189                                  134x                           134x 134x 134x 134x 134x 134x 134x 134x 134x 134x 134x 134x       11352x   11352x       45x 45x                                                                         273x     273x 273x 134x 134x 134x                         139x 139x 139x                                                                                                                                                        
// @todo add data field
import { Change } from "../changelog";
import { validateNFT } from "../../tools/validate-remark";
import { getRemarkData } from "../../tools/utils";
import { OP_TYPES, PREFIX, VERSION } from "../../tools/constants";
 
export class NFT {
  readonly block: number;
  readonly collection: string;
  readonly name: string;
  readonly instance: string;
  readonly transferable: number;
  readonly data?: string;
  readonly sn: string;
  readonly metadata?: string;
  forsale: BigInt;
  reactions: Reactionmap;
  changes: Change[] = [];
  owner: string;
  loadedMetadata?: NFTMetadata;
  burned: string;
  constructor(
    block: number,
    collection: string,
    name: string,
    instance: string,
    transferable: number,
    sn: string,
    metadata?: string,
    data?: string
  ) {
    this.block = block;
    this.collection = collection;
    this.name = name;
    this.instance = instance;
    this.transferable = transferable;
    this.sn = sn;
    this.data = data;
    this.metadata = metadata;
    this.owner = "";
    this.reactions = {};
    this.forsale = BigInt(0);
    this.burned = "";
  }
 
  public getId(): string {
    Iif (!this.block)
      throw new Error("This token is not minted, so it cannot have an ID.");
    return `${this.block}-${this.collection}-${this.instance}-${this.sn}`;
  }
 
  public addChange(c: Change): NFT {
    this.changes.push(c);
    return this;
  }
 
  public mintnft(): string {
    if (this.block) {
      throw new Error("An already existing NFT cannot be minted!");
    }
    return `${PREFIX}::${OP_TYPES.MINTNFT}::${VERSION}::${encodeURIComponent(
      JSON.stringify({
        collection: this.collection,
        name: this.name,
        instance: this.instance,
        transferable: this.transferable,
        sn: this.sn,
        metadata: this.metadata,
      })
    )}`;
  }
 
  public send(recipient: string): string {
    if (!this.block) {
      throw new Error(
        `You can only send an existing NFT. If you just minted this, please load a new, 
        separate instance as the block number is an important part of an NFT's ID.`
      );
    }
    return `${PREFIX}::${
      OP_TYPES.SEND
    }::${VERSION}::${this.getId()}::${recipient}`;
  }
 
  // @todo build this out, maybe data type?
  static checkDataFormat(data: string): boolean {
    return true;
  }
 
  static fromRemark(remark: string, block?: number): NFT | string {
    Iif (!block) {
      block = 0;
    }
    try {
      validateNFT(remark);
      const [prefix, op_type, version, dataString] = remark.split("::");
      const obj = getRemarkData(dataString);
      return new this(
        block,
        obj.collection,
        obj.name,
        obj.instance,
        typeof obj.transferable === "number"
          ? obj.transferable
          : parseInt(obj.transferable, 10),
        obj.sn,
        obj.metadata,
        obj.data
      );
    } catch (e) {
      console.error(e.message);
      console.log(`MINTNFT error: full input was ${remark}`);
      return e.message;
    }
  }
 
  /**
   * @param price In plancks, so 10000000000 for 0.01 KSM. Set to 0 if canceling listing.
   */
  public list(price: number): string {
    if (!this.block) {
      throw new Error(
        `You can only list an existing NFT. If you just minted this, please load a new, 
        separate instance as the block number is an important part of an NFT's ID.`
      );
    }
    return `${PREFIX}::${OP_TYPES.LIST}::${VERSION}::${this.getId()}::${
      price > 0 ? price : "cancel"
    }`;
  }
 
  public buy(): string {
    if (!this.block) {
      throw new Error(
        `You can only buy an existing NFT. If you just minted this, please load a new, 
        separate instance as the block number is an important part of an NFT's ID.`
      );
    }
    return `${PREFIX}::${OP_TYPES.BUY}::${VERSION}::${this.getId()}`;
  }
 
  public consume(): string {
    if (!this.block) {
      throw new Error(
        `You can only consume an existing NFT. If you just minted this, please load a new, 
        separate instance as the block number is an important part of an NFT's ID.`
      );
    }
    return `RMRK::CONSUME::${VERSION}::${this.getId()}`;
  }
 
  /**
   * TBD - hard dependency on Axios / IPFS to fetch remote
   */
  private async load_metadata(): Promise<NFTMetadata> {
    if (this.loadedMetadata) return this.loadedMetadata;
    return {} as NFTMetadata;
  }
}
 
export interface NFTMetadata {
  external_url?: string;
  image?: string;
  image_data?: string;
  description?: string;
  name?: string;
  attributes: Attribute[];
  background_color?: string;
  animation_url?: string;
  youtube_url?: string;
}
 
export interface Attribute {
  display_type: DisplayType;
  trait_type: string;
  value: number | string;
}
 
export enum DisplayType {
  null,
  "boost_number",
  "number",
  "boost_percentage",
}
 
export interface Reactionmap {
  [unicode: string]: string[];
}