SHA256
Отключить репосты и добавить Solana-модуль
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const readline = require("readline");
|
||||
const { Keypair, PublicKey, clusterApiUrl } = require("@solana/web3.js");
|
||||
|
||||
function parseEnvConfig(configPath) {
|
||||
const raw = fs.readFileSync(configPath, "utf8");
|
||||
const out = {};
|
||||
for (const line of raw.split("\n")) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith("#")) continue;
|
||||
const eq = trimmed.indexOf("=");
|
||||
if (eq === -1) continue;
|
||||
const key = trimmed.slice(0, eq).trim();
|
||||
let val = trimmed.slice(eq + 1).trim();
|
||||
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) val = val.slice(1, -1);
|
||||
val = val.replace(/\$HOME/g, process.env.HOME || "");
|
||||
out[key] = val;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function assertRequired(cfg, key) {
|
||||
if (!cfg[key]) throw new Error(`В конфиге отсутствует обязательный параметр / Missing config key: ${key}`);
|
||||
}
|
||||
|
||||
function resolveConfigPath(argvPath) {
|
||||
return argvPath ? path.resolve(argvPath) : path.resolve(__dirname, "..", "governance_token.config.env");
|
||||
}
|
||||
|
||||
function loadKeypair(filePath) {
|
||||
const arr = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||||
return Keypair.fromSecretKey(Uint8Array.from(arr));
|
||||
}
|
||||
|
||||
function findSingleJsonFile(dirPath) {
|
||||
const abs = path.resolve(dirPath);
|
||||
if (!fs.existsSync(abs)) throw new Error(`Папка не найдена / Directory not found: ${abs}`);
|
||||
const files = fs.readdirSync(abs).filter((f) => {
|
||||
const p = path.join(abs, f);
|
||||
return fs.statSync(p).isFile() && f.endsWith(".json");
|
||||
});
|
||||
if (files.length !== 1) {
|
||||
throw new Error(`В папке должен быть ровно 1 json-файл / Directory must contain exactly 1 json file: ${abs}. Сейчас: ${files.length}`);
|
||||
}
|
||||
return path.join(abs, files[0]);
|
||||
}
|
||||
|
||||
function saveKeypair(filePath, keypair) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, JSON.stringify(Array.from(keypair.secretKey)));
|
||||
}
|
||||
|
||||
function parseCluster(cluster) {
|
||||
if (cluster === "devnet" || cluster === "mainnet-beta" || cluster === "testnet") return clusterApiUrl(cluster);
|
||||
return cluster;
|
||||
}
|
||||
|
||||
function nowStamp() {
|
||||
const d = new Date();
|
||||
const p = (n) => String(n).padStart(2, "0");
|
||||
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}_${p(d.getHours())}-${p(d.getMinutes())}-${p(d.getSeconds())}`;
|
||||
}
|
||||
|
||||
async function askYes(prompt) {
|
||||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||||
const answer = await new Promise((resolve) => rl.question(prompt, resolve));
|
||||
rl.close();
|
||||
return answer.trim() === "yes";
|
||||
}
|
||||
|
||||
function colors(s, code) { return `\x1b[${code}m${s}\x1b[0m`; }
|
||||
const ui = {
|
||||
info: (s) => console.log(colors(s, "36")),
|
||||
ok: (s) => console.log(colors(s, "32")),
|
||||
warn: (s) => console.log(colors(s, "33")),
|
||||
err: (s) => console.log(colors(s, "31")),
|
||||
title: (s) => console.log(colors(s, "1;35")),
|
||||
};
|
||||
|
||||
function getMintPublicKeyFromConfig(cfg) {
|
||||
if (cfg.GT_MINT_ADDRESS && cfg.GT_MINT_ADDRESS.trim()) return new PublicKey(cfg.GT_MINT_ADDRESS.trim());
|
||||
if (cfg.GT_GOVERNMENT_TOKEN_KEYPAIR_DIR && cfg.GT_GOVERNMENT_TOKEN_KEYPAIR_DIR.trim()) {
|
||||
const kpPath = findSingleJsonFile(path.resolve(cfg.GT_GOVERNMENT_TOKEN_KEYPAIR_DIR));
|
||||
return loadKeypair(kpPath).publicKey;
|
||||
}
|
||||
if (cfg.GT_MINT_KEYPAIR_PATH && cfg.GT_MINT_KEYPAIR_PATH.trim()) return loadKeypair(path.resolve(cfg.GT_MINT_KEYPAIR_PATH)).publicKey;
|
||||
throw new Error("Не задан mint: укажите GT_MINT_ADDRESS или положите 1 keypair в GT_GOVERNMENT_TOKEN_KEYPAIR_DIR");
|
||||
}
|
||||
|
||||
function getOperatorKeypairFromConfig(cfg) {
|
||||
if (cfg.GT_DAO_CREATOR_KEYPAIR_DIR && cfg.GT_DAO_CREATOR_KEYPAIR_DIR.trim()) {
|
||||
const kpPath = findSingleJsonFile(path.resolve(cfg.GT_DAO_CREATOR_KEYPAIR_DIR));
|
||||
return loadKeypair(kpPath);
|
||||
}
|
||||
if (cfg.GT_OPERATOR_KEYPAIR_PATH && cfg.GT_OPERATOR_KEYPAIR_PATH.trim()) {
|
||||
return loadKeypair(path.resolve(cfg.GT_OPERATOR_KEYPAIR_PATH));
|
||||
}
|
||||
throw new Error("Не задан ключ оператора: укажите GT_DAO_CREATOR_KEYPAIR_DIR или GT_OPERATOR_KEYPAIR_PATH");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parseEnvConfig,
|
||||
assertRequired,
|
||||
resolveConfigPath,
|
||||
loadKeypair,
|
||||
findSingleJsonFile,
|
||||
saveKeypair,
|
||||
parseCluster,
|
||||
nowStamp,
|
||||
askYes,
|
||||
ui,
|
||||
getMintPublicKeyFromConfig,
|
||||
getOperatorKeypairFromConfig,
|
||||
};
|
||||
Reference in New Issue
Block a user