From ba7004809b6e30c9f307e4b50bb5382e493333b9 Mon Sep 17 00:00:00 2001 From: blockiosaurus Date: Tue, 16 Jan 2024 13:01:17 -0500 Subject: [PATCH] Adding better cost estimation and more helpful prints. --- clients/cli/commands/compress/images.ts | 6 +---- clients/cli/commands/cost/nft.ts | 7 +++-- clients/cli/commands/inscribe/nft.ts | 35 ++++++++++++++++++++++--- clients/cli/package.json | 3 ++- clients/cli/pnpm-lock.yaml | 7 +++++ 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/clients/cli/commands/compress/images.ts b/clients/cli/commands/compress/images.ts index b9452c2..83d3857 100644 --- a/clients/cli/commands/compress/images.ts +++ b/clients/cli/commands/compress/images.ts @@ -1,4 +1,3 @@ -import Compressor from "compressorjs"; import { readFileSync, renameSync, unlinkSync, writeFileSync } from "fs"; import { glob } from "glob"; import pMap from "p-map"; @@ -8,13 +7,12 @@ import * as path from "path"; export async function compress_images(quality: number, size: number, extension: string, concurrency: number) { const mediaFiles = await glob(`./cache/*.{png,jpeg,jpg}`); - // console.log(mediaFiles); await pMap(mediaFiles, async (filePath) => { const file = new Blob([readFileSync(filePath)]); const { width, height } = imageSize(new Uint8Array(await file.arrayBuffer())); let compressor = sharp(filePath) - .resize(width * size / 100, height * size / 100); + .resize(Math.round(width * size / 100), Math.round(height * size / 100)); switch (extension) { case 'png': @@ -28,8 +26,6 @@ export async function compress_images(quality: number, size: number, extension: const image = await compressor.toFile(filePath + '.' + 'small' + '.' + extension); - // console.log(image); - // writeFileSync(filePath, image); }, { concurrency }); // Remove the original files and rename the compressed files. diff --git a/clients/cli/commands/cost/nft.ts b/clients/cli/commands/cost/nft.ts index d60cc68..957f86c 100644 --- a/clients/cli/commands/cost/nft.ts +++ b/clients/cli/commands/cost/nft.ts @@ -25,18 +25,21 @@ export async function cost_nfts(mints: PublicKey[]) { }, { concurrency: 1 }); fetchJsonBar.stop(); + let totalSize = jsonBytes.reduce((a, b) => a + b.length, 0); let totalCost = jsonBytes.reduce((a, b) => a + getInscribeJsonCost(b.length), getInitCost() * mints.length); + console.log(`Estimating cost for ${mints.length} Media files...`); let fetchMediaBar = new SingleBar({}, Presets.shades_classic) fetchMediaBar.start(mints.length, 0); const mediaBytes = await pMap(mints, async (mint, i) => { - const mediaFiles = globSync(`./cache/${mint}.*`, { ignore: ['cache/*.json', 'cache/*.metadata'] }); + const mediaFiles = globSync(`cache/${mint}.*`, { ignore: ['cache/*.json', 'cache/*.metadata'] }); const media = readFileSync(mediaFiles[0]); fetchMediaBar.increment(); return media; }, { concurrency: 1 }); fetchMediaBar.stop(); + totalSize = mediaBytes.reduce((a, b) => a + b.length, totalSize); totalCost = mediaBytes.reduce((a, b) => a + getInscribeMediaCost(b.length), totalCost); - console.log(`Total Inscription cost is ${totalCost} SOL.`); + console.log(`Total Inscription cost for ${mints.length} NFTs and ${totalSize} bytes is ${totalCost} SOL.`); } \ No newline at end of file diff --git a/clients/cli/commands/inscribe/nft.ts b/clients/cli/commands/inscribe/nft.ts index 20a37cd..29d903d 100644 --- a/clients/cli/commands/inscribe/nft.ts +++ b/clients/cli/commands/inscribe/nft.ts @@ -1,9 +1,12 @@ import { fetchDigitalAsset, mplTokenMetadata } from "@metaplex-foundation/mpl-token-metadata"; import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; -import { accountExists, accountValid, inscribe, loadWalletKey } from "../../utils.js"; +import { accountExists, accountValid, getInitCost, getInscribeJsonCost, getInscribeMediaCost, inscribe, loadWalletKey } from "../../utils.js"; import { mplInscription, findAssociatedInscriptionPda, findInscriptionMetadataPda, findMintInscriptionPda, initializeAssociatedInscription, initializeFromMint } from "@metaplex-foundation/mpl-inscription"; import { PublicKey } from "@metaplex-foundation/umi"; import pMap from "p-map"; +import { exists, existsSync, readFileSync } from "fs"; +import { globSync } from "glob"; +import yesno from "yesno"; const INSCRIPTION_GATEWAY: string = 'https://igw.metaplex.com/'; @@ -26,9 +29,15 @@ export async function inscribe_nfts(rpc: string, keypair: string, mints: PublicK }); const jsonBytes = await pMap(nfts, async (nft, _i) => { - return Buffer.from(await (await fetch(nft.metadata.uri)).arrayBuffer()); + const cacheFile = `cache/${nft.mint.publicKey.toString()}.json`; + if (existsSync(cacheFile)) { + return readFileSync(cacheFile); + } else { + return Buffer.from(await (await fetch(nft.metadata.uri)).arrayBuffer()); + } }, { concurrency }); + let totalCost = jsonBytes.reduce((a, b) => a + getInscribeJsonCost(b.length), getInitCost() * mints.length); const totalJsonBytes = jsonBytes.reduce((a, b) => a + b.length, 0); console.log(`${jsonBytes.length} JSON files are a total of ${totalJsonBytes} bytes.`); const jsonDatas = jsonBytes.map((bytes) => JSON.parse(bytes.toString())); @@ -55,12 +64,30 @@ export async function inscribe_nfts(rpc: string, keypair: string, mints: PublicK return imageURI; }); - const mediaBytes = await pMap(imageURIs, async (imageURI, _i) => { - return Buffer.from(await (await fetch(imageURI)).arrayBuffer()); + const mediaBytes = await pMap(imageURIs, async (imageURI, i) => { + const cacheFiles = globSync(`cache/${nfts[i].metadata.mint.toString()}.*`, { + ignore: ['cache/*.json', 'cache/*.metadata'] + }); + if (cacheFiles.length > 0) { + return readFileSync(cacheFiles[0]); + } else { + return Buffer.from(await (await fetch(imageURI)).arrayBuffer()); + } }, { concurrency }); + + totalCost = mediaBytes.reduce((a, b) => a + getInscribeMediaCost(b.length), totalCost); const totalImageBytes = mediaBytes.reduce((a, b) => a + b.length, 0); console.log(`${mediaBytes.length} Image files are a total of ${totalImageBytes} bytes.`); + const ok = await yesno({ + question: `Inscribing ${nfts.length} NFTs will cost ${totalCost} SOL. Do you want to continue (y/n)?` + }); + + if (!ok) { + console.log("Aborting..."); + return; + } + console.log(`Initializing ${jsonBytes.length} Inscription JSON Accounts...`); const inscriptionMetadataAccounts = await pMap(nfts, async (nft, i) => { diff --git a/clients/cli/package.json b/clients/cli/package.json index bcedd28..d88d9b6 100644 --- a/clients/cli/package.json +++ b/clients/cli/package.json @@ -29,7 +29,8 @@ "glob": "^10.3.10", "image-size": "^1.1.1", "p-map": "npm:@esm2cjs/p-map@^5.5.0", - "sharp": "^0.33.2" + "sharp": "^0.33.2", + "yesno": "^0.4.0" }, "devDependencies": { "@types/node": "^20.10.5", diff --git a/clients/cli/pnpm-lock.yaml b/clients/cli/pnpm-lock.yaml index c523337..3f1a011 100644 --- a/clients/cli/pnpm-lock.yaml +++ b/clients/cli/pnpm-lock.yaml @@ -47,6 +47,9 @@ dependencies: sharp: specifier: ^0.33.2 version: 0.33.2 + yesno: + specifier: ^0.4.0 + version: 0.4.0 devDependencies: '@types/node': @@ -4484,3 +4487,7 @@ packages: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} dev: false + + /yesno@0.4.0: + resolution: {integrity: sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA==} + dev: false