From 3d34e6721c698dddf77683fac93c751b9383e109 Mon Sep 17 00:00:00 2001 From: bearni95 Date: Mon, 13 May 2024 16:10:19 +0200 Subject: [PATCH] added 4everland ipfs scripts --- packages/taikoon-ui/.env.example | 3 +- .../taikoon-ui/src/generated/abi/index.ts | 10 +- packages/taikoon/package.json | 5 +- packages/taikoon/script/js/4everland.js | 166 ++++++++++++++++ packages/taikoon/script/js/add-images-ipfs.js | 183 ++++++++++-------- .../taikoon/script/js/generate-merkle-tree.js | 49 +++-- packages/taikoon/script/js/resize-images.js | 81 ++++---- 7 files changed, 342 insertions(+), 155 deletions(-) create mode 100644 packages/taikoon/script/js/4everland.js diff --git a/packages/taikoon-ui/.env.example b/packages/taikoon-ui/.env.example index a9f2216670..dba149f008 100644 --- a/packages/taikoon-ui/.env.example +++ b/packages/taikoon-ui/.env.example @@ -1,2 +1,3 @@ PUBLIC_WALLETCONNECT_PROJECT_ID= -PUBLIC_IPFS_GATEWAY= +PUBLIC_IPFS_GATEWAY=https://taikoons.4everland.link/ipfs/ +PUBLIC_LAUNCH_DATE=2024-05-26T00:00:00 diff --git a/packages/taikoon-ui/src/generated/abi/index.ts b/packages/taikoon-ui/src/generated/abi/index.ts index fae30cd385..276d4ce8e7 100644 --- a/packages/taikoon-ui/src/generated/abi/index.ts +++ b/packages/taikoon-ui/src/generated/abi/index.ts @@ -3,7 +3,7 @@ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** - * - [__View Contract on Holesky Etherscan__](https://holesky.etherscan.io/address/0x0874bD201a33bff5Ebd4f8200c6482A72457FeF0) + * - [__View Contract on Holesky Etherscan__](https://holesky.etherscan.io/address/0xfDbaA6d6c382A2555856bFaB315D5E6F3CDA1393) * - */ export const taikoonTokenAbi = [ @@ -544,16 +544,16 @@ export const taikoonTokenAbi = [ ] as const; /** - * - [__View Contract on Holesky Etherscan__](https://holesky.etherscan.io/address/0x0874bD201a33bff5Ebd4f8200c6482A72457FeF0) + * - [__View Contract on Holesky Etherscan__](https://holesky.etherscan.io/address/0xfDbaA6d6c382A2555856bFaB315D5E6F3CDA1393) * - */ export const taikoonTokenAddress = { - 17000: '0x0874bD201a33bff5Ebd4f8200c6482A72457FeF0', - 31337: '0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82', + 17000: '0xfDbaA6d6c382A2555856bFaB315D5E6F3CDA1393', + 31337: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512', } as const; /** - * - [__View Contract on Holesky Etherscan__](https://holesky.etherscan.io/address/0x0874bD201a33bff5Ebd4f8200c6482A72457FeF0) + * - [__View Contract on Holesky Etherscan__](https://holesky.etherscan.io/address/0xfDbaA6d6c382A2555856bFaB315D5E6F3CDA1393) * - */ export const taikoonTokenConfig = { diff --git a/packages/taikoon/package.json b/packages/taikoon/package.json index 4a3a23534c..b3568d2128 100644 --- a/packages/taikoon/package.json +++ b/packages/taikoon/package.json @@ -5,7 +5,7 @@ "scripts": { "clean": "rm -rf abis cache* && forge clean", "compile": "forge build --build-info --extra-output storage-layout", - "eslint": "pnpm exec eslint --ignore-path .eslintignore --ext .js,.ts .", + "eslint": "pnpm exec eslint --ignore-path .eslintignore --ext .js,.ts . --fix", "fmt:sol": "forge fmt", "lint:sol": "forge fmt && pnpm solhint 'contracts/**/*.sol'", "test": "pnpm clean && pnpm compile && forge test --match-path 'test/*.t.sol' -vvv", @@ -13,6 +13,7 @@ "merkle": "node script/js/generate-merkle-tree.js", "deploy:localhost": "forge clean && pnpm compile && forge script script/sol/Deploy.s.sol --rpc-url http://localhost:8545 --broadcast", "deploy:holesky": "forge clean && pnpm compile && forge script script/sol/Deploy.s.sol --rpc-url https://l1rpc.hekla.taiko.xyz/ --broadcast --gas-estimate-multiplier 200", + "publish:ipfs": "node script/js/4everland.js", "deploy:ipfs": "rm -rf data/metadata/* && node script/js/resize-images.js && node script/js/add-images-ipfs.js && echo 'IPFS Base URI:' && ipfs add -r ./data/metadata/ && echo 'Update your .env file with the new IPFS URI'" }, "devDependencies": { @@ -33,6 +34,8 @@ "typescript": "^5.2.2" }, "dependencies": { + "@aws-sdk/client-s3": "^3.574.0", + "@aws-sdk/lib-storage": "^3.574.0", "@openzeppelin/contracts": "5.0.2", "@openzeppelin/contracts-upgradeable": "5.0.2", "@openzeppelin/merkle-tree": "^1.0.6", diff --git a/packages/taikoon/script/js/4everland.js b/packages/taikoon/script/js/4everland.js new file mode 100644 index 0000000000..c9e5c0592f --- /dev/null +++ b/packages/taikoon/script/js/4everland.js @@ -0,0 +1,166 @@ +const { S3 } = require("@aws-sdk/client-s3"); +const { Upload } = require("@aws-sdk/lib-storage"); +const fs = require("fs"); +const fsPromises = fs.promises; +const path = require("path"); + +async function uploadFile(s3, params) { + try { + const task = new Upload({ + client: s3, + queueSize: 3, // 3 MiB + params, + }); + + const res = await task.done(); + return res.ETag.split('"').join(""); + } catch (error) { + if (error) { + console.log("task", error.message); + } + } +} + +// Helper function to form the metadata JSON object +function populateNFTMetadata(name, description, CID) { + return { + name, + description, + image: CID, + }; +} + +async function main() { + const s3Params = { + accessKey: "KYEKUCIU01RUW4LL5M26", + secretKey: "f+htmVYyj9IMN1XOoJfuVuYr08P6vb4ZW2xhxIQM", + }; + const { accessKey, secretKey } = s3Params; + const s3 = new S3({ + endpoint: "https://endpoint.4everland.co", + credentials: { + accessKeyId: accessKey, + secretAccessKey: secretKey, + }, + region: "4EVERLAND", + }); + + // Get the images to upload from the local filesystem (/images) + console.log(`Importing images from the images/ directory...`); + const imgDirPath = path.join(path.resolve(__dirname, "../../data"), "images"); + const filesName = await fsPromises.readdir(imgDirPath, (err) => { + if (err) { + console.log("Import from directory failed: ", err); + } + }); + + // Uploading images to IPFS + console.log(`Uploading image data to IPFS...`); + const imageCIDs = []; + const imagesSummary = []; + let imageCount = 1; + const imagesName = filesName.filter((fileName) => fileName.includes(".png")); + for await (const imageName of imagesName) { + const imageFilePath = path.join( + path.resolve(__dirname, "../../data"), + "images", + imageName, + ); + const params = { + Bucket: "taikoons-testbucket", + Key: imageName, + ContentType: "image/png", + Body: fs.readFileSync(imageFilePath), + }; + + const imageCID = await uploadFile(s3, params); + + imageCIDs.push(imageCID); + imagesSummary.push({ imageCID, imageCount }); + console.log(`Image ${imageCount} added to IPFS with CID of ${imageCID}`); + imageCount++; + } + console.log(` `); + + // Add the metadata to IPFS + console.log(`Adding metadata to IPFS...`); + let taikoonId = 0; + for await (const imageCID of imageCIDs) { + taikoonId++; + + // write into a file + fs.writeFileSync( + path.join( + path.resolve(__dirname, "../../data"), + "metadata", + `${taikoonId}.json`, + ), + JSON.stringify( + populateNFTMetadata( + `Taikoon ${taikoonId}`, + "A Taikoon", + imageCID.toString(), + ), + ), + ); + + console.log( + path.join( + path.resolve(__dirname, "../../data"), + "metadata", + `${taikoonId}.json`, + ), + ); + /* + metadataCIDs.push(metadataCID); + for (let i = 0; i < imagesSummary.length; i++) { + if (imagesSummary[i].imageCID == imageCID) { + imagesSummary[i].metadataCID = metadataCID; + } + } */ + // console.log(`Metadata with image CID ${imageCID} added to IPFS with CID of ${metadataCID}`); + } + console.log(` `); + /* + fs.writeFileSync( + path.join( + path.resolve(__dirname, "../../data"), + "metadata", + "summary.json", + ), + JSON.stringify({ imagesSummary }), + ); +*/ + /* + const putObjectOutput = await s3.putObject({ + Bucket: "bucketname", + Key: "key", + Body: "data content", + }); + + // multipart upload + const params = { + Bucket, + Key: file.name, + Body: file, + ContentType: file.type, + }; + try { + const task = new Upload({ + client: s3, + queueSize: 3, // 3 MiB + params, + }); + task.on("httpUploadProgress", (e) => { + const progress = ((e.loaded / e.total) * 100) | 0; + console.log(progress, e); + }); + await task.done(); + } catch (error) { + if (error) { + console.log("task", error.message); + } + } */ +} + +main(); diff --git a/packages/taikoon/script/js/add-images-ipfs.js b/packages/taikoon/script/js/add-images-ipfs.js index 620de6725e..85d14a8d8d 100644 --- a/packages/taikoon/script/js/add-images-ipfs.js +++ b/packages/taikoon/script/js/add-images-ipfs.js @@ -1,95 +1,120 @@ -const path = require('path') -const fs = require('fs') -const fsPromises = fs.promises +const path = require("path"); +const fs = require("fs"); +const fsPromises = fs.promises; // Helper function to form the metadata JSON object function populateNFTMetadata(name, description, CID) { - return { - name, - description, - image: CID, - } + return { + name, + description, + image: CID, + }; } async function main() { - console.log(`Configuring the IPFS instance...`) - const { create } = await import('ipfs-http-client') - const ipfs = create() - const endpointConfig = await ipfs.getEndpointConfig() - console.log(`IPFS configured to connect via: `) - console.debug(endpointConfig) - console.log(` `) - - // Get the images to upload from the local filesystem (/images) - console.log(`Importing images from the images/ directory...`) - const imgDirPath = path.join(path.resolve(__dirname, '../../data'), 'images') - const filesName = await fsPromises.readdir(imgDirPath, (err) => { - if (err) { - console.log('Import from directory failed: ', err) - } - }) - const imagesName = filesName.filter((fileName) => fileName.includes('.png')) - let imagesData = [] - for await (const imageName of imagesName) { - let imageFilePath = path.join(path.resolve(__dirname, '../../data'), 'images', imageName) - let imageData = await fsPromises.readFile(imageFilePath) - imagesData.push(imageData) - } - console.log(`Imported images as buffered data\n`) + console.log(`Configuring the IPFS instance...`); + const { create } = await import("ipfs-http-client"); + const ipfs = create(); + const endpointConfig = await ipfs.getEndpointConfig(); + console.log(`IPFS configured to connect via: `); + console.debug(endpointConfig); + console.log(` `); - // Uploading images to IPFS - console.log(`Uploading image data to IPFS...`) - let imageCIDs = [] - let imagesSummary = [] - let imageCount = 1 - for await (const imageData of imagesData) { - let { cid: imageCID } = await ipfs.add({ - content: imageData, - }) - imageCIDs.push(imageCID) - imagesSummary.push({ imageCID, imageCount }) - console.log(`Image added to IPFS with CID of ${imageCID}`) - imageCount++ + // Get the images to upload from the local filesystem (/images) + console.log(`Importing images from the images/ directory...`); + const imgDirPath = path.join(path.resolve(__dirname, "../../data"), "images"); + const filesName = await fsPromises.readdir(imgDirPath, (err) => { + if (err) { + console.log("Import from directory failed: ", err); } - console.log(` `) - - + }); + const imagesName = filesName.filter((fileName) => fileName.includes(".png")); + const imagesData = []; + for await (const imageName of imagesName) { + const imageFilePath = path.join( + path.resolve(__dirname, "../../data"), + "images", + imageName, + ); + const imageData = await fsPromises.readFile(imageFilePath); + imagesData.push(imageData); + } + console.log(`Imported images as buffered data\n`); - // Add the metadata to IPFS - console.log(`Adding metadata to IPFS...`); - let metadataCIDs = []; - let taikoonId = 0 - for await (const imageCID of imageCIDs) { - taikoonId++ - const {cid: metadataCID} = await ipfs.add({ - // NOTE: You can implement different name & descriptions for each metadata - content: JSON.stringify(populateNFTMetadata(`Taikoon ${taikoonId}`, "A Taikoon", imageCID.toString())) - }) + // Uploading images to IPFS + console.log(`Uploading image data to IPFS...`); + const imageCIDs = []; + const imagesSummary = []; + let imageCount = 1; + for await (const imageData of imagesData) { + const { cid: imageCID } = await ipfs.add({ + content: imageData, + }); + imageCIDs.push(imageCID); + imagesSummary.push({ imageCID, imageCount }); + console.log(`Image added to IPFS with CID of ${imageCID}`); + imageCount++; + } + console.log(` `); - // write into a file - fs.writeFileSync( - path.join(path.resolve(__dirname, '../../data'), 'metadata', `${taikoonId}.json`), - JSON.stringify(populateNFTMetadata(`Taikoon ${taikoonId}`, "A Taikoon", imageCID.toString())) - ) - - - console.log(path.join(path.resolve(__dirname, '../../data'), 'metadata', `${taikoonId}.json`)) - metadataCIDs.push(metadataCID); - for (let i = 0; i < imagesSummary.length; i ++) { - if (imagesSummary[i].imageCID == imageCID) { - imagesSummary[i].metadataCID = metadataCID - } - }; - //console.log(`Metadata with image CID ${imageCID} added to IPFS with CID of ${metadataCID}`); - } - console.log(` `); + // Add the metadata to IPFS + console.log(`Adding metadata to IPFS...`); + const metadataCIDs = []; + let taikoonId = 0; + for await (const imageCID of imageCIDs) { + taikoonId++; + const { cid: metadataCID } = await ipfs.add({ + // NOTE: You can implement different name & descriptions for each metadata + content: JSON.stringify( + populateNFTMetadata( + `Taikoon ${taikoonId}`, + "A Taikoon", + imageCID.toString(), + ), + ), + }); + // write into a file fs.writeFileSync( - path.join(path.resolve(__dirname, '../../data'), 'metadata', 'summary.json'), - JSON.stringify({imagesSummary}) - ) + path.join( + path.resolve(__dirname, "../../data"), + "metadata", + `${taikoonId}.json`, + ), + JSON.stringify( + populateNFTMetadata( + `Taikoon ${taikoonId}`, + "A Taikoon", + imageCID.toString(), + ), + ), + ); + console.log( + path.join( + path.resolve(__dirname, "../../data"), + "metadata", + `${taikoonId}.json`, + ), + ); + metadataCIDs.push(metadataCID); + for (let i = 0; i < imagesSummary.length; i++) { + if (imagesSummary[i].imageCID == imageCID) { + imagesSummary[i].metadataCID = metadataCID; + } + } + // console.log(`Metadata with image CID ${imageCID} added to IPFS with CID of ${metadataCID}`); + } + console.log(` `); + fs.writeFileSync( + path.join( + path.resolve(__dirname, "../../data"), + "metadata", + "summary.json", + ), + JSON.stringify({ imagesSummary }), + ); } -main() +main(); diff --git a/packages/taikoon/script/js/generate-merkle-tree.js b/packages/taikoon/script/js/generate-merkle-tree.js index 0b95d156e6..d2655d8672 100644 --- a/packages/taikoon/script/js/generate-merkle-tree.js +++ b/packages/taikoon/script/js/generate-merkle-tree.js @@ -1,35 +1,30 @@ -const { StandardMerkleTree } = require('@openzeppelin/merkle-tree') -const path = require('path') -const fs = require('fs') -const ConvertCsvToJson = require('convert-csv-to-json'); +const { StandardMerkleTree } = require("@openzeppelin/merkle-tree"); +const path = require("path"); +const fs = require("fs"); +const ConvertCsvToJson = require("convert-csv-to-json"); async function main(network) { - const inputFile = path.join(__dirname, `../../data/whitelist/${network}.csv`) - const outputFile = path.join(__dirname, `../../data/whitelist/${network}.json`) - const rawJson = ConvertCsvToJson.fieldDelimiter(',').getJsonFromCsv(inputFile); + const inputFile = path.join(__dirname, `../../data/whitelist/${network}.csv`); + const outputFile = path.join( + __dirname, + `../../data/whitelist/${network}.json`, + ); + const rawJson = + ConvertCsvToJson.fieldDelimiter(",").getJsonFromCsv(inputFile); - const values = rawJson.map((entry) => { - return [ - entry.address, - entry.freeMints, - ] - }) + const values = rawJson.map((entry) => { + return [entry.address, entry.freeMints]; + }); - const tree = StandardMerkleTree.of( - values, - ["address", "uint256"] - ) + const tree = StandardMerkleTree.of(values, ["address", "uint256"]); - fs.writeFileSync( - outputFile, - JSON.stringify( - {...tree.dump(), - root: tree.root - }, null, 2) - ) + fs.writeFileSync( + outputFile, + JSON.stringify({ ...tree.dump(), root: tree.root }, null, 2), + ); - console.log('Merkle Root:', tree.root) + console.log("Merkle Root:", tree.root); } -main('hardhat') -main('holesky') +main("hardhat"); +main("holesky"); diff --git a/packages/taikoon/script/js/resize-images.js b/packages/taikoon/script/js/resize-images.js index 578281607a..e13d171bf8 100644 --- a/packages/taikoon/script/js/resize-images.js +++ b/packages/taikoon/script/js/resize-images.js @@ -1,59 +1,56 @@ -const fs = require('fs') -const path = require('path') -const sharp = require('sharp'); +const fs = require("fs"); +const path = require("path"); +const sharp = require("sharp"); function shuffleArray(array) { - return array /* + return array; /* for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } - return array;*/ - } - -async function main(){ - const taikoonsDir = path.join(__dirname, '../../data/original/') - - const blobs = [] - // iterate over files in the directory - fs.readdir(taikoonsDir, async (err, rawFiles) => { - if (err) { - console.error(err) - return - } - - const files = shuffleArray(rawFiles) - - const finalImageSize = 32 + return array; */ +} - for (const file of files) { - if (!file.endsWith('.png')) continue - const fileIndex = file.split('.')[0] - const sourceFilePath = path.join(taikoonsDir, file) - const destinationFilePath = path.join(__dirname, '../../data/images/', file) +async function main() { + const taikoonsDir = path.join(__dirname, "../../data/original/"); - const sharpImage = sharp(sourceFilePath).resize( - finalImageSize, - finalImageSize, - { - kernel: sharp.kernel.nearest - } - ).png({compressionLevel: 9}) + const blobs = []; + // iterate over files in the directory + fs.readdir(taikoonsDir, async (err, rawFiles) => { + if (err) { + console.error(err); + return; + } + const files = shuffleArray(rawFiles); - const contents = await sharpImage.toBuffer() - await sharpImage.toFile(destinationFilePath) + const finalImageSize = 32; - console.log(`Converted ${file} to 32x32 PNG.`) + for (const file of files) { + if (!file.endsWith(".png")) continue; + const fileIndex = file.split(".")[0]; + const sourceFilePath = path.join(taikoonsDir, file); + const destinationFilePath = path.join( + __dirname, + "../../data/images/", + file, + ); - } - console.log(`Converted ${blobs.length} Taikoon PNG files to 32x32 base64.`) + const sharpImage = sharp(sourceFilePath) + .resize(finalImageSize, finalImageSize, { + kernel: sharp.kernel.nearest, + }) + .png({ compressionLevel: 9 }); - // fs.writeFileSync(path.join(__dirname, '../../data/taikoons-32.json'), JSON.stringify({blobs}, null, 2)) + const contents = await sharpImage.toBuffer(); + await sharpImage.toFile(destinationFilePath); - }) + console.log(`Converted ${file} to 32x32 PNG.`); + } + console.log(`Converted ${blobs.length} Taikoon PNG files to 32x32 base64.`); + // fs.writeFileSync(path.join(__dirname, '../../data/taikoons-32.json'), JSON.stringify({blobs}, null, 2)) + }); } - -main() +main();