SHA256
Подготовка перед деплоем в mainnet
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
#!/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);
|
||||
});
|
||||
Reference in New Issue
Block a user