UNPKG

@wttp/site

Version:

Web3 Transfer Protocol (WTTP) - Site Contracts and deployment tools

445 lines • 21.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const config_1 = require("hardhat/config"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const arweave_1 = __importDefault(require("arweave")); (0, config_1.task)("arweave:upload", "Upload site resources to Arweave using official Arweave library") .addOptionalParam("manifestPath", "Path to an existing manifest file", undefined, config_1.types.string) .addOptionalParam("source", "Source directory path (required if generating manifest from config)", undefined, config_1.types.string) .addOptionalParam("wallet", "Path to Arweave wallet JSON file (JWK format)", undefined, config_1.types.string) .addOptionalParam("externalrules", "JSON file path containing external storage rules (for manifest generation)", undefined, config_1.types.string) .addOptionalParam("gaslimit", "Maximum gas price in gwei (for manifest generation)", undefined, config_1.types.float) .addOptionalParam("filelimit", "Maximum file size in MB (for manifest generation)", undefined, config_1.types.float) .addOptionalParam("ignorepattern", "Custom ignore pattern file path or 'none' (for manifest generation)", undefined, config_1.types.string) .addOptionalParam("output", "Output path for manifest file (when generating)", undefined, config_1.types.string) .addFlag("uploadManifest", "Upload the manifest itself to Arweave after updating") .setAction(async (taskArgs, hre) => { const { manifestPath, source, wallet, externalrules, gaslimit, filelimit, ignorepattern, output, uploadManifest, } = taskArgs; const { uploadToArweave } = await Promise.resolve().then(() => __importStar(require("../scripts/uploadToArweave"))); const { loadManifest, saveManifest, generateManifestStandalone } = await Promise.resolve().then(() => __importStar(require("../ethers/generateManifest"))); console.log(`🌐 Arweave Upload Task`); console.log(`🌐 Network: ${hre.network.name}\n`); // Validate wallet if (!wallet) { throw new Error("--wallet parameter is required. Provide path to Arweave wallet JSON file."); } if (!fs_1.default.existsSync(wallet)) { throw new Error(`Arweave wallet file not found: ${wallet}`); } let finalManifestPath; let sourcePath; // Determine if we're using existing manifest or generating new one if (manifestPath) { // Use existing manifest if (!fs_1.default.existsSync(manifestPath)) { throw new Error(`Manifest file not found: ${manifestPath}`); } finalManifestPath = manifestPath; // Try to determine source path from manifest location const manifestDir = path_1.default.dirname(manifestPath); const manifest = loadManifest(manifestPath); // Use provided source or try to infer from manifest sourcePath = source || manifestDir; console.log(`šŸ“‹ Using existing manifest: ${manifestPath}`); console.log(`šŸ“ Source directory: ${sourcePath}`); } else if (source) { // Generate new manifest if (!fs_1.default.existsSync(source)) { throw new Error(`Source directory does not exist: ${source}`); } sourcePath = source; // Build manifest config const config = {}; if (gaslimit !== undefined) { config.gasLimit = parseFloat(gaslimit); } if (filelimit !== undefined) { config.fileLimit = parseFloat(filelimit); } if (ignorepattern) { if (ignorepattern === "none") { config.ignorePattern = "none"; } else { config.ignorePattern = ignorepattern; } } // Load external storage rules if provided if (externalrules) { if (!fs_1.default.existsSync(externalrules)) { throw new Error(`External rules file not found: ${externalrules}`); } try { const rulesJson = fs_1.default.readFileSync(externalrules, "utf-8"); const rules = JSON.parse(rulesJson); config.externalStorageRules = rules; console.log(`šŸ“¦ Loaded ${rules.length} external storage rules`); } catch (error) { throw new Error(`Failed to parse external rules JSON: ${error}`); } } // Determine output path const outputPath = output || path_1.default.join(source, "wttp.manifest.json"); console.log(`šŸ“‹ Generating manifest for: ${source}`); console.log(`šŸ“ Output: ${outputPath}`); // Generate manifest const manifest = await generateManifestStandalone(source, "/", Object.keys(config).length > 0 ? config : undefined); finalManifestPath = path_1.default.resolve(outputPath); const manifestDir = path_1.default.dirname(finalManifestPath); // Save manifest saveManifest(manifest, finalManifestPath); console.log(`āœ… Manifest generated: ${finalManifestPath}`); } else { throw new Error("Either --manifestPath or --source must be provided.\n" + " --manifestPath: Use existing manifest file\n" + " --source: Generate new manifest from directory"); } // Upload to Arweave console.log(`\nšŸš€ Starting Arweave upload...`); const result = await uploadToArweave(finalManifestPath, { walletPath: wallet, sourcePath: sourcePath, uploadManifest: uploadManifest, }); console.log(`\nāœ… Arweave upload task complete!`); console.log(` Files uploaded: ${result.filesUploaded}`); console.log(` Files skipped: ${result.filesSkipped}`); if (result.manifestTxId) { console.log(` Manifest TXID: ${result.manifestTxId}`); } console.log(`\nšŸ“‹ Updated manifest saved to: ${finalManifestPath}`); if (result.filesUploaded > 0) { console.log(`\nšŸ’” Next steps:`); console.log(` 1. Review the updated manifest`); console.log(` 2. Upload redirects to your WTTP site using site:upload`); } }); (0, config_1.task)("arweave:generate", "Generate a new Arweave wallet and save to file") .addOptionalParam("output", "Output filename for the wallet JSON file", "wallet.json", config_1.types.string) .addFlag("force", "Force overwrite existing wallet file (with balance check and warning)") .setAction(async (taskArgs, hre) => { const { output, force } = taskArgs; console.log(`šŸ”‘ Generating Arweave wallet...`); // Initialize Arweave const arweave = arweave_1.default.init({ host: "arweave.net", port: 443, protocol: "https", }); try { // Generate new wallet const key = await arweave.wallets.generate(); // Get address from the key const address = await arweave.wallets.jwkToAddress(key); // Determine output path const outputPath = path_1.default.resolve(output); // Check if file already exists if (fs_1.default.existsSync(outputPath)) { if (!force) { throw new Error(`Wallet file already exists: ${outputPath}\n` + `If you want to overwrite it, use the --force flag.\n` + `āš ļø WARNING: Overwriting will permanently delete the existing wallet!`); } // Force mode: check balance of existing wallet before overwriting console.log(`āš ļø Force mode enabled - existing wallet will be overwritten!`); console.log(`šŸ“‹ Checking balance of existing wallet...`); try { // Load existing wallet const existingWalletKey = JSON.parse(fs_1.default.readFileSync(outputPath, "utf-8")); const existingAddress = await arweave.wallets.jwkToAddress(existingWalletKey); const balanceWinston = await arweave.wallets.getBalance(existingAddress); const balanceAr = arweave.ar.winstonToAr(balanceWinston); console.log(` Address: ${existingAddress}`); console.log(` Balance: ${balanceAr} AR (${balanceWinston} Winston)`); if (parseFloat(balanceWinston) > 0) { console.log(`\n🚨 āš ļø āš ļø āš ļø CRITICAL WARNING āš ļø āš ļø āš ļø 🚨`); console.log(`\n The existing wallet has a NON-ZERO balance!`); console.log(` Address: ${existingAddress}`); console.log(` Balance: ${balanceAr} AR`); console.log(`\n āš ļø OVERWRITING THIS WALLET WILL PERMANENTLY DELETE IT!`); console.log(` āš ļø YOU WILL LOSE ACCESS TO ${balanceAr} AR!`); console.log(` āš ļø THIS CANNOT BE UNDONE!`); console.log(`\n You have 10 seconds to cancel (Ctrl+C)...\n`); // Countdown with warnings for (let i = 10; i > 0; i--) { process.stdout.write(`\r ā±ļø Continuing in ${i} seconds... (Press Ctrl+C to cancel) `); await new Promise(resolve => setTimeout(resolve, 1000)); } console.log(`\n\n āš ļø Proceeding with wallet overwrite...\n`); } else { console.log(`\n ā„¹ļø Existing wallet has zero balance - safe to overwrite.`); } } catch (error) { console.warn(` āš ļø Could not check existing wallet balance: ${error.message}`); console.warn(` Proceeding with overwrite anyway...`); } } // Ensure directory exists const outputDir = path_1.default.dirname(outputPath); if (outputDir !== "." && !fs_1.default.existsSync(outputDir)) { fs_1.default.mkdirSync(outputDir, { recursive: true }); } // Check and update .gitignore const gitignorePath = path_1.default.resolve(".gitignore"); if (fs_1.default.existsSync(gitignorePath)) { const gitignoreContent = fs_1.default.readFileSync(gitignorePath, "utf-8"); const gitignoreLines = gitignoreContent.split("\n").map(line => line.trim()); // Get relative path from workspace root for gitignore const workspaceRoot = process.cwd(); const relativeOutputPath = path_1.default.relative(workspaceRoot, outputPath).replace(/\\/g, "/"); // Check if the file or a matching pattern is already in .gitignore const filename = path_1.default.basename(relativeOutputPath); const isIgnored = gitignoreLines.some(line => { // Skip empty lines and comments if (!line || line.startsWith("#")) return false; // Check for exact match if (line === relativeOutputPath || line === filename) return true; // Check for wildcard patterns (simple check) if (line.includes("*")) { // Convert gitignore pattern to regex-like check const pattern = line.replace(/\*/g, ".*"); try { const regex = new RegExp(`^${pattern}$`); if (regex.test(relativeOutputPath) || regex.test(filename)) { return true; } } catch { // Invalid regex, skip } } return false; }); if (!isIgnored) { // Add to .gitignore const newEntry = `\n# Arweave wallet file\n${relativeOutputPath}\n`; fs_1.default.appendFileSync(gitignorePath, newEntry); console.log(`šŸ“ Added ${relativeOutputPath} to .gitignore`); } } else { // Create .gitignore if it doesn't exist const relativeOutputPath = path_1.default.relative(process.cwd(), outputPath).replace(/\\/g, "/"); fs_1.default.writeFileSync(gitignorePath, `# Arweave wallet file\n${relativeOutputPath}\n`); console.log(`šŸ“ Created .gitignore and added ${relativeOutputPath}`); } // Save wallet to file fs_1.default.writeFileSync(outputPath, JSON.stringify(key, null, 2)); console.log(`āœ… Wallet generated successfully!`); console.log(` Address: ${address}`); console.log(` Saved to: ${outputPath}`); console.log(`\nāš ļø WARNING: Keep this file secure! Anyone with access to this file can control the wallet.`); console.log(` Never commit this file to version control.`); } catch (error) { console.error(`āŒ Failed to generate wallet:`, error.message); throw error; } }); (0, config_1.task)("arweave:address", "Get the Arweave address from a wallet file") .addOptionalParam("wallet", "Path to Arweave wallet JSON file (JWK format)", "wallet.json", config_1.types.string) .setAction(async (taskArgs, hre) => { const { wallet } = taskArgs; console.log(`šŸ” Getting address from wallet...`); // Validate wallet file if (!fs_1.default.existsSync(wallet)) { throw new Error(`Wallet file not found: ${wallet}, to generate a new wallet, run "npx hardhat arweave:generate --output ${wallet}"`); } try { // Initialize Arweave const arweave = arweave_1.default.init({ host: "arweave.net", port: 443, protocol: "https", }); // Load wallet const walletKey = JSON.parse(fs_1.default.readFileSync(wallet, "utf-8")); // Get address const address = await arweave.wallets.jwkToAddress(walletKey); console.log(`āœ… Wallet address: ${address}`); return address; } catch (error) { console.error(`āŒ Failed to get address:`, error.message); throw error; } }); (0, config_1.task)("arweave:balance", "Get the balance of an Arweave wallet or address") .addOptionalParam("wallet", "Path to Arweave wallet JSON file (JWK format)", "wallet.json", config_1.types.string) .addOptionalParam("address", "Arweave address to check balance", undefined, config_1.types.string) .setAction(async (taskArgs, hre) => { const { wallet, address: addressParam } = taskArgs; try { // Initialize Arweave const arweave = arweave_1.default.init({ host: "arweave.net", port: 443, protocol: "https", }); // If both wallet and address are provided, validate they match if (addressParam && fs_1.default.existsSync(wallet)) { const walletKey = JSON.parse(fs_1.default.readFileSync(wallet, "utf-8")); const walletAddress = await arweave.wallets.jwkToAddress(walletKey); if (walletAddress !== addressParam) { throw new Error(`Wallet address mismatch!\n` + ` Wallet file: ${wallet}\n` + ` Wallet address: ${walletAddress}\n` + ` Provided address: ${addressParam}\n` + `These addresses do not match. Please provide either --wallet or --address, not both with mismatched values.`); } } // If address is provided, use it; otherwise use wallet (defaults to wallet.json) const useWallet = !addressParam; let address; if (useWallet) { // Validate wallet file if (!fs_1.default.existsSync(wallet)) { throw new Error(`Wallet file not found: ${wallet}, to generate a new wallet, run "npx hardhat arweave:generate --output ${wallet}"`); } // Load wallet and get address const walletKey = JSON.parse(fs_1.default.readFileSync(wallet, "utf-8")); address = await arweave.wallets.jwkToAddress(walletKey); console.log(`šŸ” Wallet address: ${address}`); } else { address = addressParam; console.log(`šŸ” Checking balance for address: ${address}`); } // Get balance const balanceWinston = await arweave.wallets.getBalance(address); const balanceAr = arweave.ar.winstonToAr(balanceWinston); console.log(`\nšŸ’° Balance:`); console.log(` Winston: ${balanceWinston}`); console.log(` AR: ${balanceAr}`); return { address, winston: balanceWinston, ar: balanceAr, }; } catch (error) { console.error(`āŒ Failed to get balance:`, error.message); throw error; } }); (0, config_1.task)("arweave:send", "Send AR tokens from a wallet to an address") .addParam("wallet", "Path to Arweave wallet JSON file (JWK format)", undefined, config_1.types.string) .addParam("to", "Destination Arweave address", undefined, config_1.types.string) .addParam("amount", "Amount to send in AR (not Winston)", undefined, config_1.types.string) .addFlag("self", "Allow sending to the same address (for testing/debugging)") .setAction(async (taskArgs, hre) => { const { wallet, to, amount, self } = taskArgs; console.log(`šŸ’ø Sending AR transaction...`); // Validate wallet file if (!fs_1.default.existsSync(wallet)) { throw new Error(`Wallet file not found: ${wallet}`); } // Validate amount const amountNum = parseFloat(amount); if (isNaN(amountNum) || amountNum <= 0) { throw new Error(`Invalid amount: ${amount}. Must be a positive number.`); } try { // Initialize Arweave const arweave = arweave_1.default.init({ host: "arweave.net", port: 443, protocol: "https", }); // Load wallet const walletKey = JSON.parse(fs_1.default.readFileSync(wallet, "utf-8")); const fromAddress = await arweave.wallets.jwkToAddress(walletKey); console.log(` From: ${fromAddress}`); console.log(` To: ${to}`); console.log(` Amount: ${amount} AR`); // Check for self-send if (fromAddress === to && !self) { throw new Error(`Cannot send to the same address (${fromAddress}).\n` + `If you intended to send to yourself for testing or debugging, use the --self flag.`); } // Convert AR to Winston const amountWinston = arweave.ar.arToWinston(amount); // Create transaction const transaction = await arweave.createTransaction({ target: to, quantity: amountWinston, }, walletKey); // Sign transaction await arweave.transactions.sign(transaction, walletKey); console.log(`\nšŸ“ Transaction created:`); console.log(` ID: ${transaction.id}`); console.log(` Format: ${transaction.format}`); console.log(` Quantity: ${transaction.quantity} Winston (${amount} AR)`); console.log(` Reward: ${transaction.reward} Winston`); // Submit transaction console.log(`\nšŸ“¤ Submitting transaction...`); const response = await arweave.transactions.post(transaction); if (response.status === 200 || response.status === 208) { console.log(`āœ… Transaction submitted successfully!`); console.log(` Transaction ID: ${transaction.id}`); console.log(` Status: ${response.status === 208 ? "Already posted" : "Posted"}`); console.log(`\nšŸ’” You can view the transaction at:`); console.log(` https://viewblock.io/arweave/tx/${transaction.id}`); } else { throw new Error(`Failed to submit transaction. Status: ${response.status}`); } return { txId: transaction.id, from: fromAddress, to, amount: amount, amountWinston: transaction.quantity, }; } catch (error) { console.error(`āŒ Failed to send transaction:`, error.message); throw error; } }); exports.default = {}; //# sourceMappingURL=arweave.js.map