@coinbase/agentkit
Version:
Coinbase AgentKit core primitives
201 lines (200 loc) • 7.31 kB
JavaScript
;
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;
}
}