113 lines
4.0 KiB
JavaScript
Executable File
113 lines
4.0 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
||
"use strict";
|
||
|
||
const fs = require("fs");
|
||
const path = require("path");
|
||
const readline = require("readline");
|
||
const BN = require("bn.js");
|
||
const { Connection, Keypair, PublicKey, Transaction, sendAndConfirmTransaction, clusterApiUrl } = require("@solana/web3.js");
|
||
const { PROGRAM_VERSION_V3, withRevokeGoverningTokens } = require("@solana/spl-governance");
|
||
|
||
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 loadKeypair(filePath) {
|
||
const arr = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||
return Keypair.fromSecretKey(Uint8Array.from(arr));
|
||
}
|
||
|
||
async function askYes() {
|
||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||
const answer = await new Promise((resolve) =>
|
||
rl.question("Введите YES для отзыва (burn/revoke) governance токенов: ", resolve)
|
||
);
|
||
rl.close();
|
||
return answer.trim() === "YES";
|
||
}
|
||
|
||
async function main() {
|
||
const configPath = process.argv[2]
|
||
? path.resolve(process.argv[2])
|
||
: path.resolve(__dirname, "dao.config.env");
|
||
const realmStr = process.argv[3];
|
||
const mintStr = process.argv[4];
|
||
const targetOwnerStr = process.argv[5];
|
||
const amountStr = process.argv[6] || "1";
|
||
if (!realmStr || !mintStr || !targetOwnerStr) {
|
||
throw new Error(
|
||
"Использование: node scripts/dao/revoke_member_token_full_exec.js <config.env> <realm> <mint> <target_owner_pubkey> [amount]"
|
||
);
|
||
}
|
||
|
||
const cfg = parseEnvConfig(configPath);
|
||
const cluster = cfg.DAO_CLUSTER || "devnet";
|
||
const governanceProgramId = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID);
|
||
const revokeKpPath = cfg.DAO_REVOKE_AUTHORITY_KEYPAIR || cfg.DAO_ISSUER_KEYPAIR;
|
||
if (!revokeKpPath) throw new Error("В конфиге нет DAO_REVOKE_AUTHORITY_KEYPAIR и DAO_ISSUER_KEYPAIR");
|
||
const revokeAuthority = loadKeypair(path.resolve(revokeKpPath));
|
||
|
||
const realm = new PublicKey(realmStr);
|
||
const mint = new PublicKey(mintStr);
|
||
const targetOwner = new PublicKey(targetOwnerStr);
|
||
const amount = new BN(amountStr);
|
||
if (amount.lten(0)) throw new Error("amount должен быть > 0");
|
||
|
||
console.log("============================================================");
|
||
console.log("REVOKE/BURN GOVERNANCE TOKENS");
|
||
console.log("------------------------------------------------------------");
|
||
console.log("Сеть: ", cluster);
|
||
console.log("Governance program: ", governanceProgramId.toBase58());
|
||
console.log("Realm: ", realm.toBase58());
|
||
console.log("Mint: ", mint.toBase58());
|
||
console.log("Target owner: ", targetOwner.toBase58());
|
||
console.log("Amount: ", amount.toString());
|
||
console.log("Revoke authority: ", revokeAuthority.publicKey.toBase58());
|
||
console.log("============================================================");
|
||
|
||
const ok = await askYes();
|
||
if (!ok) {
|
||
console.log("Отменено пользователем.");
|
||
return;
|
||
}
|
||
|
||
const connection = new Connection(clusterApiUrl(cluster), "confirmed");
|
||
const ix = [];
|
||
await withRevokeGoverningTokens(
|
||
ix,
|
||
governanceProgramId,
|
||
PROGRAM_VERSION_V3,
|
||
realm,
|
||
targetOwner,
|
||
mint,
|
||
revokeAuthority.publicKey,
|
||
amount
|
||
);
|
||
|
||
const sig = await sendAndConfirmTransaction(connection, new Transaction().add(...ix), [revokeAuthority], {
|
||
commitment: "confirmed",
|
||
});
|
||
console.log("Готово. Tx:", sig);
|
||
}
|
||
|
||
main().catch((e) => {
|
||
console.error("Ошибка revoke:", e?.message || e);
|
||
process.exit(1);
|
||
});
|
||
|