Files

163 lines
5.8 KiB
JavaScript

#!/usr/bin/env node
"use strict";
const fs = require("fs");
const path = require("path");
const BN = require("bn.js");
const {
Connection,
Keypair,
SystemProgram,
Transaction,
sendAndConfirmTransaction,
} = require("@solana/web3.js");
const {
TOKEN_PROGRAM_ID,
AuthorityType,
getMintLen,
createInitializeMintInstruction,
createSetAuthorityInstruction,
} = require("@solana/spl-token");
const { createUmi } = require("@metaplex-foundation/umi-bundle-defaults");
const { createSignerFromKeypair, signerIdentity, percentAmount, none, some } = require("@metaplex-foundation/umi");
const { fromWeb3JsKeypair, fromWeb3JsPublicKey } = require("@metaplex-foundation/umi-web3js-adapters");
const { mplTokenMetadata, createV1, TokenStandard } = require("@metaplex-foundation/mpl-token-metadata");
const {
PublicKey,
assertRequired,
clusterUrl,
ensureArweaveUri,
ensureDir,
loadKeypair,
nowStamp,
parseEnvConfig,
resolveConfigPath,
} = require("./js_common");
function lamportsToSol(lamports) {
return Number(lamports) / 1_000_000_000;
}
async function attachGovernanceMetadata(cfg, cluster, issuer, mintPubkey, mintKeypair) {
ensureArweaveUri("DAO_GOV_TOKEN_METADATA_URI", cfg.DAO_GOV_TOKEN_METADATA_URI);
ensureArweaveUri("DAO_GOV_TOKEN_IMAGE_URL", cfg.DAO_GOV_TOKEN_IMAGE_URL);
const umi = createUmi(clusterUrl(cluster));
const issuerSigner = createSignerFromKeypair(umi, fromWeb3JsKeypair(issuer));
const mintSigner = createSignerFromKeypair(umi, fromWeb3JsKeypair(mintKeypair));
umi.use(signerIdentity(issuerSigner));
umi.use(mplTokenMetadata());
const builder = createV1(umi, {
mint: mintSigner,
authority: issuerSigner,
payer: issuerSigner,
updateAuthority: issuerSigner,
name: cfg.DAO_GOV_NFT_NAME,
symbol: cfg.DAO_GOV_NFT_SYMBOL,
uri: cfg.DAO_GOV_TOKEN_METADATA_URI,
sellerFeeBasisPoints: percentAmount(0),
tokenStandard: TokenStandard.Fungible,
decimals: some(0),
creators: none(),
collection: none(),
uses: none(),
collectionDetails: none(),
ruleSet: none(),
printSupply: none(),
primarySaleHappened: false,
isMutable: true,
isCollection: false,
splTokenProgram: fromWeb3JsPublicKey(TOKEN_PROGRAM_ID),
});
const result = await builder.sendAndConfirm(umi);
return Buffer.from(result.signature).toString("base64");
}
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_GOV_NFT_NAME",
"DAO_GOV_NFT_SYMBOL",
"DAO_GOV_TOKEN_DESCRIPTION",
"DAO_ISSUER_KEYPAIR",
"SPL_GOVERNANCE_PROGRAM_ID",
"DAO_GOV_TOKEN_METADATA_URI",
"DAO_GOV_TOKEN_IMAGE_URL",
].forEach((k) => assertRequired(cfg, k));
const cluster = cfg.DAO_CLUSTER;
const connection = new Connection(clusterUrl(cluster), "confirmed");
const issuer = loadKeypair(path.resolve(cfg.DAO_ISSUER_KEYPAIR));
const mintKeypair = cfg.DAO_GOV_MINT_KEYPAIR
? loadKeypair(path.resolve(cfg.DAO_GOV_MINT_KEYPAIR))
: Keypair.generate();
const governanceProgramId = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID);
const startBalance = await connection.getBalance(issuer.publicKey, "confirmed");
console.log("============================================================");
console.log("CREATE SHiNE GOVERNANCE MINT");
console.log("------------------------------------------------------------");
console.log("Сеть: ", cluster);
console.log("Mint: ", mintKeypair.publicKey.toBase58());
console.log("Issuer: ", issuer.publicKey.toBase58());
console.log("Governance program:", governanceProgramId.toBase58());
console.log("Баланс до: ", `${lamportsToSol(startBalance)} SOL`);
console.log("Name: ", cfg.DAO_GOV_NFT_NAME);
console.log("Symbol: ", cfg.DAO_GOV_NFT_SYMBOL);
console.log("Metadata URI: ", cfg.DAO_GOV_TOKEN_METADATA_URI);
console.log("Image URL: ", cfg.DAO_GOV_TOKEN_IMAGE_URL);
console.log("============================================================");
const mintLen = getMintLen([]);
const mintRent = await connection.getMinimumBalanceForRentExemption(mintLen);
const txMint = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: issuer.publicKey,
newAccountPubkey: mintKeypair.publicKey,
space: mintLen,
lamports: mintRent,
programId: TOKEN_PROGRAM_ID,
}),
createInitializeMintInstruction(mintKeypair.publicKey, 0, issuer.publicKey, issuer.publicKey, TOKEN_PROGRAM_ID)
);
const sigMint = await sendAndConfirmTransaction(connection, txMint, [issuer, mintKeypair], {
commitment: "confirmed",
});
const sigMetadata = await attachGovernanceMetadata(cfg, cluster, issuer, mintKeypair.publicKey, mintKeypair);
const report = {
createdAt: new Date().toISOString(),
cluster,
mint: mintKeypair.publicKey.toBase58(),
issuer: issuer.publicKey.toBase58(),
mintAuthority: issuer.publicKey.toBase58(),
freezeAuthority: issuer.publicKey.toBase58(),
metadataUpdateAuthority: issuer.publicKey.toBase58(),
governanceProgramId: governanceProgramId.toBase58(),
metadataUri: cfg.DAO_GOV_TOKEN_METADATA_URI,
imageUrl: cfg.DAO_GOV_TOKEN_IMAGE_URL,
txMint: sigMint,
txMetadata: sigMetadata,
};
const runsDir = path.resolve(__dirname, "..", "local", "runs");
ensureDir(runsDir);
const reportPath = path.join(runsDir, `${nowStamp()}_governance_mint.json`);
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
console.log("Готово. Governance mint создан.");
console.log("mint:", mintKeypair.publicKey.toBase58());
console.log("report:", reportPath);
}
main().catch((e) => {
console.error("CreateMint error:", e?.message || e);
process.exit(1);
});