Files
SHiNE-server/shine-solana/shine/create-SHiNE-DAO/scripts/06_propose_vote_mint_nft.js
T

108 lines
4.5 KiB
JavaScript

#!/usr/bin/env node
"use strict";
const fs = require("fs");
const path = require("path");
const { Connection, PublicKey, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js");
const { TOKEN_2022_PROGRAM_ID, getAssociatedTokenAddressSync, createAssociatedTokenAccountIdempotentInstruction, createMintToInstruction } = require("@solana/spl-token");
const {
PROGRAM_VERSION_V3,
Vote,
YesNoVote,
VoteType,
withCreateProposal,
withInsertTransaction,
withSignOffProposal,
withCastVote,
getTokenOwnerRecordAddress,
getProposalTransactionAddress,
} = require("@solana/spl-governance");
const { clusterUrl, ensureDir, loadKeypair, nowStamp, parseEnvConfig, resolveConfigPath, toInstructionData } = require("./js_common");
async function main() {
const targetWallet = process.argv[3];
const nftMintStr = process.argv[4];
if (!targetWallet || !nftMintStr) throw new Error("Usage: node 06_propose_vote_mint_nft.js <config.env> <target_wallet> <nft_mint>");
const cfg = parseEnvConfig(resolveConfigPath(process.argv[2], "nft-flow.config.env"));
const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed");
const main = loadKeypair(path.resolve(__dirname, "..", cfg.MAIN_KEYPAIR));
const realm = new PublicKey(cfg.REALM);
const governance = new PublicKey(cfg.GOVERNANCE);
const governingMint = new PublicKey(cfg.GOVERNING_MINT);
const govPid = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID);
const nftMint = new PublicKey(nftMintStr);
const target = new PublicKey(targetWallet);
const mainTor = await getTokenOwnerRecordAddress(govPid, realm, governingMint, main.publicKey);
const targetAta = getAssociatedTokenAddressSync(nftMint, target, false, TOKEN_2022_PROGRAM_ID);
const ixCreate = [];
const proposal = await withCreateProposal(
ixCreate,
govPid,
PROGRAM_VERSION_V3,
realm,
governance,
mainTor,
`Mint NFT to ${target.toBase58().slice(0, 8)}`,
cfg.NFT_METADATA_URI || "https://arweave.net/",
governingMint,
main.publicKey,
undefined,
VoteType.SINGLE_CHOICE,
["Approve"],
true,
main.publicKey
);
const txCreate = await sendAndConfirmTransaction(conn, new Transaction().add(...ixCreate), [main], { commitment: "confirmed" });
const mintIx = [
createAssociatedTokenAccountIdempotentInstruction(main.publicKey, targetAta, target, nftMint, TOKEN_2022_PROGRAM_ID),
createMintToInstruction(nftMint, targetAta, governance, 1n, [], TOKEN_2022_PROGRAM_ID),
].map(toInstructionData);
const ixInsert = [];
const proposalTx = await withInsertTransaction(ixInsert, govPid, PROGRAM_VERSION_V3, governance, proposal, mainTor, main.publicKey, 0, 0, 0, mintIx, main.publicKey);
const txInsert = await sendAndConfirmTransaction(conn, new Transaction().add(...ixInsert), [main], { commitment: "confirmed" });
const ixSign = [];
withSignOffProposal(ixSign, govPid, PROGRAM_VERSION_V3, realm, governance, proposal, main.publicKey, undefined, mainTor);
const txSign = await sendAndConfirmTransaction(conn, new Transaction().add(...ixSign), [main], { commitment: "confirmed" });
const vote = Vote.fromYesNoVote(YesNoVote.Yes);
const ixVote1 = [];
await withCastVote(ixVote1, govPid, PROGRAM_VERSION_V3, realm, governance, proposal, mainTor, mainTor, main.publicKey, governingMint, vote, main.publicKey);
const txVote1 = await sendAndConfirmTransaction(conn, new Transaction().add(...ixVote1), [main], { commitment: "confirmed" });
const computedTx = await getProposalTransactionAddress(govPid, PROGRAM_VERSION_V3, proposal, 0, 0);
if (!computedTx.equals(proposalTx)) throw new Error("proposal tx mismatch");
const runs = path.resolve(__dirname, "..", cfg.RUNS_DIR || "./local/runs");
ensureDir(runs);
const report = {
type: "mint_nft",
realm: realm.toBase58(),
governance: governance.toBase58(),
proposal: proposal.toBase58(),
proposalTransaction: proposalTx.toBase58(),
nftMint: nftMint.toBase58(),
targetWallet: target.toBase58(),
targetAta: targetAta.toBase58(),
txCreate,
txInsert,
txSign,
txVote1,
};
const reportPath = path.join(runs, `${nowStamp()}_proposal_mint_${target.toBase58().slice(0, 8)}.json`);
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
console.log("Mint proposal создан и проголосован.");
console.log("proposal:", report.proposal);
console.log("proposalTransaction:", report.proposalTransaction);
console.log("report:", reportPath);
}
main().catch((e) => {
console.error(e?.message || e);
process.exit(1);
});