From 265c8f093b0e03161c7c0340b82eb1a55763a01d Mon Sep 17 00:00:00 2001 From: Weronika K Date: Fri, 8 Dec 2023 13:39:35 +0100 Subject: [PATCH] fix: use tag constructor in nft module --- src/__tests__/nft.test.ts | 17 +++++---- src/core/nft.ts | 80 +++++++++++++++++++++------------------ src/types/contract.ts | 7 ++++ 3 files changed, 59 insertions(+), 45 deletions(-) diff --git a/src/__tests__/nft.test.ts b/src/__tests__/nft.test.ts index 03be0c5d..0f1f2a0d 100644 --- a/src/__tests__/nft.test.ts +++ b/src/__tests__/nft.test.ts @@ -1,4 +1,4 @@ -import { Akord, CollectionMetadata, NFTMetadata, StorageType, UDL } from "../index"; +import { Akord, NFTMetadata, StorageType, UDL } from "../index"; import faker from '@faker-js/faker'; import { initInstance, testDataPath } from './common'; import { email, password } from './data/test-credentials'; @@ -78,27 +78,28 @@ describe("Testing NFT functions", () => { name: "Flora Fantasy Test", creator: "xxxx", owner: "yyyy", - collection: "Flora Fantasy Test", description: "A rare digital representation of the mythical Golden Orchid", - type: "image", topics: ["floral", "nature"], banner: file, - } as CollectionMetadata; + }; const udl = { licenseFee: { type: "One-Time", value: 10 } } as UDL; - const { data } = await akord.nft.mintCollection( + const { data, errors } = await akord.nft.mintCollection( vaultId, [{ asset: file, metadata: { name: "Golden Orchid #1" } }], collectionMetadata, { udl: udl } ); + console.log(errors); console.log("Collection id: " + data.collectionId); - console.log("Collection object: " + data.object); - console.log("Minted NFTs: " + data.items); + console.log("Collection object: "); + console.log(data.object); + console.log("Minted NFTs: "); + console.log(data.items); }); it.skip("should list all nfts & collections for given vault", async () => { @@ -110,4 +111,4 @@ describe("Testing NFT functions", () => { const collections = await akord.nft.listAllCollections(vaultId); console.log(collections); }); -}); +}); \ No newline at end of file diff --git a/src/core/nft.ts b/src/core/nft.ts index 9677c99b..e421b152 100644 --- a/src/core/nft.ts +++ b/src/core/nft.ts @@ -13,10 +13,12 @@ import { ListOptions } from "../types/query-options"; import { mergeState, paginate } from "./common"; import { v4 as uuidv4 } from "uuid"; import { StackService } from "./stack"; +import { Logger } from "../logger"; const DEFAULT_TICKER = "ATOMIC"; const DEFAULT_TYPE = "image"; const DEFAULT_CONTRACT_SRC = "Of9pi--Gj7hCTawhgxOwbuWnFI1h24TTgO5pw8ENJNQ"; // Atomic asset contract source +const WARP_MANIFEST = '{"evaluationOptions":{"sourceType":"redstone-sequencer","allowBigInt":true,"internalWrites":true,"unsafeClient":"skip","useConstructor":true}}'; class NFTService extends NodeService { objectType = nodeType.NFT; @@ -60,12 +62,12 @@ class NFTService extends NodeService { createOptions.cacheOnly = service.vault.cacheOnly; if (createOptions.ucm) { - createOptions.arweaveTags = createOptions.arweaveTags.concat([{ name: 'Indexed-By', value: 'ucm' }]); + createOptions.arweaveTags = createOptions.arweaveTags.concat([new Tag('Indexed-By', 'ucm')]); } const fileLike = await createFileLike(asset, createOptions); if (fileLike.type) { - createOptions.arweaveTags.push({ name: 'Content-Type', value: fileLike.type }); + createOptions.arweaveTags.push(new Tag('Content-Type', fileLike.type)); } const fileService = new FileService(this.wallet, this.api, service); const fileUploadResult = await fileService.create(fileLike, createOptions); @@ -119,6 +121,10 @@ class NFTService extends NodeService { throw new BadRequest("No items provided for minting."); } + if (!metadata.name) { + throw new BadRequest("Missing collection name."); + } + const vault = await this.api.getVault(vaultId); if (!vault.public || vault.cacheOnly) { throw new BadRequest("NFT module applies only to public permanent vaults."); @@ -149,7 +155,7 @@ class NFTService extends NodeService { ucm: options.ucm, } as any; - const { nodeId: collectionId, transactionId, object: collectionCreation } = await service.nodeCreate(collectionState, { parentId: options.parentId }); + const { nodeId: collectionId } = await service.nodeCreate(collectionState, { parentId: options.parentId }); for (let nft of items) { try { @@ -164,18 +170,15 @@ class NFTService extends NodeService { mintedItems.push(object.getUri(StorageType.ARWEAVE)); nfts.push({ nftId, transactionId, object }); } catch (error) { + Logger.log("Minting the atomic asset failed."); + Logger.log(error); errors.push({ name: nft.metadata.name, message: error.message, error: error }); } } if (mintedItems.length === 0) { return { - data: { - items: [], - collectionId: collectionId, - transactionId: transactionId, - object: collectionCreation - }, + data: { items: [], collectionId: undefined, transactionId: undefined, object: undefined }, errors, } } @@ -186,26 +189,29 @@ class NFTService extends NodeService { } as any; const collectionTags = [ - { name: 'Data-Protocol', value: "Collection" }, - { name: 'Content-Type', value: "application/json" }, - { name: smartweaveTags.APP_NAME, value: 'SmartWeaveContract' }, - { name: smartweaveTags.APP_VERSION, value: '0.3.0' }, - { name: smartweaveTags.CONTRACT_SOURCE, value: metadata.contractTxId || DEFAULT_CONTRACT_SRC }, - { name: smartweaveTags.INIT_STATE, value: JSON.stringify(collectionMintedState) }, - { name: assetTags.TITLE, value: metadata.name }, - { name: 'Name', value: metadata.name }, - { name: assetTags.DESCRIPTION, value: metadata.description }, - { name: assetTags.TYPE, value: "Document" }, - { name: 'Contract-Manifest', value: '{"evaluationOptions":{"sourceType":"redstone-sequencer","allowBigInt":true,"internalWrites":true,"unsafeClient":"skip","useConstructor":true}}' }, - { name: 'Vault-Id', value: vaultId }, + new Tag('Data-Protocol', "Collection"), + new Tag('Content-Type', "application/json"), + new Tag(smartweaveTags.APP_NAME, 'SmartWeaveContract'), + new Tag(smartweaveTags.APP_VERSION, '0.3.0'), + new Tag(smartweaveTags.CONTRACT_SOURCE, metadata.contractTxId || DEFAULT_CONTRACT_SRC), + new Tag(smartweaveTags.INIT_STATE, JSON.stringify(collectionMintedState)), + new Tag(assetTags.TITLE, metadata.name), + new Tag('Name', metadata.name), + new Tag(assetTags.TYPE, "Document"), + new Tag('Contract-Manifest', WARP_MANIFEST), + new Tag('Vault-Id', vaultId), ]; + if (metadata.description) { + collectionTags.push(new Tag(assetTags.DESCRIPTION, metadata.description)); + } + if (metadata.creator) { - collectionTags.push({ name: 'Creator', value: metadata.creator }); + collectionTags.push(new Tag('Creator', metadata.creator)); } if (metadata.code) { - collectionTags.push({ name: 'Collection-Code', value: metadata.code }); + collectionTags.push(new Tag('Collection-Code', metadata.code)); } if (metadata.banner) { @@ -216,11 +222,11 @@ class NFTService extends NodeService { (metadata.banner).name ? (metadata.banner).name : "Collection banner", { parentId: collectionId } ); - collectionTags.push({ name: 'Banner', value: banner.getUri(StorageType.ARWEAVE) }); + collectionTags.push(new Tag('Banner', banner.getUri(StorageType.ARWEAVE))); collectionMintedState.bannerUri = banner.versions[0].resourceUri; } else { // if not provided, set the first NFT as a collection banner - collectionTags.push({ name: 'Banner', value: nfts[0].object.asset.getUri(StorageType.ARWEAVE) }); + collectionTags.push(new Tag('Banner', nfts[0].object.asset.getUri(StorageType.ARWEAVE))); collectionMintedState.bannerUri = nfts[0].object.asset.resourceUri; } @@ -232,7 +238,7 @@ class NFTService extends NodeService { (metadata.thumbnail).name ? (metadata.thumbnail).name : "Collection thumbnail", { parentId: collectionId } ); - collectionTags.push({ name: 'Thumbnail', value: thumbnail.getUri(StorageType.ARWEAVE) }); + collectionTags.push(new Tag('Thumbnail', thumbnail.getUri(StorageType.ARWEAVE))); collectionMintedState.thumbnailUri = thumbnail.versions[0].resourceUri; } @@ -311,27 +317,27 @@ export const nftMetadataToTags = (metadata: NFTMetadata): Tags => { } as any; const nftTags = [ - { name: smartweaveTags.APP_NAME, value: 'SmartWeaveContract' }, - { name: smartweaveTags.APP_VERSION, value: '0.3.0' }, - { name: smartweaveTags.CONTRACT_SOURCE, value: metadata.contractTxId || DEFAULT_CONTRACT_SRC }, - { name: smartweaveTags.INIT_STATE, value: JSON.stringify(initState) }, - { name: assetTags.TITLE, value: metadata.name }, - { name: assetTags.TYPE, value: metadata.type || DEFAULT_TYPE }, - { name: 'Contract-Manifest', value: '{"evaluationOptions":{"sourceType":"redstone-sequencer","allowBigInt":true,"internalWrites":true,"unsafeClient":"skip","useConstructor":true}}' }, + new Tag(smartweaveTags.APP_NAME, 'SmartWeaveContract'), + new Tag(smartweaveTags.APP_VERSION, '0.3.0'), + new Tag(smartweaveTags.CONTRACT_SOURCE, metadata.contractTxId || DEFAULT_CONTRACT_SRC), + new Tag(smartweaveTags.INIT_STATE, JSON.stringify(initState)), + new Tag(assetTags.TITLE, metadata.name), + new Tag(assetTags.TYPE, metadata.type || DEFAULT_TYPE), + new Tag('Contract-Manifest', WARP_MANIFEST), ]; if (metadata.creator) { - nftTags.push({ name: 'Creator', value: metadata.creator }); + nftTags.push(new Tag('Creator', metadata.creator)); } if (metadata.description) { - nftTags.push({ name: assetTags.DESCRIPTION, value: metadata.description }); + nftTags.push(new Tag(assetTags.DESCRIPTION, metadata.description)); } if (metadata.collection) { - nftTags.push({ name: 'Collection-Code', value: metadata.collection }); + nftTags.push(new Tag('Collection-Code', metadata.collection)); } if (metadata.topics) { for (let topic of metadata.topics) { - nftTags.push({ name: assetTags.TOPIC + ":" + topic, value: topic }); + nftTags.push(new Tag(assetTags.TOPIC + ":" + topic, topic)); } } return nftTags; diff --git a/src/types/contract.ts b/src/types/contract.ts index b0c4178c..4b5c02c9 100644 --- a/src/types/contract.ts +++ b/src/types/contract.ts @@ -5,6 +5,7 @@ import { NodeLike } from "./node"; import { Folder } from "./folder"; import { Memo } from "./memo"; import { Stack } from "./stack"; +import { BadRequest } from "../errors/bad-request"; export interface Contract { state: ContractState @@ -37,6 +38,12 @@ export class Tag { * @returns */ constructor(name: string, value: any) { + if (!name) { + throw new BadRequest("Missing tag name."); + } + if (!value) { + throw new BadRequest("Missing tag value for: " + name); + } return { name: name, value: value.toString()