diff --git a/src/index.ts b/src/index.ts index a345df9..50d4aec 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,4 @@ export * from "./account"; export * from "./chain"; export * from "./assetType"; export * from "./assetId"; +export * from "./tokenURI"; diff --git a/src/spec.ts b/src/spec.ts index dc09153..a851fa7 100644 --- a/src/spec.ts +++ b/src/spec.ts @@ -87,6 +87,21 @@ const CAIP19AssetId: IdentifierSpec = { }, }; +const CAIP19TokenURI: IdentifierSpec = { + name: "tokenURI", + regex: "[-:a-zA-Z0-9]{13,226}", + parameters: { + delimiter: "#", + values: { + 0: CAIP19AssetId, + 1: { + name: "blockNumberTag", + regex: "[0-9]{1,78}|latest", + }, + }, + }, +}; + export const CAIP = { "2": CAIP2, "10": CAIP10, @@ -94,5 +109,6 @@ export const CAIP = { assetName: AssetName, assetType: CAIP19AssetType, assetId: CAIP19AssetId, + tokenURI: CAIP19TokenURI, }, }; diff --git a/src/tokenURI.ts b/src/tokenURI.ts new file mode 100644 index 0000000..ca218a2 --- /dev/null +++ b/src/tokenURI.ts @@ -0,0 +1,47 @@ +import { AssetId, AssetIdParams } from "./assetId"; +import { CAIP } from "./spec"; +import { IdentifierSpec } from "./types"; +import { isValidId, joinParams, getParams } from "./utils"; + +export interface TokenURIParams { + assetId: string | AssetIdParams; + blockNumberTag: string; +} + +export class TokenURI { + public static spec: IdentifierSpec = CAIP["19"].tokenURI; + + public static parse(id: string): TokenURIParams { + if (!isValidId(id, this.spec)) { + throw new Error(`Invalid ${this.spec.name} provided: ${id}`); + } + return new TokenURI(getParams(id, this.spec)).toJSON(); + } + + public static format(params: TokenURIParams): string { + return joinParams(params as any, this.spec); + } + + public assetId: AssetId; + public blockNumberTag: string; + + constructor(params: TokenURIParams | string) { + if (typeof params === "string") { + params = TokenURI.parse(params); + } + + this.assetId = new AssetId(params.assetId); + this.blockNumberTag = params.blockNumberTag; + } + + public toString(): string { + return TokenURI.format(this.toJSON()); + } + + public toJSON(): TokenURIParams { + return { + assetId: this.assetId, + blockNumberTag: this.blockNumberTag, + }; + } +} diff --git a/test/data/index.ts b/test/data/index.ts index 0c2e781..50a1fb0 100644 --- a/test/data/index.ts +++ b/test/data/index.ts @@ -1,4 +1,5 @@ import { AssetIdParams, AssetTypeParams } from "../../src"; +import { TokenURIParams } from "../../src"; import { AssetNameParams } from "../../src/assetName"; // ChainId Data Points @@ -63,3 +64,15 @@ export const ASSET_ID_NESTED_PARAMS: AssetIdParams = { assetName: ASSET_NAME_PARAMS, tokenId: TOKEN_ID, }; + +export const BLOCK_NUMBER_TAG = "123"; +export const TOKEN_URI_STRING = `${ASSET_ID_STRING}#${BLOCK_NUMBER_TAG}`; +export const TOKEN_URI_PARAMS: TokenURIParams = { + assetId: ASSET_ID_STRING, + blockNumberTag: BLOCK_NUMBER_TAG, +}; + +export const TOKEN_URI_NESTED_PARAMS: TokenURIParams = { + assetId: ASSET_ID_NESTED_PARAMS, + blockNumberTag: BLOCK_NUMBER_TAG, +}; diff --git a/test/tokenURI.test.ts b/test/tokenURI.test.ts new file mode 100644 index 0000000..a79150c --- /dev/null +++ b/test/tokenURI.test.ts @@ -0,0 +1,44 @@ +import { TokenURI } from "../src"; + +import * as data from "./data"; + +function assertInterface(result: TokenURI) { + expect(result.assetId.toString()).toEqual(data.ASSET_ID_STRING); + expect(result.blockNumberTag).toEqual(data.BLOCK_NUMBER_TAG); + expect(result.toString()).toEqual(data.TOKEN_URI_STRING); + expect(result.toJSON()).toEqual(data.TOKEN_URI_NESTED_PARAMS); +} + +describe("AssetId", () => { + it("should parse string", async () => { + const result = TokenURI.parse(data.TOKEN_URI_STRING); + expect(result).toEqual(data.TOKEN_URI_NESTED_PARAMS); + }); + + it("should format params", async () => { + const result = TokenURI.format(data.TOKEN_URI_PARAMS); + expect(result).toEqual(data.TOKEN_URI_STRING); + }); + + it("should instantiate from params", async () => { + const result = new TokenURI(data.TOKEN_URI_PARAMS); + assertInterface(result); + }); + + it("should instantiate from string", async () => { + const result = new TokenURI(data.TOKEN_URI_STRING); + assertInterface(result); + }); + + it("should instantiate from nested params", async () => { + const result = new TokenURI(data.TOKEN_URI_NESTED_PARAMS); + assertInterface(result); + }); + + it("should support JSON.stringify", async () => { + const result = new TokenURI(data.TOKEN_URI_PARAMS); + const str = JSON.stringify(result); + const json = JSON.parse(str); + assertInterface(new TokenURI(json)); + }); +});