UNPKG

@coinbase/agentkit

Version:

Coinbase AgentKit core primitives

201 lines (200 loc) 7.31 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateZoraTokenUri = generateZoraTokenUri; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); /** * Reads a local file and converts it to base64 * * @param imageFileName - Path to the local file * @returns Base64 encoded file and mime type */ async function readFileAsBase64(imageFileName) { return new Promise((resolve, reject) => { fs_1.default.readFile(imageFileName, (err, data) => { if (err) { reject(new Error(`Failed to read file: ${err.message}`)); return; } // Determine mime type based on file extension const extension = path_1.default.extname(imageFileName).toLowerCase(); let mimeType = "application/octet-stream"; // default if (extension === ".png") mimeType = "image/png"; else if (extension === ".jpg" || extension === ".jpeg") mimeType = "image/jpeg"; else if (extension === ".gif") mimeType = "image/gif"; else if (extension === ".svg") mimeType = "image/svg+xml"; const base64 = data.toString("base64"); resolve({ base64, mimeType }); }); }); } /** * Uploads a file to IPFS using Pinata * * @param params - Configuration and file data * @param params.pinataConfig - Pinata configuration including JWT * @param params.fileData - Base64 encoded file data * @param params.fileName - Name for the uploaded file * @param params.mimeType - MIME type of the file * @returns Upload response with CID and other details */ async function uploadFileToIPFS(params) { try { const formData = new FormData(); // Convert base64 to Blob and then to File const byteCharacters = atob(params.fileData); const byteArrays = []; for (let offset = 0; offset < byteCharacters.length; offset += 1024) { const slice = byteCharacters.slice(offset, offset + 1024); const byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } const blob = new Blob(byteArrays, { type: params.mimeType }); const file = new File([blob], params.fileName, { type: params.mimeType }); formData.append("file", file); const pinataMetadata = { name: params.fileName, }; formData.append("pinataMetadata", JSON.stringify(pinataMetadata)); const pinataOptions = { cidVersion: 1, }; formData.append("pinataOptions", JSON.stringify(pinataOptions)); const response = await fetch("https://api.pinata.cloud/pinning/pinFileToIPFS", { method: "POST", headers: { Authorization: `Bearer ${params.pinataConfig.jwt}`, }, body: formData, }); if (!response.ok) { const error = await response.json(); throw new Error(`Failed to upload file to IPFS: ${error.message || response.statusText}`); } const data = await response.json(); return { IpfsHash: data.IpfsHash, PinSize: data.PinSize, Timestamp: data.Timestamp, isDuplicate: data.isDuplicate || false, }; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to upload file to IPFS: ${error.message}`); } throw error; } } /** * Uploads JSON data to IPFS using Pinata * * @param params - Configuration and JSON data * @param params.pinataConfig - Pinata configuration including JWT * @param params.json - JSON data to upload * @returns Upload response with CID and other details */ async function uploadJsonToIPFS(params) { try { const requestBody = { pinataOptions: { cidVersion: 1, }, pinataMetadata: { name: `${params.json.name}-metadata.json`, }, pinataContent: params.json, }; const response = await fetch("https://api.pinata.cloud/pinning/pinJSONToIPFS", { method: "POST", headers: { Authorization: `Bearer ${params.pinataConfig.jwt}`, "Content-Type": "application/json", }, body: JSON.stringify(requestBody), }); if (!response.ok) { const error = await response.json(); throw new Error(`Failed to upload JSON to IPFS: ${error.message || response.statusText}`); } const data = await response.json(); return { IpfsHash: data.IpfsHash, PinSize: data.PinSize, Timestamp: data.Timestamp, isDuplicate: data.isDuplicate || false, }; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to upload JSON to IPFS: ${error.message}`); } throw error; } } /** * Generates a Zora token URI by handling local file or URI * * @param params - Parameters for generating the token URI * @returns A promise that resolves to object containing the IPFS URI */ async function generateZoraTokenUri(params) { try { let imageUri; // Check if image is already a URI (ipfs:// or https://) if (params.image.startsWith("ipfs://") || params.image.startsWith("https://")) { imageUri = params.image; } else { // Handle local file const { base64, mimeType } = await readFileAsBase64(params.image); const fileName = path_1.default.basename(params.image); const imageRes = await uploadFileToIPFS({ pinataConfig: params.pinataConfig, fileData: base64, fileName, mimeType, }); imageUri = `ipfs://${imageRes.IpfsHash}`; } // Create and upload the metadata const metadata = { name: params.name, description: params.description, symbol: params.symbol, image: imageUri, content: { uri: imageUri, mime: imageUri.startsWith("ipfs://") || imageUri.startsWith("https://") ? "image/*" : "image/png", }, properties: { category: params.category || "social", }, }; const metadataRes = await uploadJsonToIPFS({ pinataConfig: params.pinataConfig, json: metadata, }); const uri = `ipfs://${metadataRes.IpfsHash}`; return { uri, imageUri }; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to generate Zora token URI: ${error.message}`); } throw error; } }