61 lines
4.4 KiB
JavaScript
61 lines
4.4 KiB
JavaScript
#!/usr/bin/env node
|
|
"use strict";
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const BN = require("bn.js");
|
|
const { Connection, PublicKey, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js");
|
|
const { TOKEN_2022_PROGRAM_ID, getAssociatedTokenAddressSync, createMintToInstruction } = require("@solana/spl-token");
|
|
const {
|
|
PROGRAM_VERSION_V3, Vote, YesNoVote, VoteType,
|
|
withCreateProposal, withInsertTransaction, withSignOffProposal, withCastVote,
|
|
getTokenOwnerRecordAddress, getProposalTransactionAddress
|
|
} = require("@solana/spl-governance");
|
|
const { parseEnvConfig, resolveConfigPath, loadKeypair, clusterUrl, nowStamp, 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 02_propose_vote_mint_nft.js <config.env> <target_wallet> <nft_mint>");
|
|
const cfg = parseEnvConfig(resolveConfigPath(process.argv[2]));
|
|
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 ataExists = (await conn.getAccountInfo(targetAta, "confirmed")) !== null;
|
|
if (!ataExists) throw new Error(`Target ATA not found. Create it first: ${targetAta.toBase58()}`);
|
|
|
|
const ixCreate = [];
|
|
const proposal = await withCreateProposal(ixCreate, govPid, PROGRAM_VERSION_V3, realm, governance, mainTor, `Mint NFT to ${target.toBase58().slice(0,8)}`, "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 = [createMintToInstruction(nftMint, targetAta, governance, 1n, [], TOKEN_2022_PROGRAM_ID)];
|
|
const insertData = mintIx.map(toInstructionData);
|
|
const ixInsert = [];
|
|
const proposalTx = await withInsertTransaction(ixInsert, govPid, PROGRAM_VERSION_V3, governance, proposal, mainTor, main.publicKey, 0, 0, 0, insertData, 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 || "./runs"); fs.mkdirSync(runs, { recursive: true });
|
|
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 rp = path.join(runs, `${nowStamp()}_proposal_mint_${target.toBase58().slice(0,8)}.json`);
|
|
fs.writeFileSync(rp, JSON.stringify(report, null, 2));
|
|
console.log("proposal mint created and voted");
|
|
console.log("report:", rp);
|
|
console.log("execute command:");
|
|
console.log(`node 03_execute_mint_nft.js ${resolveConfigPath(process.argv[2])} ${proposal.toBase58()} ${proposalTx.toBase58()} ${nftMint.toBase58()} ${target.toBase58()}`);
|
|
}
|
|
main().catch((e)=>{console.error(e?.message||e);process.exit(1);});
|