Files
SHiNE-server/shine-solana/shine/create-SHiNE-DAO/scripts/03_transfer_dao_authorities.js
T

269 lines
9.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
"use strict";
const fs = require("fs");
const path = require("path");
const readline = require("readline");
const {
Connection,
PublicKey,
Transaction,
sendAndConfirmTransaction,
} = require("@solana/web3.js");
const {
TOKEN_PROGRAM_ID,
AuthorityType,
createSetAuthorityInstruction,
} = require("@solana/spl-token");
const {
PROGRAM_VERSION_V3,
getRealm,
withSetRealmAuthority,
SetRealmAuthorityAction,
} = require("@solana/spl-governance");
const { createUmi } = require("@metaplex-foundation/umi-bundle-defaults");
const {
createSignerFromKeypair,
signerIdentity,
none,
some,
} = require("@metaplex-foundation/umi");
const { fromWeb3JsKeypair, fromWeb3JsPublicKey } = require("@metaplex-foundation/umi-web3js-adapters");
const {
mplTokenMetadata,
fetchMetadataFromSeeds,
updateV1,
} = require("@metaplex-foundation/mpl-token-metadata");
const {
clusterUrl,
ensureDir,
loadKeypair,
nowStamp,
parseEnvConfig,
resolveConfigPath,
} = require("./js_common");
function lamportsToSol(lamports) {
return Number(lamports) / 1_000_000_000;
}
async function askYes() {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const answer = await new Promise((resolve) =>
rl.question("Введите YES для передачи authority на DAO: ", resolve)
);
rl.close();
return answer.trim() === "YES";
}
async function fetchMetadataState(cluster, issuer, mint) {
const umi = createUmi(clusterUrl(cluster));
const issuerSigner = createSignerFromKeypair(umi, fromWeb3JsKeypair(issuer));
umi.use(signerIdentity(issuerSigner));
umi.use(mplTokenMetadata());
const metadata = await fetchMetadataFromSeeds(umi, { mint: fromWeb3JsPublicKey(mint) });
return {
umi,
issuerSigner,
metadata,
};
}
async function fetchMetadataUpdateAuthority(cluster, mint) {
const umi = createUmi(clusterUrl(cluster));
umi.use(mplTokenMetadata());
const metadata = await fetchMetadataFromSeeds(umi, { mint: fromWeb3JsPublicKey(mint) });
return metadata.updateAuthority.toString();
}
async function main() {
const configPath = resolveConfigPath(process.argv[2], "dao.config.env");
if (!fs.existsSync(configPath)) throw new Error(`Конфиг не найден: ${configPath}`);
const cfg = parseEnvConfig(configPath);
[
"DAO_CLUSTER",
"DAO_REALM_NAME",
"DAO_ISSUER_KEYPAIR",
"SPL_GOVERNANCE_PROGRAM_ID",
"DAO_GOV_TOKEN_MINT",
].forEach((k) => {
if (!cfg[k]) throw new Error(`В конфиге отсутствует обязательный параметр: ${k}`);
});
const cluster = cfg.DAO_CLUSTER;
const connection = new Connection(clusterUrl(cluster), "confirmed");
const issuer = loadKeypair(path.resolve(cfg.DAO_ISSUER_KEYPAIR));
const governanceProgramId = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID);
const mint = new PublicKey(cfg.DAO_GOV_TOKEN_MINT);
const [realmPk] = PublicKey.findProgramAddressSync(
[Buffer.from("governance"), Buffer.from(cfg.DAO_REALM_NAME, "utf8")],
governanceProgramId
);
const [governancePk] = PublicKey.findProgramAddressSync(
[Buffer.from("account-governance"), realmPk.toBuffer(), realmPk.toBuffer()],
governanceProgramId
);
const realmInfo = await connection.getAccountInfo(realmPk, "confirmed");
if (!realmInfo) throw new Error(`Realm не найден: ${realmPk.toBase58()}. Сначала выполните шаг 2.`);
const governanceInfo = await connection.getAccountInfo(governancePk, "confirmed");
if (!governanceInfo) {
throw new Error(`Governance PDA не найден: ${governancePk.toBase58()}. Сначала выполните шаг 2.`);
}
const realmBefore = await getRealm(connection, realmPk);
const realmAuthorityBefore = realmBefore.account.authority?.toBase58?.() || null;
if (realmAuthorityBefore !== issuer.publicKey.toBase58()) {
throw new Error(`Realm authority уже не у issuer: ${realmAuthorityBefore}`);
}
const mintInfoBefore = await connection.getParsedAccountInfo(mint, "confirmed");
if (!mintInfoBefore.value) throw new Error(`Mint не найден: ${mint.toBase58()}`);
const mintParsedBefore = mintInfoBefore.value.data.parsed.info;
const mintAuthorityBefore = mintParsedBefore.mintAuthority || null;
const freezeAuthorityBefore = mintParsedBefore.freezeAuthority || null;
if (mintAuthorityBefore && mintAuthorityBefore !== issuer.publicKey.toBase58()) {
throw new Error(`Mint authority уже не у issuer: ${mintAuthorityBefore}`);
}
if (freezeAuthorityBefore && freezeAuthorityBefore !== issuer.publicKey.toBase58()) {
throw new Error(`Freeze authority уже не у issuer: ${freezeAuthorityBefore}`);
}
const { umi, metadata, issuerSigner } = await fetchMetadataState(cluster, issuer, mint);
const metadataUpdateAuthorityBefore = metadata.updateAuthority.toString();
if (metadataUpdateAuthorityBefore !== issuer.publicKey.toBase58()) {
throw new Error(`Metadata update authority уже не у issuer: ${metadataUpdateAuthorityBefore}`);
}
const startBalance = await connection.getBalance(issuer.publicKey, "confirmed");
console.log("============================================================");
console.log("TRANSFER DAO AUTHORITIES");
console.log("------------------------------------------------------------");
console.log("Сеть: ", cluster);
console.log("Realm: ", realmPk.toBase58());
console.log("Governance PDA: ", governancePk.toBase58());
console.log("Issuer: ", issuer.publicKey.toBase58());
console.log("Governance mint: ", mint.toBase58());
console.log("Баланс до: ", `${lamportsToSol(startBalance)} SOL`);
console.log("Mint authority до: ", mintAuthorityBefore || "null");
console.log("Freeze authority до: ", freezeAuthorityBefore || "null");
console.log("Realm authority до: ", realmAuthorityBefore || "null");
console.log("Metadata authority до: ", metadataUpdateAuthorityBefore);
console.log("============================================================");
if (!(await askYes())) {
console.log("Отменено.");
return;
}
const ixTransferAuthorities = [
createSetAuthorityInstruction(
mint,
issuer.publicKey,
AuthorityType.MintTokens,
governancePk,
[],
TOKEN_PROGRAM_ID
),
createSetAuthorityInstruction(
mint,
issuer.publicKey,
AuthorityType.FreezeAccount,
governancePk,
[],
TOKEN_PROGRAM_ID
),
];
const sigTransferAuthorities = await sendAndConfirmTransaction(
connection,
new Transaction().add(...ixTransferAuthorities),
[issuer],
{ commitment: "confirmed" }
);
const ixSetRealmAuthority = [];
withSetRealmAuthority(
ixSetRealmAuthority,
governanceProgramId,
PROGRAM_VERSION_V3,
realmPk,
issuer.publicKey,
governancePk,
SetRealmAuthorityAction.SetChecked
);
const sigSetRealmAuthority = await sendAndConfirmTransaction(
connection,
new Transaction().add(...ixSetRealmAuthority),
[issuer],
{ commitment: "confirmed" }
);
const metadataTx = await updateV1(umi, {
authority: issuerSigner,
mint: fromWeb3JsPublicKey(mint),
newUpdateAuthority: some(fromWeb3JsPublicKey(governancePk)),
data: none(),
primarySaleHappened: none(),
isMutable: none(),
collection: { __kind: "None" },
collectionDetails: { __kind: "None" },
uses: { __kind: "None" },
ruleSet: { __kind: "None" },
authorizationData: none(),
}).sendAndConfirm(umi);
const sigMetadataAuthority = Buffer.from(metadataTx.signature).toString("base64");
const mintInfoAfter = await connection.getParsedAccountInfo(mint, "confirmed");
const mintParsedAfter = mintInfoAfter.value.data.parsed.info;
const mintAuthorityAfter = mintParsedAfter.mintAuthority || null;
const freezeAuthorityAfter = mintParsedAfter.freezeAuthority || null;
const realmAfter = await getRealm(connection, realmPk);
const realmAuthorityAfter = realmAfter.account.authority?.toBase58?.() || null;
const metadataUpdateAuthorityAfter = await fetchMetadataUpdateAuthority(cluster, mint);
const report = {
createdAt: new Date().toISOString(),
cluster,
realmName: cfg.DAO_REALM_NAME,
governanceProgramId: governanceProgramId.toBase58(),
issuer: issuer.publicKey.toBase58(),
governingMint: mint.toBase58(),
realm: realmPk.toBase58(),
governance: governancePk.toBase58(),
mintAuthorityBefore,
freezeAuthorityBefore,
metadataUpdateAuthorityBefore,
realmAuthorityBefore,
mintAuthorityAfter,
freezeAuthorityAfter,
realmAuthorityAfter,
metadataUpdateAuthorityAfter,
txTransferAuthorities: sigTransferAuthorities,
txSetRealmAuthority: sigSetRealmAuthority,
txSetMetadataUpdateAuthority: sigMetadataAuthority,
};
const runsDir = path.resolve(__dirname, "..", "local", "runs");
ensureDir(runsDir);
const reportPath = path.join(runsDir, `${nowStamp()}_transfer_dao_authorities.json`);
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
console.log("Все authority переданы на DAO.");
console.log("realm:", realmPk.toBase58());
console.log("governance:", governancePk.toBase58());
console.log("mintAuthority после:", mintAuthorityAfter || "null");
console.log("freezeAuthority после:", freezeAuthorityAfter || "null");
console.log("realmAuthority после:", realmAuthorityAfter || "null");
console.log("metadataAuthority после:", metadataUpdateAuthorityAfter);
console.log("report:", reportPath);
}
main().catch((e) => {
console.error("TransferAuthorities error:", e?.message || e);
process.exit(1);
});