diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/00_prepare_voter2_deposit.js b/shine/scripts/CreateGovernmentNFTAndDAO/00_prepare_voter2_deposit.js new file mode 100644 index 0000000..0e4c5ee --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/00_prepare_voter2_deposit.js @@ -0,0 +1,38 @@ +#!/usr/bin/env node +"use strict"; +const path = require("path"); +const BN = require("bn.js"); +const { Connection, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js"); +const { getAssociatedTokenAddressSync, createAssociatedTokenAccountIdempotentInstruction, createMintToInstruction, TOKEN_PROGRAM_ID } = require("@solana/spl-token"); +const { withDepositGoverningTokens, PROGRAM_VERSION_V3, getTokenOwnerRecordAddress } = require("@solana/spl-governance"); +const { parseEnvConfig, resolveConfigPath, loadKeypair, clusterUrl, PublicKey } = require("./js_common"); + +async function main() { + 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 voter2 = loadKeypair(path.resolve(__dirname, cfg.VOTER2_KEYPAIR)); + const realm = new PublicKey(cfg.REALM); + const mint = new PublicKey(cfg.GOVERNING_MINT); + const govPid = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID); + + const ata = getAssociatedTokenAddressSync(mint, voter2.publicKey, false, TOKEN_PROGRAM_ID); + const ix1 = [ + createAssociatedTokenAccountIdempotentInstruction(main.publicKey, ata, voter2.publicKey, mint, TOKEN_PROGRAM_ID), + createMintToInstruction(mint, ata, main.publicKey, 1n, [], TOKEN_PROGRAM_ID), + ]; + const sigMint = await sendAndConfirmTransaction(conn, new Transaction().add(...ix1), [main], { commitment: "confirmed" }); + + const tor = await getTokenOwnerRecordAddress(govPid, realm, mint, voter2.publicKey); + const ai = await conn.getAccountInfo(tor, "confirmed"); + let sigDeposit = null; + if (!ai) { + const ix2 = []; + await withDepositGoverningTokens(ix2, govPid, PROGRAM_VERSION_V3, realm, ata, mint, voter2.publicKey, main.publicKey, voter2.publicKey, new BN(1), true); + sigDeposit = await sendAndConfirmTransaction(conn, new Transaction().add(...ix2), [main, voter2], { commitment: "confirmed" }); + } + console.log("prepare done"); + console.log("mint tx:", sigMint); + console.log("deposit tx:", sigDeposit || "already exists"); +} +main().catch((e) => { console.error(e?.message || e); process.exit(1); }); diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/01_create_nft_for_wallet_admin.js b/shine/scripts/CreateGovernmentNFTAndDAO/01_create_nft_for_wallet_admin.js new file mode 100644 index 0000000..c2ea03f --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/01_create_nft_for_wallet_admin.js @@ -0,0 +1,56 @@ +#!/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_2022_PROGRAM_ID, + ExtensionType, + getMintLen, + createInitializeMintInstruction, + createInitializePermanentDelegateInstruction, + createInitializeNonTransferableMintInstruction, + getAssociatedTokenAddressSync, + createAssociatedTokenAccountIdempotentInstruction, + createMintToInstruction, + createSetAuthorityInstruction, + AuthorityType, +} = require("@solana/spl-token"); +const { parseEnvConfig, resolveConfigPath, loadKeypair, clusterUrl, nowStamp, PublicKey } = require("./js_common"); + +async function main() { + const target = process.argv[3]; + if (!target) throw new Error("Usage: node 01_create_nft_for_wallet_admin.js "); + 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 governance = new PublicKey(cfg.GOVERNANCE); + const targetPk = new PublicKey(target); + const mint = Keypair.generate(); + const mintLen = getMintLen([ExtensionType.NonTransferable, ExtensionType.PermanentDelegate]); + const rentMint = await conn.getMinimumBalanceForRentExemption(mintLen, "confirmed"); + const ata = getAssociatedTokenAddressSync(mint.publicKey, targetPk, false, TOKEN_2022_PROGRAM_ID); + const tx = new Transaction().add( + SystemProgram.createAccount({ fromPubkey: main.publicKey, newAccountPubkey: mint.publicKey, space: mintLen, lamports: rentMint, programId: TOKEN_2022_PROGRAM_ID }), + createInitializeNonTransferableMintInstruction(mint.publicKey, TOKEN_2022_PROGRAM_ID), + createInitializePermanentDelegateInstruction(mint.publicKey, main.publicKey, TOKEN_2022_PROGRAM_ID), + createInitializeMintInstruction(mint.publicKey, 0, main.publicKey, main.publicKey, TOKEN_2022_PROGRAM_ID), + createAssociatedTokenAccountIdempotentInstruction(main.publicKey, ata, targetPk, mint.publicKey, TOKEN_2022_PROGRAM_ID), + createMintToInstruction(mint.publicKey, ata, main.publicKey, 1n, [], TOKEN_2022_PROGRAM_ID), + createSetAuthorityInstruction(mint.publicKey, main.publicKey, AuthorityType.MintTokens, governance, [], TOKEN_2022_PROGRAM_ID), + createSetAuthorityInstruction(mint.publicKey, main.publicKey, AuthorityType.FreezeAccount, governance, [], TOKEN_2022_PROGRAM_ID), + createSetAuthorityInstruction(mint.publicKey, main.publicKey, AuthorityType.PermanentDelegate, governance, [], TOKEN_2022_PROGRAM_ID) + ); + const sig = await sendAndConfirmTransaction(conn, tx, [main, mint], { commitment: "confirmed" }); + const runs = path.resolve(__dirname, cfg.RUNS_DIR || "./runs"); + fs.mkdirSync(runs, { recursive: true }); + const report = { createdAt: new Date().toISOString(), mint: mint.publicKey.toBase58(), owner: targetPk.toBase58(), ata: ata.toBase58(), tx: sig }; + const rp = path.join(runs, `${nowStamp()}_admin_create_nft_${targetPk.toBase58().slice(0,8)}.json`); + fs.writeFileSync(rp, JSON.stringify(report, null, 2)); + console.log("NFT created and delegated to governance"); + console.log("mint:", report.mint); + console.log("owner:", report.owner); + console.log("report:", rp); +} +main().catch((e)=>{console.error(e?.message||e);process.exit(1);}); diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/01b_create_empty_nft_template.js b/shine/scripts/CreateGovernmentNFTAndDAO/01b_create_empty_nft_template.js new file mode 100644 index 0000000..932fcdc --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/01b_create_empty_nft_template.js @@ -0,0 +1,44 @@ +#!/usr/bin/env node +"use strict"; +const fs = require("fs"); +const path = require("path"); +const { Connection, Keypair, SystemProgram, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js"); +const { + TOKEN_2022_PROGRAM_ID, + ExtensionType, + getMintLen, + createInitializeMintInstruction, + createInitializePermanentDelegateInstruction, + createInitializeNonTransferableMintInstruction, + createSetAuthorityInstruction, + AuthorityType, +} = require("@solana/spl-token"); +const { parseEnvConfig, resolveConfigPath, loadKeypair, clusterUrl, nowStamp, PublicKey } = require("./js_common"); + +async function main() { + 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 governance = new PublicKey(cfg.GOVERNANCE); + const mint = Keypair.generate(); + const mintLen = getMintLen([ExtensionType.NonTransferable, ExtensionType.PermanentDelegate]); + const rentMint = await conn.getMinimumBalanceForRentExemption(mintLen, "confirmed"); + const tx = new Transaction().add( + SystemProgram.createAccount({ fromPubkey: main.publicKey, newAccountPubkey: mint.publicKey, space: mintLen, lamports: rentMint, programId: TOKEN_2022_PROGRAM_ID }), + createInitializeNonTransferableMintInstruction(mint.publicKey, TOKEN_2022_PROGRAM_ID), + createInitializePermanentDelegateInstruction(mint.publicKey, main.publicKey, TOKEN_2022_PROGRAM_ID), + createInitializeMintInstruction(mint.publicKey, 0, main.publicKey, main.publicKey, TOKEN_2022_PROGRAM_ID), + createSetAuthorityInstruction(mint.publicKey, main.publicKey, AuthorityType.MintTokens, governance, [], TOKEN_2022_PROGRAM_ID), + createSetAuthorityInstruction(mint.publicKey, main.publicKey, AuthorityType.FreezeAccount, governance, [], TOKEN_2022_PROGRAM_ID), + createSetAuthorityInstruction(mint.publicKey, main.publicKey, AuthorityType.PermanentDelegate, governance, [], TOKEN_2022_PROGRAM_ID) + ); + const sig = await sendAndConfirmTransaction(conn, tx, [main, mint], { commitment: "confirmed" }); + const runs = path.resolve(__dirname, cfg.RUNS_DIR || "./runs"); + fs.mkdirSync(runs, { recursive: true }); + const rp = path.join(runs, `${nowStamp()}_empty_nft_template.json`); + fs.writeFileSync(rp, JSON.stringify({ mint: mint.publicKey.toBase58(), tx: sig, createdAt: new Date().toISOString() }, null, 2)); + console.log("EMPTY NFT template created"); + console.log("mint:", mint.publicKey.toBase58()); + console.log("report:", rp); +} +main().catch((e)=>{console.error(e?.message||e);process.exit(1);}); diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/02_propose_vote_mint_nft.js b/shine/scripts/CreateGovernmentNFTAndDAO/02_propose_vote_mint_nft.js new file mode 100644 index 0000000..97c8558 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/02_propose_vote_mint_nft.js @@ -0,0 +1,60 @@ +#!/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 "); + 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);}); diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/03_execute_mint_nft.js b/shine/scripts/CreateGovernmentNFTAndDAO/03_execute_mint_nft.js new file mode 100644 index 0000000..b41c00e --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/03_execute_mint_nft.js @@ -0,0 +1,30 @@ +#!/usr/bin/env node +"use strict"; +const { Connection, PublicKey, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js"); +const { TOKEN_2022_PROGRAM_ID, getAssociatedTokenAddressSync, createMintToInstruction } = require("@solana/spl-token"); +const { PROGRAM_VERSION_V3, withExecuteTransaction } = require("@solana/spl-governance"); +const { parseEnvConfig, resolveConfigPath, loadKeypair, clusterUrl, toInstructionData } = require("./js_common"); +const path = require("path"); + +async function main() { + const cfg = parseEnvConfig(resolveConfigPath(process.argv[2])); + const proposal = new PublicKey(process.argv[3]); + const proposalTx = new PublicKey(process.argv[4]); + const nftMint = new PublicKey(process.argv[5]); + const target = new PublicKey(process.argv[6]); + if (!process.argv[6]) throw new Error("Usage: node 03_execute_mint_nft.js "); + const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed"); + const main = loadKeypair(path.resolve(__dirname, cfg.MAIN_KEYPAIR)); + const governance = new PublicKey(cfg.GOVERNANCE); const govPid = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID); + 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 mintIx = [ + createMintToInstruction(nftMint, targetAta, governance, 1n, [], TOKEN_2022_PROGRAM_ID), + ].map(toInstructionData); + const ix = []; + await withExecuteTransaction(ix, govPid, PROGRAM_VERSION_V3, governance, proposal, proposalTx, mintIx); + const sig = await sendAndConfirmTransaction(conn, new Transaction().add(...ix), [main], { commitment: "confirmed" }); + console.log("execute mint done:", sig); +} +main().catch((e)=>{console.error(e?.message||e);process.exit(1);}); diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/04_propose_vote_burn_nft.js b/shine/scripts/CreateGovernmentNFTAndDAO/04_propose_vote_burn_nft.js new file mode 100644 index 0000000..3eb6269 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/04_propose_vote_burn_nft.js @@ -0,0 +1,53 @@ +#!/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, createBurnCheckedInstruction } = require("@solana/spl-token"); +const { + PROGRAM_VERSION_V3, Vote, YesNoVote, VoteType, + withCreateProposal, withInsertTransaction, withSignOffProposal, withCastVote, + getTokenOwnerRecordAddress +} = 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 04_propose_vote_burn_nft.js "); + 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 sourceAta = getAssociatedTokenAddressSync(nftMint, target, false, TOKEN_2022_PROGRAM_ID); + + const ixCreate = []; + const proposal = await withCreateProposal(ixCreate, govPid, PROGRAM_VERSION_V3, realm, governance, mainTor, `Burn NFT ${nftMint.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 burnIx = [toInstructionData(createBurnCheckedInstruction(sourceAta, nftMint, governance, 1n, 0, [], TOKEN_2022_PROGRAM_ID))]; + const ixInsert = []; + const proposalTx = await withInsertTransaction(ixInsert, govPid, PROGRAM_VERSION_V3, governance, proposal, mainTor, main.publicKey, 0, 0, 0, burnIx, 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 runs = path.resolve(__dirname, cfg.RUNS_DIR || "./runs"); fs.mkdirSync(runs, { recursive: true }); + const report = { type: "burn_nft", realm: realm.toBase58(), governance: governance.toBase58(), proposal: proposal.toBase58(), proposalTransaction: proposalTx.toBase58(), nftMint: nftMint.toBase58(), targetWallet: target.toBase58(), sourceAta: sourceAta.toBase58(), txCreate, txInsert, txSign, txVote1 }; + const rp = path.join(runs, `${nowStamp()}_proposal_burn_${target.toBase58().slice(0,8)}.json`); + fs.writeFileSync(rp, JSON.stringify(report, null, 2)); + console.log("proposal burn created and voted"); + console.log("report:", rp); + console.log("execute command:"); + console.log(`node 05_execute_burn_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);}); diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/05_execute_burn_nft.js b/shine/scripts/CreateGovernmentNFTAndDAO/05_execute_burn_nft.js new file mode 100644 index 0000000..d0ebbc9 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/05_execute_burn_nft.js @@ -0,0 +1,26 @@ +#!/usr/bin/env node +"use strict"; +const path = require("path"); +const { Connection, PublicKey, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js"); +const { TOKEN_2022_PROGRAM_ID, getAssociatedTokenAddressSync, createBurnCheckedInstruction } = require("@solana/spl-token"); +const { PROGRAM_VERSION_V3, withExecuteTransaction } = require("@solana/spl-governance"); +const { parseEnvConfig, resolveConfigPath, loadKeypair, clusterUrl, toInstructionData } = require("./js_common"); + +async function main() { + const cfg = parseEnvConfig(resolveConfigPath(process.argv[2])); + const proposal = new PublicKey(process.argv[3]); + const proposalTx = new PublicKey(process.argv[4]); + const nftMint = new PublicKey(process.argv[5]); + const target = new PublicKey(process.argv[6]); + if (!process.argv[6]) throw new Error("Usage: node 05_execute_burn_nft.js "); + const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed"); + const main = loadKeypair(path.resolve(__dirname, cfg.MAIN_KEYPAIR)); + const governance = new PublicKey(cfg.GOVERNANCE); const govPid = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID); + const sourceAta = getAssociatedTokenAddressSync(nftMint, target, false, TOKEN_2022_PROGRAM_ID); + const burnIx = [toInstructionData(createBurnCheckedInstruction(sourceAta, nftMint, governance, 1n, 0, [], TOKEN_2022_PROGRAM_ID))]; + const ix = []; + await withExecuteTransaction(ix, govPid, PROGRAM_VERSION_V3, governance, proposal, proposalTx, burnIx); + const sig = await sendAndConfirmTransaction(conn, new Transaction().add(...ix), [main], { commitment: "confirmed" }); + console.log("execute burn done:", sig); +} +main().catch((e)=>{console.error(e?.message||e);process.exit(1);}); diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/README.md b/shine/scripts/CreateGovernmentNFTAndDAO/README.md new file mode 100644 index 0000000..20abefe --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/README.md @@ -0,0 +1,66 @@ +# CreateGovernmentNFTAndDAO + +## RU + +Скрипты для Devnet, чтобы управлять NFT через DAO (Realms/SPL Governance): +1) создать предложение на выпуск NFT (`mint`) и выполнить его; +2) создать предложение на сжигание NFT (`burn`) и выполнить его. + +### Что лежит в папке + +- `config.env` — параметры кластера, DAO, ключей. +- `keypairs/` — ключи оператора и второго участника. +- `runs/` — отчёты запусков (proposal, tx и т.д.). +- `00_prepare_voter2_deposit.js` — депонирование governance-токена для второго голосующего. +- `01_create_nft_for_wallet_admin.js` — создать NFT на кошелёк и делегировать право governance PDA. +- `01b_create_empty_nft_template.js` — создать пустой NFT mint-шаблон (supply=0) для будущего DAO mint. +- `02_propose_vote_mint_nft.js` — создать+подписать+проголосовать за proposal на mint. +- `03_execute_mint_nft.js` — выполнить proposal mint. +- `04_propose_vote_burn_nft.js` — создать+подписать+проголосовать за proposal на burn. +- `05_execute_burn_nft.js` — выполнить proposal burn. + +### Важно перед запуском + +1. Нужен `node`, `@solana/web3.js`, `@solana/spl-token`, `@solana/spl-governance`. +2. В `config.env` должен быть корректный `REALM`, `GOVERNANCE`, `GOVERNING_MINT`, `MAIN_KEYPAIR`. +3. Для `mint via DAO` целевой ATA должен существовать заранее (скрипт `02` это проверяет). + +### Быстрый полный тест (mint + burn) + +1. Создать NFT-шаблон (куда DAO будет минтить): + - `node 01b_create_empty_nft_template.js ./config.env` +2. Создать ATA для целевого кошелька и этого mint (если ещё нет). +3. Поднять proposal на mint: + - `node 02_propose_vote_mint_nft.js ./config.env ` +4. Выполнить proposal (команду берёшь из консоли шага 3): + - `node 03_execute_mint_nft.js ./config.env ` +5. Создать NFT для burn-теста: + - `node 01_create_nft_for_wallet_admin.js ./config.env ` +6. Поднять proposal на burn: + - `node 04_propose_vote_burn_nft.js ./config.env ` +7. Выполнить proposal burn (команда из шага 6): + - `node 05_execute_burn_nft.js ./config.env ` + +### Как проверить результат + +Смотри JSON-отчёты в `runs/`: там есть `proposal`, `proposalTransaction`, tx подписи и mint/кошельки. + +Для проверки через час: +1) поднимаешь proposal (скрипт `02` или `04`); +2) ждёшь; +3) запускаешь соответствующий `execute` скрипт с параметрами из отчёта. + +### Проверка DAO + +В текущем `config.env`: +- Realm: `2DTh1ivaekAW8kRYzGPsL2taFLJFFkBjEwqPisebxsS7` +- Governance PDA: `EMZ8vmr1xB4HZBDCFL9rHB98m1C5cYrGnRA8ZHayyGwD` +- Governing mint: `F1KctLRvVzqwcBYNGsivnjR39gY8Uvq5U3uyaqEBNASg` + +## EN + +Devnet scripts for DAO-governed NFT flow (Realms/SPL Governance): +- propose/sign/vote/execute NFT mint to a wallet; +- propose/sign/vote/execute NFT burn from a wallet. + +Main idea: first script in each pair creates proposal and vote, second script executes proposal later. diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/TEMP_DEVNET_REPORT_2026-05-15.md b/shine/scripts/CreateGovernmentNFTAndDAO/TEMP_DEVNET_REPORT_2026-05-15.md new file mode 100644 index 0000000..533a6d8 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/TEMP_DEVNET_REPORT_2026-05-15.md @@ -0,0 +1,37 @@ +# TEMP Devnet Report (2026-05-15) + +## Актуальный DAO из config.env + +- Realm: `2DTh1ivaekAW8kRYzGPsL2taFLJFFkBjEwqPisebxsS7` +- Governance PDA: `EMZ8vmr1xB4HZBDCFL9rHB98m1C5cYrGnRA8ZHayyGwD` +- Governing mint: `F1KctLRvVzqwcBYNGsivnjR39gY8Uvq5U3uyaqEBNASg` + +## Подтверждённый успешный цикл (mint + burn через DAO) + +- NFT minted by DAO: + - mint: `4xU8omSH3RfTyDHxWVCEm1HVTcf97YWkT8H67GvVpssz` + - target ATA: `2fUHqDDdcCcxyG62dZwyNiT2H9xZc3Rgy9oFtWiTdCXr` + - result: `amount=1`, `supply=1` +- NFT burned by DAO: + - mint: `9Up5SURRoBybsrfZnR7nKZC5gHarrccdVuMzoxeU3Xia` + - source ATA: `3Q3cfBrNgyagEpMNPEZngYpCgw9U8Bd2KK4BbBfzvL5u` + - result: `amount=0`, `supply=0` + +Стоимость этого полного цикла: **`0.22709372 SOL`**. + +## Тест "запустить сейчас, проверить/выполнить через час" + +Создан новый proposal на mint (уже signed + voted): +- report: `runs/2026-05-15_18-29-59_proposal_mint_HMww7YSV.json` +- proposal: `CKV2RSJ4HiUGQvdhGyizub89csTF52TD2Z2HciQFPkdW` +- proposalTx: `AAvEV3q9MeHedbJMsTk1C8nLg3LJtTLUBWt9fgi2wnqC` +- nft mint: `hssoT46Vp7KzisNAffBSQxGLmtxfzcswVeeEi4eq8gW` +- target wallet: `HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA` + +Команда для выполнения через час: + +```bash +node 03_execute_mint_nft.js ./config.env CKV2RSJ4HiUGQvdhGyizub89csTF52TD2Z2HciQFPkdW AAvEV3q9MeHedbJMsTk1C8nLg3LJtTLUBWt9fgi2wnqC hssoT46Vp7KzisNAffBSQxGLmtxfzcswVeeEi4eq8gW HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA +``` + +Если execute пройдёт успешно — DAO путь для mint подтверждён повторно. diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/config.env b/shine/scripts/CreateGovernmentNFTAndDAO/config.env new file mode 100644 index 0000000..9092cdf --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/config.env @@ -0,0 +1,13 @@ +CLUSTER="devnet" +SPL_GOVERNANCE_PROGRAM_ID="GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw" + +# DAO for NFT governance flow (created 2026-05-15, single-voter supply) +REALM="2DTh1ivaekAW8kRYzGPsL2taFLJFFkBjEwqPisebxsS7" +GOVERNANCE="EMZ8vmr1xB4HZBDCFL9rHB98m1C5cYrGnRA8ZHayyGwD" +GOVERNING_MINT="F1KctLRvVzqwcBYNGsivnjR39gY8Uvq5U3uyaqEBNASg" + +# voters +MAIN_KEYPAIR="./keypairs/main_FUc28.json" +VOTER2_KEYPAIR="./keypairs/voter2_HMww7.json" + +RUNS_DIR="./runs" diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/js_common.js b/shine/scripts/CreateGovernmentNFTAndDAO/js_common.js new file mode 100644 index 0000000..8fcdd53 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/js_common.js @@ -0,0 +1,50 @@ +#!/usr/bin/env node +"use strict"; +const fs = require("fs"); +const path = require("path"); +const { PublicKey, Keypair, clusterApiUrl } = require("@solana/web3.js"); +const { InstructionData, AccountMetaData } = require("@solana/spl-governance"); + +function parseEnvConfig(configPath) { + const raw = fs.readFileSync(configPath, "utf8"); + const out = {}; + for (const line of raw.split("\n")) { + const t = line.trim(); + if (!t || t.startsWith("#")) continue; + const i = t.indexOf("="); + if (i < 0) continue; + const k = t.slice(0, i).trim(); + let v = t.slice(i + 1).trim(); + if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1); + out[k] = v; + } + return out; +} + +function resolveConfigPath(argvPath) { + return argvPath ? path.resolve(argvPath) : path.resolve(__dirname, "config.env"); +} + +function loadKeypair(fp) { + return Keypair.fromSecretKey(Uint8Array.from(JSON.parse(fs.readFileSync(fp, "utf8")))); +} + +function clusterUrl(cluster) { + if (cluster === "devnet" || cluster === "mainnet-beta" || cluster === "testnet") return clusterApiUrl(cluster); + return cluster; +} + +function nowStamp() { + const d = new Date(); const p = (n) => String(n).padStart(2, "0"); + return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}_${p(d.getHours())}-${p(d.getMinutes())}-${p(d.getSeconds())}`; +} + +function toInstructionData(ix) { + return new InstructionData({ + programId: ix.programId, + accounts: ix.keys.map((k) => new AccountMetaData({ pubkey: k.pubkey, isSigner: !!k.isSigner, isWritable: !!k.isWritable })), + data: Uint8Array.from(ix.data), + }); +} + +module.exports = { parseEnvConfig, resolveConfigPath, loadKeypair, clusterUrl, nowStamp, toInstructionData, PublicKey }; diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/keypairs/main_FUc28.json b/shine/scripts/CreateGovernmentNFTAndDAO/keypairs/main_FUc28.json new file mode 100644 index 0000000..3d11ee7 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/keypairs/main_FUc28.json @@ -0,0 +1 @@ +[221,119,143,125,90,136,155,115,191,198,210,85,228,111,251,118,168,138,27,60,249,62,247,24,121,228,139,112,218,69,55,143,215,21,229,69,219,1,74,36,10,239,63,163,48,240,58,208,237,251,209,37,17,202,215,77,13,165,178,18,141,21,193,64] \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/keypairs/voter2_HMww7.json b/shine/scripts/CreateGovernmentNFTAndDAO/keypairs/voter2_HMww7.json new file mode 100644 index 0000000..f2beade --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/keypairs/voter2_HMww7.json @@ -0,0 +1 @@ +[241,27,187,46,105,28,241,60,120,167,90,61,230,106,125,146,230,244,198,39,162,46,76,224,131,59,229,157,27,45,29,78,243,24,184,76,185,238,209,112,35,54,121,13,2,104,76,182,80,33,144,21,30,188,173,23,63,146,192,200,40,126,139,145] \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-06-41_admin_create_nft_FUc28vNi.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-06-41_admin_create_nft_FUc28vNi.json new file mode 100644 index 0000000..413ba95 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-06-41_admin_create_nft_FUc28vNi.json @@ -0,0 +1,7 @@ +{ + "createdAt": "2026-05-15T15:06:41.395Z", + "mint": "3HJQNtkpP4pv1uKe1XDWRkp1KjY5qmEMEZqzNwDBHuph", + "owner": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "ata": "CHr24KW5RY3hR1dnQATEMq9ZxFuqorDmoc95TZsmJLQG", + "tx": "5nSjS43xRUdVJH4V5xZygcdUjzz1dFUJBVQc4rQwyZZAm7juGeVtKdWHrhb8tXLLcnZmKasZWPAqfqQpXWYtqhs" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-06-48_empty_nft_template.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-06-48_empty_nft_template.json new file mode 100644 index 0000000..93ec320 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-06-48_empty_nft_template.json @@ -0,0 +1,5 @@ +{ + "mint": "HgbScrAw5HN8dx5VG9w5JvGQqgXvinbLwL48VvVfBS6d", + "tx": "4Zk2FBjiEaP9C1kVdADkFX3gD12Lsw9vF7X3E3wYH58CN1fGceU2vnEYE8c3kcevAFeqw7LEr4JPxMbkU7ujREwU", + "createdAt": "2026-05-15T15:06:48.909Z" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-07-00_proposal_mint_HMww7YSV.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-07-00_proposal_mint_HMww7YSV.json new file mode 100644 index 0000000..d5a02a2 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-07-00_proposal_mint_HMww7YSV.json @@ -0,0 +1,14 @@ +{ + "type": "mint_nft", + "realm": "H35UPU98sC2bo265Q4R6PWdvbaotbbCvjDcuvwjDvewf", + "governance": "9aD18P5nun1RPVpEeeCeG5ensyry9WKrwjdX4stVa7qP", + "proposal": "FE5Bmd1ojeSNL9mVYP9E5EgjqMKwpGeaj5fjXBuh1yUB", + "proposalTransaction": "AxCuhhgk1DjYMkijePe3fpr6t9LCb8bNaYq1ksEbKKa1", + "nftMint": "HgbScrAw5HN8dx5VG9w5JvGQqgXvinbLwL48VvVfBS6d", + "targetWallet": "HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA", + "targetAta": "65FUT9jtrscCCTjsxd3z1DMwG4S1Yq2Sd9ZupcocpoK", + "txCreate": "4iu3DmStaP94J3rZ3mmCNpv4XycLa82PyXj8HEyijUiX3BDcrjaL6GQahjbUjLhXaREz1RiGNTsq8ChDGtJjFTMp", + "txInsert": "kiWnMQGvVwckLXrjPVNVStKQyXyHMhAn2pY9tSeyeW898zat84KUCYQrDDkhs6pLe5PrczUPViGVCDNzFiMe16w", + "txSign": "52dD55YaitkKLQmASeJDPiy64eeGTWiNktDTAcTEB4u7cVisxtoVGPyPbatEKDbcMZKkUBc7VMtBuDz7xefMyfn5", + "txVote1": "hApj3uocwAep4a58GSqsVW2vt7bwPZMawfPRs3hwNvYNEtQBWrcW32eGPWuPYjtcVunbxizy5kDXe8QwMiA4h8k" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-08-51_create_dao.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-08-51_create_dao.json new file mode 100644 index 0000000..e6389a3 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-08-51_create_dao.json @@ -0,0 +1,16 @@ +{ + "createdAt": "2026-05-15T15:08:51.409Z", + "cluster": "devnet", + "realmName": "NFT-DAO-0515-E", + "governanceProgramId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", + "governingMint": "2yf1CwPzdBoDM7a376CGSfyEAf58CHtA4rZRGwNeUTNd", + "operator": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "realm": "8yRYrnMWTrhHieegyGniMzskwPFZTka8JNZeKppoD6Zw", + "governance": "8V676kt5LAY3CawCDsbpLheTds9RPJSn6CdwpHew6GD6", + "nativeTreasury": "642nn5B6nvsHrTnM75Jb8wW4JFye11n37Mzc3u4b7GNz", + "tokenOwnerRecord": "EfW4fSPKqXcWYEEYZWXY1Nk3FtmaE1VJij8uZNjnCUwR", + "txRealm": "A6Rmb6jKDKrx5mc3Lge8iPe2nJMNpoczw7mmpQBS9Z7131viztzzGtx1z3xwzNfh1nU5rDekyLrcCn4ZpkDF6kE", + "txDeposit": "4hk68TjzKVrnpjse2SUSXuCzQfYEHUr2jvKVuUGDFABkvrKFHopfuDvcrsWwKoKCS9crM9oXnUy1asnPTAsZya69", + "txGovernanceTreasury": "4gGjGwjrPzvWYG7unBbWQrrCRcfuRiD7E6FQBGv6WcJtnuerPgo9nCWo6dygNEAeWPpYQRDRA5sjmKwDBc2yWUDM", + "txSetRealmAuthority": "W6zWxPKAge7Nu4o9uUiL5AxKYBuhc1g6C8BtqQrhqdtTgYPNxFwZDpvFsm1WEEM1ewhxUqn5qHWvhJDDtrPXvPc" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-09-35_create_dao.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-09-35_create_dao.json new file mode 100644 index 0000000..d329006 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-09-35_create_dao.json @@ -0,0 +1,16 @@ +{ + "createdAt": "2026-05-15T15:09:35.073Z", + "cluster": "devnet", + "realmName": "NFT-DAO-0515-F", + "governanceProgramId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", + "governingMint": "F1KctLRvVzqwcBYNGsivnjR39gY8Uvq5U3uyaqEBNASg", + "operator": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "realm": "2DTh1ivaekAW8kRYzGPsL2taFLJFFkBjEwqPisebxsS7", + "governance": "EMZ8vmr1xB4HZBDCFL9rHB98m1C5cYrGnRA8ZHayyGwD", + "nativeTreasury": "3L9neAHgGb158ieWs7LBbHUaH3qrSN6AnLGPmNNpF5Mq", + "tokenOwnerRecord": "94JCGgmCxoen3JfN75rV3DRHpJ5QA7TaisW8Y9CnwvcY", + "txRealm": "2j2cDJ6joMn6XuRv4sttm7fbAsSRTh8e17TCSTEpV7k1LSFYnRHTmccCVew2w86vWc7gQCJnZKXzJTrRxeopXqAR", + "txDeposit": "4ccx97DF3ezqpFaUhLLE1n9L7HwFibYyv8mwnPcE5EGQJnL4Tq61vUa79ES1mD7qJrak6kwGTeZa1ELVSJy3R6Wi", + "txGovernanceTreasury": "66drnSSkhB4ES6nFMSdQbAK9khMo469c6DwA71XJU2jzUKog1UgzYZ99d3SBCsRHpJhakT7J71xmP2MvgfTiuGun", + "txSetRealmAuthority": "2zsYuJd1nGTPoUenS4eDeVxwHrEtTLss4hBRBjcciZm8Xv3zsMqaJwGnHUETNquPijeUAcn4vn9EDHpAfGTzAA4b" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-09-52_admin_create_nft_FUc28vNi.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-09-52_admin_create_nft_FUc28vNi.json new file mode 100644 index 0000000..0bc660b --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-09-52_admin_create_nft_FUc28vNi.json @@ -0,0 +1,7 @@ +{ + "createdAt": "2026-05-15T15:09:52.941Z", + "mint": "744E11uXxhuy2AwVcrYwrC6yeF5GzEtmcL6Z9o6NzZV", + "owner": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "ata": "8xQnr4dh3aYpnePn39qsA3EC6uFEMBa5eNptCjMd2ufZ", + "tx": "2KiztENxtLJz61A9T8seFWJS5BcJQn3oQr5jL6WtRPWckG26vPrjv8TRurqVVusA8qkXBBW2Zk7ZydP1G5PFY5Zz" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-09-52_empty_nft_template.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-09-52_empty_nft_template.json new file mode 100644 index 0000000..35b4a51 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-09-52_empty_nft_template.json @@ -0,0 +1,5 @@ +{ + "mint": "G4UewUZ3M7eohHGYMMmASbsxyi62ovdb3PcvvWQLNWj", + "tx": "4pgepoc4mCxJxg73JWg9Dqxa96Y6owbSFXjbvAE9J4g2SXRBhPvphFCb5WT8MTTQYNzMQdweuTta9vr7VfBmT6DL", + "createdAt": "2026-05-15T15:09:52.455Z" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-10-02_proposal_mint_HMww7YSV.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-10-02_proposal_mint_HMww7YSV.json new file mode 100644 index 0000000..2c7cf4f --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-10-02_proposal_mint_HMww7YSV.json @@ -0,0 +1,14 @@ +{ + "type": "mint_nft", + "realm": "2DTh1ivaekAW8kRYzGPsL2taFLJFFkBjEwqPisebxsS7", + "governance": "EMZ8vmr1xB4HZBDCFL9rHB98m1C5cYrGnRA8ZHayyGwD", + "proposal": "HfUPpCUyN4f9fydw7zPQf8EmhLFxsarF3oLQ54hRkHmP", + "proposalTransaction": "QDoGm745Evej1B78qAyCjGEosskxQJC77hGBiDhKNnS", + "nftMint": "G4UewUZ3M7eohHGYMMmASbsxyi62ovdb3PcvvWQLNWj", + "targetWallet": "HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA", + "targetAta": "q9Q74ajue4S5qr7xXX1fz3PbkQ9wugQfW4NBnjtjKKZ", + "txCreate": "7WaFb6kzibkUMXyTVrahxuu12ydCdCcDU6bYgWP6juA6uYhEoWtMXrYAR5J8pBDMbx89T5pqrTdHyC1pmiR8mmi", + "txInsert": "5EedBVP48q13RySrGMxdhwrMqXa59bwCYLcUL1EZykDJbecDyoFagDZsEjHosGUeTYepzRDXqSkb7S2ZykcsWAeP", + "txSign": "3VsA4wwyervDjaAaw1eU72mvuvZorDCEbyczUJnvSemcfWrthSYM8VPV3EzWLAd7hhT2wZKkrDXQRk3nrswg15ri", + "txVote1": "66CzrV93qqccquAJt3Px2AFDH16Qkk6rJkCTDZkuLWnqb5GHs6ff1g944g9xtjNMpSiBz9YLwXUULSykuMB52f9v" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-10-59_proposal_mint_HMww7YSV.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-10-59_proposal_mint_HMww7YSV.json new file mode 100644 index 0000000..eb7d318 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-10-59_proposal_mint_HMww7YSV.json @@ -0,0 +1,14 @@ +{ + "type": "mint_nft", + "realm": "2DTh1ivaekAW8kRYzGPsL2taFLJFFkBjEwqPisebxsS7", + "governance": "EMZ8vmr1xB4HZBDCFL9rHB98m1C5cYrGnRA8ZHayyGwD", + "proposal": "GcSrXHmxBNxGskXeE16e9EX7GgWtAP3X6x2DCuTcDdp8", + "proposalTransaction": "Hgjnsd8qJxrYbtemsHLyerBjgyHpK55RhoSftW4HvwXR", + "nftMint": "G4UewUZ3M7eohHGYMMmASbsxyi62ovdb3PcvvWQLNWj", + "targetWallet": "HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA", + "targetAta": "q9Q74ajue4S5qr7xXX1fz3PbkQ9wugQfW4NBnjtjKKZ", + "txCreate": "4WmFfSry92sv5GnMXHQiL28NERQ8j7jcrc2sQSHANaznLePwwSfeMQFVnxvdK2TPwr2dQ4uAJt1ARNCyuVhR5AH2", + "txInsert": "2iKF1EbRKatnpz566WmShHyUcYHkvKhMtBvdFTtoqKoUrrKwSCfFGmv6UxD998hGCuCmJ5sgtzR7aQQNcPTQrJdf", + "txSign": "4iZHCAMxoJMPmzqNKccf48Qhg1kBV7eurdSQ3cGsALihGZK8nxE4vrCyyUQiTJffGKBc4KZNTSSxphQsGmFMSWen", + "txVote1": "2jxbmxUFSc9CkwiXhE3gnMXS6w83ZdXYSMmfzDUv3PYHSnWQUAttbFQMixmUiho1Gs2XgostMqvKJ2AVL8rAec51" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-11-21_proposal_burn_FUc28vNi.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-11-21_proposal_burn_FUc28vNi.json new file mode 100644 index 0000000..4b6e698 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-11-21_proposal_burn_FUc28vNi.json @@ -0,0 +1,14 @@ +{ + "type": "burn_nft", + "realm": "2DTh1ivaekAW8kRYzGPsL2taFLJFFkBjEwqPisebxsS7", + "governance": "EMZ8vmr1xB4HZBDCFL9rHB98m1C5cYrGnRA8ZHayyGwD", + "proposal": "BYuwup4JQUEXcigREwwMoHW1TKvDM29ohmjXDGwZfb47", + "proposalTransaction": "6TZdVa1dgN5hdWvW2rmZTMhYHLCP2GddeNttdhTvpu32", + "nftMint": "744E11uXxhuy2AwVcrYwrC6yeF5GzEtmcL6Z9o6NzZV", + "targetWallet": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "sourceAta": "8xQnr4dh3aYpnePn39qsA3EC6uFEMBa5eNptCjMd2ufZ", + "txCreate": "4pzzFSnd6F89VriF8HaUjoR2zvtLxxvKRU1UBrYy9capswmrsjx9DkmY5aHAaUgSrVM34H49G8UmV5CicPbfScem", + "txInsert": "4E5FggG9dSPcYXiaHZLiMuw54QMzAFFbom48NfxxR2SfsifGZ9SBbNb7sthohcG8nMALUCxoZiNGw3Nsvogf2163", + "txSign": "4CidBfaWqX3FWtABRgELhHdH99TcKHtiDiakwDx129ZZ8j9kSvsrnAQsFiZWeTabaWDFnzm3dB6EQ8HcUTS4XeL5", + "txVote1": "VkiFThKY1W41Uy8baxWoz4vBgJQZNMGDwewKxTbMsbFqQHAejztFreKmdryesCHnv4h3PPejBkV3EmV37qQLdA7" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-04_admin_create_nft_FUc28vNi.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-04_admin_create_nft_FUc28vNi.json new file mode 100644 index 0000000..ad8ebc5 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-04_admin_create_nft_FUc28vNi.json @@ -0,0 +1,7 @@ +{ + "createdAt": "2026-05-15T15:14:04.947Z", + "mint": "9Up5SURRoBybsrfZnR7nKZC5gHarrccdVuMzoxeU3Xia", + "owner": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "ata": "3Q3cfBrNgyagEpMNPEZngYpCgw9U8Bd2KK4BbBfzvL5u", + "tx": "NvaRG6LUNf4RqG5J4uZzKHkKSkyACWTPYC9WDGovdFedUjUR3JA2Wbfhx9H6X35iXkuMz6jfjqMq2jHHo3FHdfy" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-04_empty_nft_template.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-04_empty_nft_template.json new file mode 100644 index 0000000..cc05daa --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-04_empty_nft_template.json @@ -0,0 +1,5 @@ +{ + "mint": "4xU8omSH3RfTyDHxWVCEm1HVTcf97YWkT8H67GvVpssz", + "tx": "4KLKJaG6w67YX5RidkYvgKuA4UAb9v8N1AbxWY6cmHJYSbZA2nevM2v8qBtTRe7t6jxGqFATttGpXogNK2Teyse7", + "createdAt": "2026-05-15T15:14:04.949Z" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-36_proposal_mint_HMww7YSV.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-36_proposal_mint_HMww7YSV.json new file mode 100644 index 0000000..b970b7b --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-36_proposal_mint_HMww7YSV.json @@ -0,0 +1,14 @@ +{ + "type": "mint_nft", + "realm": "2DTh1ivaekAW8kRYzGPsL2taFLJFFkBjEwqPisebxsS7", + "governance": "EMZ8vmr1xB4HZBDCFL9rHB98m1C5cYrGnRA8ZHayyGwD", + "proposal": "EspTK14DG6WAbip3hdh27zWLYSCjEiMqj7ZbdoxZVrPH", + "proposalTransaction": "DYH4dFzVsxYFyLpcTSGL3ce7VN1xNFbrcc3AMpWqA2XT", + "nftMint": "4xU8omSH3RfTyDHxWVCEm1HVTcf97YWkT8H67GvVpssz", + "targetWallet": "HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA", + "targetAta": "2fUHqDDdcCcxyG62dZwyNiT2H9xZc3Rgy9oFtWiTdCXr", + "txCreate": "xwWL7W2VTgc2zyswo7ZdkvV7exEJA6FGzpiZan4DtDVjjNhf5wL1VkhH1Cxng2z4ACmT8TQHXyct6tkSZvPuTrj", + "txInsert": "4fmyYG4AJ7pZLa6is5qrPY2znLtVe3YuXDEKhqrwVZZsLm64nUrMdoQ6WPQR17HsGqsxt2WqWgQkCSg9QzERLunn", + "txSign": "4ouuHY2kpp573y3YB2w5TuaneNvwy1hbCYz8n6z2SRxV52YGe5fVdTMgoiFwNPwj9XeCxpb1CVfKvvsSttFgSUqB", + "txVote1": "48gz4HjvaK8seAs6otZYnf1viiy8jtmBFvPZCsjsjZ6ig5tqVMfksMvxf1orZRtYe5BXMozMD6uW7NmNyzMasZgP" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-53_proposal_burn_FUc28vNi.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-53_proposal_burn_FUc28vNi.json new file mode 100644 index 0000000..2bf1b7b --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-14-53_proposal_burn_FUc28vNi.json @@ -0,0 +1,14 @@ +{ + "type": "burn_nft", + "realm": "2DTh1ivaekAW8kRYzGPsL2taFLJFFkBjEwqPisebxsS7", + "governance": "EMZ8vmr1xB4HZBDCFL9rHB98m1C5cYrGnRA8ZHayyGwD", + "proposal": "BbwBWxzB3Tkf7r8QftycZAsFAKNtKz6nWZGsabjhSSxc", + "proposalTransaction": "4SbdRwFyDN52CRETNpHqe79ABiTSkeSwuttHUAnq99om", + "nftMint": "9Up5SURRoBybsrfZnR7nKZC5gHarrccdVuMzoxeU3Xia", + "targetWallet": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "sourceAta": "3Q3cfBrNgyagEpMNPEZngYpCgw9U8Bd2KK4BbBfzvL5u", + "txCreate": "5D6P7tPnvVy2B3NjrYQCYRwVKczkwD2D8PQCeTNy1SuiNoMdP31DiHDTi4k9B5fQbiEo5x2FCp5CR28muFTTnrWp", + "txInsert": "vwmnX2w9uJxbNoVK8jn2pLSvjbmEYYN7QcGnEm3zGSfXaQsNZpS8FsyH6584XpB2831A78MZxdNX6Fdi4a97Zn6", + "txSign": "4ViTxSiumXdkFMaHsN6Hghdegc6hNrzH4LJPuvkPRXxThsLbevdFBs4q5u4bKRCDGKBkBF4Ftx24beq34AqzNDN3", + "txVote1": "2Z367p3DztZHkVo25V649HBRzsdxk69mAByTEPg7PfBRLHRtiZ4KJeb2Hm5vkAj9g7EGHxKKfY35jPyaaPfXhKfC" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-29-37_empty_nft_template.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-29-37_empty_nft_template.json new file mode 100644 index 0000000..2aef98b --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-29-37_empty_nft_template.json @@ -0,0 +1,5 @@ +{ + "mint": "hssoT46Vp7KzisNAffBSQxGLmtxfzcswVeeEi4eq8gW", + "tx": "5LwmJLfUskcJgz9ZhZH3ojnXDvMjeBe3TiLLtLVndYxnb5oVUfGkrpGsdvh5RYGgC1zahATpQUHc9bJMtvNe4UW9", + "createdAt": "2026-05-15T15:29:37.181Z" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-29-59_proposal_mint_HMww7YSV.json b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-29-59_proposal_mint_HMww7YSV.json new file mode 100644 index 0000000..b354339 --- /dev/null +++ b/shine/scripts/CreateGovernmentNFTAndDAO/runs/2026-05-15_18-29-59_proposal_mint_HMww7YSV.json @@ -0,0 +1,14 @@ +{ + "type": "mint_nft", + "realm": "2DTh1ivaekAW8kRYzGPsL2taFLJFFkBjEwqPisebxsS7", + "governance": "EMZ8vmr1xB4HZBDCFL9rHB98m1C5cYrGnRA8ZHayyGwD", + "proposal": "CKV2RSJ4HiUGQvdhGyizub89csTF52TD2Z2HciQFPkdW", + "proposalTransaction": "AAvEV3q9MeHedbJMsTk1C8nLg3LJtTLUBWt9fgi2wnqC", + "nftMint": "hssoT46Vp7KzisNAffBSQxGLmtxfzcswVeeEi4eq8gW", + "targetWallet": "HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA", + "targetAta": "9wxC9Zb5FCbkrhjcHkyhft1Kn6FVdzamijk99zcQz5xZ", + "txCreate": "3z2otjQtptrDgDXPQUWKos6EhDXqK9zifAXaSdZjKEPyHTxaFS32TXoCHy2QKBEMv5NAMVXqDRF9mifFLSbbhUXK", + "txInsert": "Sys1JeebsUyACPb3TjUQ5NciBXwAjAhUVj5geRpabmxB23MhsQsw9USCGqCKqX9zpdUAg6rQ7qt8v4QFtMB3cDt", + "txSign": "5wdYVUgYtew7PPmtHCyNPWVZvruw58FmiQhwbVGHngPKbfL1s1LTtZtNiAAVfzBzpaavzA8y2CjUYrSizS7hQuh9", + "txVote1": "DtWj3CCq48JBVRmSfwWu2k2uQSHuArMAbJbz2KSXpMDBqtTsaBr9Duintn8gFz5Dnmm4VjT4Cf6L5yB2PxyW8Cc" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/01_create_governance_token.sh b/shine/scripts/CreateGovernmentTokenAndDAO/01_create_governance_token.sh new file mode 100755 index 0000000..c0dbca6 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/01_create_governance_token.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail +# +# RU: Создает governance token (Token-2022, NonTransferable + PermanentDelegate) +# с настройками из governance_token.config.env. +# EN: Creates governance token (Token-2022, NonTransferable + PermanentDelegate) +# using settings from governance_token.config.env. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_PATH="$SCRIPT_DIR/governance_token.config.env" +node "$SCRIPT_DIR/js/01_create_governance_token_exec.js" "$CONFIG_PATH" diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/02_mint_token_to_wallet.sh b/shine/scripts/CreateGovernmentTokenAndDAO/02_mint_token_to_wallet.sh new file mode 100755 index 0000000..9b946ad --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/02_mint_token_to_wallet.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -euo pipefail +# +# RU: Выпускает ровно 1 membership-токен на указанный кошелек. +# Если у кошелька уже есть >=1 токен, скрипт завершится ошибкой. +# EN: Mints exactly 1 membership token to the given wallet. +# If wallet already has >=1 token, script exits with error. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_PATH="$SCRIPT_DIR/governance_token.config.env" +WALLET="${1:-}" + +if [[ -z "$WALLET" ]]; then + echo "Использование:" + echo " $0 " + echo "Usage:" + echo " $0 " + exit 1 +fi + +node "$SCRIPT_DIR/js/02_mint_membership_to_wallet_exec.js" "$CONFIG_PATH" "$WALLET" diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/03_force_burn_from_wallet.sh b/shine/scripts/CreateGovernmentTokenAndDAO/03_force_burn_from_wallet.sh new file mode 100755 index 0000000..2b58857 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/03_force_burn_from_wallet.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail +# +# RU: Принудительно сжигает 1 membership-токен на указанном кошельке. +# EN: Force-burns exactly 1 membership token from the given wallet. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_PATH="$SCRIPT_DIR/governance_token.config.env" +WALLET="${1:-}" + +if [[ -z "$WALLET" ]]; then + echo "Использование:" + echo " $0 " + echo "Usage:" + echo " $0 " + exit 1 +fi + +node "$SCRIPT_DIR/js/03_force_burn_from_wallet_exec.js" "$CONFIG_PATH" "$WALLET" diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/04_create_dao.sh b/shine/scripts/CreateGovernmentTokenAndDAO/04_create_dao.sh new file mode 100755 index 0000000..78ca80c --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/04_create_dao.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail +# +# RU: Создает DAO (Realm + Governance + Treasury) на уже существующем governance mint. +# EN: Creates DAO (Realm + Governance + Treasury) using existing governance mint. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_PATH="$SCRIPT_DIR/governance_token.config.env" + +node "$SCRIPT_DIR/js/05_create_dao_exec.js" "$CONFIG_PATH" diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/05_transfer_rights_to_governance_pda.sh b/shine/scripts/CreateGovernmentTokenAndDAO/05_transfer_rights_to_governance_pda.sh new file mode 100755 index 0000000..c69a3bc --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/05_transfer_rights_to_governance_pda.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail +# +# RU: Передает права Mint/Freeze/PermanentDelegate на Governance PDA из конфига. +# Перед отправкой транзакции внутри JS будет подтверждение "yes". +# EN: Transfers Mint/Freeze/PermanentDelegate authorities to Governance PDA +# from config. JS script asks for "yes" confirmation before sending. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_PATH="$SCRIPT_DIR/governance_token.config.env" +node "$SCRIPT_DIR/js/04_transfer_rights_to_governance_pda_exec.js" "$CONFIG_PATH" diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/README.md b/shine/scripts/CreateGovernmentTokenAndDAO/README.md new file mode 100644 index 0000000..246d12d --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/README.md @@ -0,0 +1,74 @@ +# CreateGovernmentTokenAndDAO + +## RU + +Единый набор скриптов для: +1. создания governance token, +2. выдачи/сжигания membership токенов, +3. передачи прав на Governance PDA, +4. создания DAO (Realm/Governance/Treasury). + +### Важная структура ключей + +Используются две папки: +- `keypairs/dao_creator/` — ключ инициатора DAO и плательщика (ровно 1 `*.json`). +- `keypairs/government_token/` — ключ mint governance token (ровно 1 `*.json`). + +Скрипты автоматически берут единственный файл из этих папок. +Если в папке `government_token` 0 файлов или больше 1 — скрипт завершится ошибкой. + +### Скрипты + +```bash +./01_create_governance_token.sh +./02_mint_token_to_wallet.sh +./03_force_burn_from_wallet.sh +./04_create_dao.sh +./05_transfer_rights_to_governance_pda.sh +./grind_vanity_mint.sh [PREFIX] [COUNT] [ignore-case] +``` + +### Базовый порядок + +1. (Опционально) `grind_vanity_mint.sh`, затем ОБЯЗАТЕЛЬНО скопировать выбранный json в `keypairs/government_token/`. + Пример: + ```bash + cp ./runs/.json ./keypairs/government_token/selected_mint.json + ``` +2. `01_create_governance_token.sh` +3. В `governance_token.config.env` указать `GT_MINT_ADDRESS`. +4. `02_mint_token_to_wallet.sh ` +5. `03_force_burn_from_wallet.sh ` +6. `04_create_dao.sh` +7. Внести полученный Governance PDA в `GT_GOVERNANCE_PDA`. +8. `05_transfer_rights_to_governance_pda.sh` + +--- + +## EN + +Unified scripts for: +1. governance token creation, +2. membership mint/burn, +3. authority transfer to Governance PDA, +4. DAO creation (Realm/Governance/Treasury). + +### Required keypair layout + +Two folders are used: +- `keypairs/dao_creator/` — DAO creator/payer keypair (exactly 1 `*.json`). +- `keypairs/government_token/` — governance token mint keypair (exactly 1 `*.json`). + +Scripts auto-detect the single file in each folder. +If `government_token` has 0 files or more than 1 file, script fails with error. + +### Scripts + +```bash +./01_create_governance_token.sh +./02_mint_token_to_wallet.sh +./03_force_burn_from_wallet.sh +./04_create_dao.sh +./05_transfer_rights_to_governance_pda.sh +./grind_vanity_mint.sh [PREFIX] [COUNT] [ignore-case] +``` diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/governance_token.config.env b/shine/scripts/CreateGovernmentTokenAndDAO/governance_token.config.env new file mode 100644 index 0000000..9828d85 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/governance_token.config.env @@ -0,0 +1,33 @@ +# Конфиг CreateGovernmentTokenAndDAO + +# devnet | mainnet-beta +GT_CLUSTER="devnet" + +# Папка с keypair инициатора DAO/плательщика. +# Внутри должен быть ровно 1 json-файл. +GT_DAO_CREATOR_KEYPAIR_DIR="./keypairs/dao_creator" + +# Папка с keypair governance token mint. +# Внутри должен быть ровно 1 json-файл (или 0, тогда 01-скрипт создаст selected_mint.json). +GT_GOVERNMENT_TOKEN_KEYPAIR_DIR="./keypairs/government_token" + +# Governance PDA (сюда передаем управляющие права после создания DAO) +GT_GOVERNANCE_PDA="REPLACE_WITH_GOVERNANCE_PDA" + +# Явный mint-адрес (если указан, приоритетнее keypair-папки) +GT_MINT_ADDRESS="" + +# Папка для результатов/логов +GT_RUNS_DIR="./runs" + +# Дефолт для vanity-подбора (05) +GT_VANITY_PREFIX="SHiNE" + +# ===== DAO create settings (05_create_dao.sh) ===== +DAO_REALM_NAME="CreateDAO Test 2026-05-15" +DAO_VOTING_TIME_SEC="3600" +DAO_APPROVAL_THRESHOLD_PERCENT="51" +DAO_RUNS_DIR="./runs" + +# SPL Governance program +SPL_GOVERNANCE_PROGRAM_ID="GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw" diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/grind_vanity_mint.sh b/shine/scripts/CreateGovernmentTokenAndDAO/grind_vanity_mint.sh new file mode 100755 index 0000000..f3524ea --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/grind_vanity_mint.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail +# +# RU: Подбирает vanity mint keypair через `solana-keygen grind`. +# Параметры: [PREFIX] [COUNT] [ignore-case] +# EN: Finds vanity mint keypair using `solana-keygen grind`. +# Args: [PREFIX] [COUNT] [ignore-case] + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_PATH="$SCRIPT_DIR/governance_token.config.env" +PREFIX="${1:-}" +COUNT="${2:-1}" +IGNORE_CASE="${3:-}" + +if [[ -n "$PREFIX" ]]; then + node "$SCRIPT_DIR/js/grind_vanity_mint_exec.js" "$CONFIG_PATH" "$PREFIX" "$COUNT" "$IGNORE_CASE" +else + node "$SCRIPT_DIR/js/grind_vanity_mint_exec.js" "$CONFIG_PATH" +fi diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/js/01_create_governance_token_exec.js b/shine/scripts/CreateGovernmentTokenAndDAO/js/01_create_governance_token_exec.js new file mode 100644 index 0000000..0d4f261 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/js/01_create_governance_token_exec.js @@ -0,0 +1,40 @@ +#!/usr/bin/env node +"use strict"; +const fs = require("fs"); +const path = require("path"); +const { Connection, SystemProgram, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js"); +const { TOKEN_PROGRAM_ID, getMintLen, createInitializeMintInstruction } = require("@solana/spl-token"); +const { parseEnvConfig, assertRequired, resolveConfigPath, loadKeypair, findSingleJsonFile, saveKeypair, parseCluster, nowStamp, ui, getOperatorKeypairFromConfig } = require("./_common"); + +async function main() { + const cfg = parseEnvConfig(resolveConfigPath(process.argv[2])); + assertRequired(cfg, "GT_CLUSTER"); assertRequired(cfg, "GT_RUNS_DIR"); + const operator = getOperatorKeypairFromConfig(cfg); + const connection = new Connection(parseCluster(cfg.GT_CLUSTER), "confirmed"); + const gtDir = path.resolve(cfg.GT_GOVERNMENT_TOKEN_KEYPAIR_DIR || path.join(__dirname, "..", "keypairs", "government_token")); + fs.mkdirSync(gtDir, { recursive: true }); + const mintKeypairPath = findSingleJsonFile(gtDir); + const mint = loadKeypair(mintKeypairPath); + const mintLen = getMintLen([]); + const rent = await connection.getMinimumBalanceForRentExemption(mintLen, "confirmed"); + ui.title("=== Создание governance token (SPL classic) / Create governance token (SPL classic) ==="); + const tx = new Transaction().add( + SystemProgram.createAccount({ fromPubkey: operator.publicKey, newAccountPubkey: mint.publicKey, space: mintLen, lamports: rent, programId: TOKEN_PROGRAM_ID }), + createInitializeMintInstruction(mint.publicKey, 0, operator.publicKey, operator.publicKey, TOKEN_PROGRAM_ID) + ); + const sig = await sendAndConfirmTransaction(connection, tx, [operator, mint], { commitment: "confirmed" }); + const runsDir = path.resolve(cfg.GT_RUNS_DIR); fs.mkdirSync(runsDir, { recursive: true }); + const outMintPath = mintKeypairPath; + saveKeypair(outMintPath, mint); + fs.writeFileSync(path.join(runsDir, `${nowStamp()}_create_token.json`), JSON.stringify({ mint: mint.publicKey.toBase58(), txCreateMint: sig }, null, 2)); + ui.ok(`OK: Mint ${mint.publicKey.toBase58()}`); + ui.info(`RU: Использован keypair: ${mintKeypairPath}`); + ui.info(`EN: Used keypair: ${mintKeypairPath}`); + ui.info(`RU: Вставьте этот mint в файл: ${path.resolve(__dirname, "..", "governance_token.config.env")}`); + ui.info(`RU: Строка: GT_MINT_ADDRESS="${mint.publicKey.toBase58()}"`); + ui.info(`EN: Put this mint into file: ${path.resolve(__dirname, "..", "governance_token.config.env")}`); + ui.info(`EN: Line: GT_MINT_ADDRESS="${mint.publicKey.toBase58()}"`); + ui.info(`Mint keypair: ${outMintPath}`); + ui.info(`Tx: ${sig}`); +} +main().catch((e) => { ui.err(`Ошибка / Error: ${e?.message || e}`); process.exit(1); }); diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/js/02_mint_membership_to_wallet_exec.js b/shine/scripts/CreateGovernmentTokenAndDAO/js/02_mint_membership_to_wallet_exec.js new file mode 100644 index 0000000..1498bbc --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/js/02_mint_membership_to_wallet_exec.js @@ -0,0 +1,34 @@ +#!/usr/bin/env node +"use strict"; +const { Connection, PublicKey, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js"); +const { TOKEN_2022_PROGRAM_ID, getAssociatedTokenAddressSync, createAssociatedTokenAccountIdempotentInstruction, createMintToInstruction, getAccount } = require("@solana/spl-token"); +const { parseEnvConfig, assertRequired, resolveConfigPath, parseCluster, ui, getMintPublicKeyFromConfig, getOperatorKeypairFromConfig } = require("./_common"); + +async function main() { + const cfg = parseEnvConfig(resolveConfigPath(process.argv[2])); + const receiver = new PublicKey(process.argv[3]); + if (!process.argv[3]) throw new Error("Использование / Usage: node .../02...js "); + assertRequired(cfg, "GT_CLUSTER"); + const mint = getMintPublicKeyFromConfig(cfg); + const operator = getOperatorKeypairFromConfig(cfg); + const connection = new Connection(parseCluster(cfg.GT_CLUSTER), "confirmed"); + const ata = getAssociatedTokenAddressSync(mint, receiver, false, TOKEN_2022_PROGRAM_ID); + const ataInfo = await connection.getAccountInfo(ata, "confirmed"); + if (ataInfo) { + const tokenAcc = await getAccount(connection, ata, "confirmed", TOKEN_2022_PROGRAM_ID); + if (tokenAcc.amount >= 1n) { + throw new Error( + `На кошельке уже есть membership token / Wallet already has membership token. wallet=${receiver.toBase58()} amount=${tokenAcc.amount.toString()}` + ); + } + } + const ix = [ + createAssociatedTokenAccountIdempotentInstruction(operator.publicKey, ata, receiver, mint, TOKEN_2022_PROGRAM_ID), + createMintToInstruction(mint, ata, operator.publicKey, 1n, [], TOKEN_2022_PROGRAM_ID), + ]; + ui.title("=== Выпуск 1 membership токена / Mint 1 membership token ==="); + const sig = await sendAndConfirmTransaction(connection, new Transaction().add(...ix), [operator], { commitment: "confirmed" }); + ui.ok("Успешно / Success"); + ui.info(`Mint: ${mint.toBase58()}`); ui.info(`Wallet: ${receiver.toBase58()}`); ui.info(`Tx: ${sig}`); +} +main().catch((e) => { ui.err(`Ошибка / Error: ${e?.message || e}`); process.exit(1); }); diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/js/03_force_burn_from_wallet_exec.js b/shine/scripts/CreateGovernmentTokenAndDAO/js/03_force_burn_from_wallet_exec.js new file mode 100644 index 0000000..06d24cc --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/js/03_force_burn_from_wallet_exec.js @@ -0,0 +1,22 @@ +#!/usr/bin/env node +"use strict"; +const { Connection, PublicKey, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js"); +const { TOKEN_2022_PROGRAM_ID, getAssociatedTokenAddressSync, createBurnCheckedInstruction } = require("@solana/spl-token"); +const { parseEnvConfig, assertRequired, resolveConfigPath, parseCluster, ui, getMintPublicKeyFromConfig, getOperatorKeypairFromConfig } = require("./_common"); + +async function main() { + const cfg = parseEnvConfig(resolveConfigPath(process.argv[2])); + const targetOwner = new PublicKey(process.argv[3]); + if (!process.argv[3]) throw new Error("Использование / Usage: node .../03...js "); + assertRequired(cfg, "GT_CLUSTER"); + const mint = getMintPublicKeyFromConfig(cfg); + const operator = getOperatorKeypairFromConfig(cfg); + const connection = new Connection(parseCluster(cfg.GT_CLUSTER), "confirmed"); + const targetAta = getAssociatedTokenAddressSync(mint, targetOwner, false, TOKEN_2022_PROGRAM_ID); + const ix = createBurnCheckedInstruction(targetAta, mint, operator.publicKey, 1n, 0, [], TOKEN_2022_PROGRAM_ID); + ui.title("=== Принудительное сжигание 1 токена / Force burn 1 token ==="); + const sig = await sendAndConfirmTransaction(connection, new Transaction().add(ix), [operator], { commitment: "confirmed" }); + ui.ok("Успешно / Success"); + ui.info(`Mint: ${mint.toBase58()}`); ui.info(`Wallet: ${targetOwner.toBase58()}`); ui.info(`Tx: ${sig}`); +} +main().catch((e) => { ui.err(`Ошибка / Error: ${e?.message || e}`); process.exit(1); }); diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/js/04_transfer_rights_to_governance_pda_exec.js b/shine/scripts/CreateGovernmentTokenAndDAO/js/04_transfer_rights_to_governance_pda_exec.js new file mode 100644 index 0000000..071726c --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/js/04_transfer_rights_to_governance_pda_exec.js @@ -0,0 +1,28 @@ +#!/usr/bin/env node +"use strict"; +const { Connection, PublicKey, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js"); +const { TOKEN_2022_PROGRAM_ID, AuthorityType, createSetAuthorityInstruction } = require("@solana/spl-token"); +const { parseEnvConfig, assertRequired, resolveConfigPath, parseCluster, askYes, ui, getMintPublicKeyFromConfig, getOperatorKeypairFromConfig } = require("./_common"); + +async function main() { + const cfg = parseEnvConfig(resolveConfigPath(process.argv[2])); + assertRequired(cfg, "GT_CLUSTER"); assertRequired(cfg, "GT_GOVERNANCE_PDA"); + const mint = getMintPublicKeyFromConfig(cfg); + const operator = getOperatorKeypairFromConfig(cfg); + const governancePda = new PublicKey(cfg.GT_GOVERNANCE_PDA); + const connection = new Connection(parseCluster(cfg.GT_CLUSTER), "confirmed"); + ui.title("=== Передача прав DAO / Transfer rights to DAO ==="); + ui.warn(`RU: Будут переданы права Mint/Freeze/PermanentDelegate от ${operator.publicKey.toBase58()} на ${governancePda.toBase58()}`); + ui.warn(`EN: Mint/Freeze/PermanentDelegate authorities will be transferred to governance PDA.`); + const ok = await askYes("Введите yes / Type yes to continue: "); + if (!ok) return ui.warn("Отменено / Cancelled"); + const ixs = [ + createSetAuthorityInstruction(mint, operator.publicKey, AuthorityType.MintTokens, governancePda, [], TOKEN_2022_PROGRAM_ID), + createSetAuthorityInstruction(mint, operator.publicKey, AuthorityType.FreezeAccount, governancePda, [], TOKEN_2022_PROGRAM_ID), + createSetAuthorityInstruction(mint, operator.publicKey, AuthorityType.PermanentDelegate, governancePda, [], TOKEN_2022_PROGRAM_ID), + ]; + const sig = await sendAndConfirmTransaction(connection, new Transaction().add(...ixs), [operator], { commitment: "confirmed" }); + ui.ok("Успешно / Success"); + ui.info(`Mint: ${mint.toBase58()}`); ui.info(`DAO PDA: ${governancePda.toBase58()}`); ui.info(`Tx: ${sig}`); +} +main().catch((e) => { ui.err(`Ошибка / Error: ${e?.message || e}`); process.exit(1); }); diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/js/05_create_dao_exec.js b/shine/scripts/CreateGovernmentTokenAndDAO/js/05_create_dao_exec.js new file mode 100644 index 0000000..a00e53c --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/js/05_create_dao_exec.js @@ -0,0 +1,191 @@ +#!/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 { + getAssociatedTokenAddressSync, + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, +} = require("@solana/spl-token"); +const { + MintMaxVoteWeightSource, + VoteThreshold, + VoteThresholdType, + VoteTipping, + GovernanceConfig, + PROGRAM_VERSION_V3, + GoverningTokenConfigAccountArgs, + GoverningTokenType, + withCreateRealm, + withDepositGoverningTokens, + withCreateGovernance, + withCreateNativeTreasury, + withSetRealmAuthority, + SetRealmAuthorityAction, +} = require("@solana/spl-governance"); +const { parseEnvConfig, assertRequired, resolveConfigPath, parseCluster, nowStamp, getOperatorKeypairFromConfig, getMintPublicKeyFromConfig, ui } = require("./_common"); + +async function main() { + const configPath = resolveConfigPath(process.argv[2]); + const cfg = parseEnvConfig(configPath); + [ + "GT_CLUSTER", "DAO_REALM_NAME", "SPL_GOVERNANCE_PROGRAM_ID", "DAO_VOTING_TIME_SEC", "DAO_APPROVAL_THRESHOLD_PERCENT" + ].forEach((k) => assertRequired(cfg, k)); + + const cluster = cfg.GT_CLUSTER; + const connection = new Connection(parseCluster(cluster), "confirmed"); + const operator = getOperatorKeypairFromConfig(cfg); + const governanceProgramId = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID); + const mint = getMintPublicKeyFromConfig(cfg); + const votingTimeSec = Number(cfg.DAO_VOTING_TIME_SEC); + const thresholdPct = Number(cfg.DAO_APPROVAL_THRESHOLD_PERCENT); + const runsDir = path.resolve(cfg.DAO_RUNS_DIR || path.join(__dirname, "runs")); + fs.mkdirSync(runsDir, { recursive: true }); + + const mintAi = await connection.getAccountInfo(mint, "confirmed"); + if (!mintAi) throw new Error(`Governing mint not found: ${mint.toBase58()}`); + if (!mintAi.owner.equals(TOKEN_PROGRAM_ID)) { + throw new Error( + `Этот CreateDAO ожидает governing mint под классическим SPL Token (${TOKEN_PROGRAM_ID.toBase58()}). ` + + `Текущий mint owner: ${mintAi.owner.toBase58()}` + ); + } + + const [realmPda] = PublicKey.findProgramAddressSync( + [Buffer.from("governance"), Buffer.from(cfg.DAO_REALM_NAME, "utf8")], + governanceProgramId + ); + const realmExists = (await connection.getAccountInfo(realmPda)) !== null; + if (realmExists) throw new Error(`Realm already exists: ${realmPda.toBase58()}`); + + const ownerAtaToken2022 = getAssociatedTokenAddressSync(mint, operator.publicKey, false, TOKEN_2022_PROGRAM_ID); + const ownerAtaToken = getAssociatedTokenAddressSync(mint, operator.publicKey, false, TOKEN_PROGRAM_ID); + let ownerAta = ownerAtaToken2022; + let ownerAtaInfo = await connection.getAccountInfo(ownerAtaToken2022, "confirmed"); + let tokenProgramId = TOKEN_2022_PROGRAM_ID; + if (!ownerAtaInfo) { + ownerAta = ownerAtaToken; + ownerAtaInfo = await connection.getAccountInfo(ownerAtaToken, "confirmed"); + tokenProgramId = TOKEN_PROGRAM_ID; + } + if (!ownerAtaInfo) throw new Error("Operator ATA for governing mint not found. Mint at least 1 token to operator first."); + + const programVersion = PROGRAM_VERSION_V3; + const ixRealm = []; + const communityTokenConfig = new GoverningTokenConfigAccountArgs({ + voterWeightAddin: undefined, + maxVoterWeightAddin: undefined, + tokenType: GoverningTokenType.Membership, + }); + const realmPk = await withCreateRealm( + ixRealm, + governanceProgramId, + programVersion, + cfg.DAO_REALM_NAME, + operator.publicKey, + mint, + operator.publicKey, + undefined, + MintMaxVoteWeightSource.FULL_SUPPLY_FRACTION, + new BN(1), + communityTokenConfig, + undefined + ); + const sigRealm = await sendAndConfirmTransaction(connection, new Transaction().add(...ixRealm), [operator], { commitment: "confirmed" }); + + const ixDeposit = []; + const tokenOwnerRecordPk = await withDepositGoverningTokens( + ixDeposit, + governanceProgramId, + programVersion, + realmPk, + ownerAta, + mint, + operator.publicKey, + operator.publicKey, + operator.publicKey, + new BN(1), + true, + tokenProgramId + ); + const sigDeposit = await sendAndConfirmTransaction(connection, new Transaction().add(...ixDeposit), [operator], { commitment: "confirmed" }); + + const governanceConfig = new GovernanceConfig({ + communityVoteThreshold: new VoteThreshold({ type: VoteThresholdType.YesVotePercentage, value: thresholdPct }), + minCommunityTokensToCreateProposal: new BN(1), + minInstructionHoldUpTime: 0, + baseVotingTime: votingTimeSec, + communityVoteTipping: VoteTipping.Early, + minCouncilTokensToCreateProposal: new BN(0), + councilVoteThreshold: new VoteThreshold({ type: VoteThresholdType.Disabled }), + councilVetoVoteThreshold: new VoteThreshold({ type: VoteThresholdType.Disabled }), + communityVetoVoteThreshold: new VoteThreshold({ type: VoteThresholdType.Disabled }), + councilVoteTipping: VoteTipping.Disabled, + votingCoolOffTime: 0, + depositExemptProposalCount: 0, + }); + const ixGov = []; + const governancePk = await withCreateGovernance( + ixGov, + governanceProgramId, + programVersion, + realmPk, + realmPk, + governanceConfig, + tokenOwnerRecordPk, + operator.publicKey, + operator.publicKey + ); + const treasuryPk = await withCreateNativeTreasury(ixGov, governanceProgramId, programVersion, governancePk, operator.publicKey); + const sigGov = await sendAndConfirmTransaction(connection, new Transaction().add(...ixGov), [operator], { commitment: "confirmed" }); + + const ixRealmAuthority = []; + withSetRealmAuthority( + ixRealmAuthority, + governanceProgramId, + programVersion, + realmPk, + operator.publicKey, + governancePk, + SetRealmAuthorityAction.SetChecked + ); + const sigSetRealmAuthority = await sendAndConfirmTransaction(connection, new Transaction().add(...ixRealmAuthority), [operator], { commitment: "confirmed" }); + + const report = { + createdAt: new Date().toISOString(), + cluster, + realmName: cfg.DAO_REALM_NAME, + governanceProgramId: governanceProgramId.toBase58(), + governingMint: mint.toBase58(), + operator: operator.publicKey.toBase58(), + realm: realmPk.toBase58(), + governance: governancePk.toBase58(), + nativeTreasury: treasuryPk.toBase58(), + tokenOwnerRecord: tokenOwnerRecordPk.toBase58(), + txRealm: sigRealm, + txDeposit: sigDeposit, + txGovernanceTreasury: sigGov, + txSetRealmAuthority: sigSetRealmAuthority, + }; + const reportPath = path.join(runsDir, `${nowStamp()}_create_dao.json`); + fs.writeFileSync(reportPath, JSON.stringify(report, null, 2)); + + ui.ok("DAO created successfully / DAO успешно создан"); + ui.info(`Realm: ${realmPk.toBase58()}`); + ui.info(`Governance PDA: ${governancePk.toBase58()}`); + ui.info(`Treasury: ${treasuryPk.toBase58()}`); + ui.info(`Report: ${reportPath}`); +} + +main().catch((e) => { + console.error("CreateDAO error:", e?.message || e); + process.exit(1); +}); diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/js/_common.js b/shine/scripts/CreateGovernmentTokenAndDAO/js/_common.js new file mode 100644 index 0000000..2232f43 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/js/_common.js @@ -0,0 +1,118 @@ +#!/usr/bin/env node +"use strict"; + +const fs = require("fs"); +const path = require("path"); +const readline = require("readline"); +const { Keypair, PublicKey, clusterApiUrl } = require("@solana/web3.js"); + +function parseEnvConfig(configPath) { + const raw = fs.readFileSync(configPath, "utf8"); + const out = {}; + for (const line of raw.split("\n")) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) continue; + const eq = trimmed.indexOf("="); + if (eq === -1) continue; + const key = trimmed.slice(0, eq).trim(); + let val = trimmed.slice(eq + 1).trim(); + if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) val = val.slice(1, -1); + val = val.replace(/\$HOME/g, process.env.HOME || ""); + out[key] = val; + } + return out; +} + +function assertRequired(cfg, key) { + if (!cfg[key]) throw new Error(`В конфиге отсутствует обязательный параметр / Missing config key: ${key}`); +} + +function resolveConfigPath(argvPath) { + return argvPath ? path.resolve(argvPath) : path.resolve(__dirname, "..", "governance_token.config.env"); +} + +function loadKeypair(filePath) { + const arr = JSON.parse(fs.readFileSync(filePath, "utf8")); + return Keypair.fromSecretKey(Uint8Array.from(arr)); +} + +function findSingleJsonFile(dirPath) { + const abs = path.resolve(dirPath); + if (!fs.existsSync(abs)) throw new Error(`Папка не найдена / Directory not found: ${abs}`); + const files = fs.readdirSync(abs).filter((f) => { + const p = path.join(abs, f); + return fs.statSync(p).isFile() && f.endsWith(".json"); + }); + if (files.length !== 1) { + throw new Error(`В папке должен быть ровно 1 json-файл / Directory must contain exactly 1 json file: ${abs}. Сейчас: ${files.length}`); + } + return path.join(abs, files[0]); +} + +function saveKeypair(filePath, keypair) { + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + fs.writeFileSync(filePath, JSON.stringify(Array.from(keypair.secretKey))); +} + +function parseCluster(cluster) { + if (cluster === "devnet" || cluster === "mainnet-beta" || cluster === "testnet") return clusterApiUrl(cluster); + return cluster; +} + +function nowStamp() { + const d = new Date(); + const p = (n) => String(n).padStart(2, "0"); + return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}_${p(d.getHours())}-${p(d.getMinutes())}-${p(d.getSeconds())}`; +} + +async function askYes(prompt) { + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + const answer = await new Promise((resolve) => rl.question(prompt, resolve)); + rl.close(); + return answer.trim() === "yes"; +} + +function colors(s, code) { return `\x1b[${code}m${s}\x1b[0m`; } +const ui = { + info: (s) => console.log(colors(s, "36")), + ok: (s) => console.log(colors(s, "32")), + warn: (s) => console.log(colors(s, "33")), + err: (s) => console.log(colors(s, "31")), + title: (s) => console.log(colors(s, "1;35")), +}; + +function getMintPublicKeyFromConfig(cfg) { + if (cfg.GT_MINT_ADDRESS && cfg.GT_MINT_ADDRESS.trim()) return new PublicKey(cfg.GT_MINT_ADDRESS.trim()); + if (cfg.GT_GOVERNMENT_TOKEN_KEYPAIR_DIR && cfg.GT_GOVERNMENT_TOKEN_KEYPAIR_DIR.trim()) { + const kpPath = findSingleJsonFile(path.resolve(cfg.GT_GOVERNMENT_TOKEN_KEYPAIR_DIR)); + return loadKeypair(kpPath).publicKey; + } + if (cfg.GT_MINT_KEYPAIR_PATH && cfg.GT_MINT_KEYPAIR_PATH.trim()) return loadKeypair(path.resolve(cfg.GT_MINT_KEYPAIR_PATH)).publicKey; + throw new Error("Не задан mint: укажите GT_MINT_ADDRESS или положите 1 keypair в GT_GOVERNMENT_TOKEN_KEYPAIR_DIR"); +} + +function getOperatorKeypairFromConfig(cfg) { + if (cfg.GT_DAO_CREATOR_KEYPAIR_DIR && cfg.GT_DAO_CREATOR_KEYPAIR_DIR.trim()) { + const kpPath = findSingleJsonFile(path.resolve(cfg.GT_DAO_CREATOR_KEYPAIR_DIR)); + return loadKeypair(kpPath); + } + if (cfg.GT_OPERATOR_KEYPAIR_PATH && cfg.GT_OPERATOR_KEYPAIR_PATH.trim()) { + return loadKeypair(path.resolve(cfg.GT_OPERATOR_KEYPAIR_PATH)); + } + throw new Error("Не задан ключ оператора: укажите GT_DAO_CREATOR_KEYPAIR_DIR или GT_OPERATOR_KEYPAIR_PATH"); +} + +module.exports = { + parseEnvConfig, + assertRequired, + resolveConfigPath, + loadKeypair, + findSingleJsonFile, + saveKeypair, + parseCluster, + nowStamp, + askYes, + ui, + getMintPublicKeyFromConfig, + getOperatorKeypairFromConfig, +}; diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/js/grind_vanity_mint_exec.js b/shine/scripts/CreateGovernmentTokenAndDAO/js/grind_vanity_mint_exec.js new file mode 100644 index 0000000..8f4cb16 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/js/grind_vanity_mint_exec.js @@ -0,0 +1,36 @@ +#!/usr/bin/env node +"use strict"; +const fs = require("fs"); +const path = require("path"); +const { spawn } = require("child_process"); +const { parseEnvConfig, resolveConfigPath, nowStamp, ui } = require("./_common"); +const DEFAULT_PREFIX = "SHi"; + +async function main() { + const cfg = parseEnvConfig(resolveConfigPath(process.argv[2])); + const runsDir = path.resolve(cfg.GT_RUNS_DIR || path.join(__dirname, "..", "runs")); + fs.mkdirSync(runsDir, { recursive: true }); + const prefix = process.argv[3] || cfg.GT_VANITY_PREFIX || DEFAULT_PREFIX; + if (!/^[1-9A-HJ-NP-Za-km-z]+$/.test(prefix)) throw new Error("Префикс Base58 без 0/O/I/l"); + ui.title("=== Vanity подбор mint keypair / Vanity mint keypair grind ==="); + ui.info(`Prefix: ${prefix}`); + const args = ["grind", "--starts-with", `${prefix}:1`]; + const p = spawn("solana-keygen", args, { cwd: runsDir, stdio: ["ignore", "pipe", "pipe"] }); + const lines = []; + const on = (d) => { + for (const l of String(d).split("\n")) { + const line = l.trim(); if (!line) continue; + lines.push(line); console.log(line); + } + }; + p.stdout.on("data", on); p.stderr.on("data", on); + const code = await new Promise((resolve) => p.on("close", resolve)); + if (code !== 0) throw new Error(`solana-keygen grind exit code ${code}`); + const rp = path.join(runsDir, `${nowStamp()}_vanity_grind_report.json`); + fs.writeFileSync(rp, JSON.stringify({ createdAt: new Date().toISOString(), prefix, command: `solana-keygen ${args.join(" ")}`, outputLog: lines }, null, 2)); + ui.ok("Готово / Done"); + ui.info(`Report: ${rp}`); + ui.info(`RU: Скопируйте выбранный keypair из runs в keypairs/government_token/ (один json-файл).`); + ui.info(`EN: Copy selected keypair from runs to keypairs/government_token/ (single json file).`); +} +main().catch((e) => { ui.err(`Ошибка / Error: ${e?.message || e}`); process.exit(1); }); diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/keypairs/dao_creator/FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P.json b/shine/scripts/CreateGovernmentTokenAndDAO/keypairs/dao_creator/FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P.json new file mode 100644 index 0000000..3d11ee7 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/keypairs/dao_creator/FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P.json @@ -0,0 +1 @@ +[221,119,143,125,90,136,155,115,191,198,210,85,228,111,251,118,168,138,27,60,249,62,247,24,121,228,139,112,218,69,55,143,215,21,229,69,219,1,74,36,10,239,63,163,48,240,58,208,237,251,209,37,17,202,215,77,13,165,178,18,141,21,193,64] \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/keypairs/government_token/SHiEKwoeggb2Nd6AvkqKBFgh3ubBmW5YtVES4xu5j7h.json b/shine/scripts/CreateGovernmentTokenAndDAO/keypairs/government_token/SHiEKwoeggb2Nd6AvkqKBFgh3ubBmW5YtVES4xu5j7h.json new file mode 100644 index 0000000..ef64c3d --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/keypairs/government_token/SHiEKwoeggb2Nd6AvkqKBFgh3ubBmW5YtVES4xu5j7h.json @@ -0,0 +1 @@ +[134,197,95,124,255,199,219,182,107,27,148,43,9,167,197,238,72,191,98,205,70,227,160,213,107,110,89,3,33,49,199,29,6,122,106,78,28,40,164,141,120,125,226,194,56,246,248,203,15,90,120,32,226,163,174,32,67,73,246,167,52,25,64,236] \ No newline at end of file diff --git a/shine/scripts/governance_token/runs/2026-05-15_15-20-22_vanity_grind_report.json b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_15-33-36_vanity_grind_report.json similarity index 77% rename from shine/scripts/governance_token/runs/2026-05-15_15-20-22_vanity_grind_report.json rename to shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_15-33-36_vanity_grind_report.json index bbc51a7..f138f1a 100644 --- a/shine/scripts/governance_token/runs/2026-05-15_15-20-22_vanity_grind_report.json +++ b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_15-33-36_vanity_grind_report.json @@ -1,5 +1,5 @@ { - "createdAt": "2026-05-15T12:20:22.373Z", + "createdAt": "2026-05-15T12:33:36.911Z", "prefix": "DAo", "count": 1, "ignoreCase": false, @@ -11,6 +11,6 @@ "outputLog": [ "Searching with 16 threads for:", "1 pubkey that starts with 'DAo' and ends with ''", - "Wrote keypair to DAou7SeaykoMooghA5SURLYhkkU8NEhV5Y2T6fsXD7rn.json" + "Wrote keypair to DAoHZ4bmVZU7Cx9xnSsc1xJJfTJnP2s4TeMgci7x6AsG.json" ] } \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_15-34-25_vanity_grind_report.json b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_15-34-25_vanity_grind_report.json new file mode 100644 index 0000000..51c9c24 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_15-34-25_vanity_grind_report.json @@ -0,0 +1,17 @@ +{ + "createdAt": "2026-05-15T12:34:25.973Z", + "prefix": "SHi", + "count": 1, + "ignoreCase": false, + "runsDir": "/home/ai/work/SOLANA/shine-solana/shine/scripts/governance_token/runs", + "avgExpectedTriesPerMatch": 195112, + "attemptsObserved": 1000000, + "foundHintsInOutput": 1, + "command": "solana-keygen grind --starts-with SHi:1", + "outputLog": [ + "Searching with 16 threads for:", + "1 pubkey that starts with 'SHi' and ends with ''", + "Searched 1000000 keypairs in 4s. 0 matches found.", + "Wrote keypair to SHiwSvDUGjsye9ZE8YAttXdycuDcprWq95oqr69WP9f.json" + ] +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_15-46-32_create_token.json b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_15-46-32_create_token.json new file mode 100644 index 0000000..d64c915 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_15-46-32_create_token.json @@ -0,0 +1,13 @@ +{ + "createdAt": "2026-05-15T12:46:32.749Z", + "cluster": "devnet", + "operator": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "mint": "SHiwSvDUGjsye9ZE8YAttXdycuDcprWq95oqr69WP9f", + "tokenProgram": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "nonTransferable": true, + "permanentDelegate": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "mintAuthority": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "freezeAuthority": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "mintKeypairPath": "/home/ai/work/SOLANA/shine-solana/shine/scripts/governance_token/mint_keypairs/selected_mint.json", + "txCreateMint": "4oBPaP4L6E1z4UZUgLKXAA3ZBJcxtPgDcTL6MCfQJkmG8dsX3sARp7dDKYrqYT9B4H326N4HZpTwAJytfjnDfYQb" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_16-57-13_vanity_grind_report.json b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_16-57-13_vanity_grind_report.json new file mode 100644 index 0000000..7d35082 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_16-57-13_vanity_grind_report.json @@ -0,0 +1,10 @@ +{ + "createdAt": "2026-05-15T13:57:13.110Z", + "prefix": "SHi", + "command": "solana-keygen grind --starts-with SHi:1", + "outputLog": [ + "Searching with 16 threads for:", + "1 pubkey that starts with 'SHi' and ends with ''", + "Wrote keypair to SHiEKwoeggb2Nd6AvkqKBFgh3ubBmW5YtVES4xu5j7h.json" + ] +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_17-00-31_create_token.json b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_17-00-31_create_token.json new file mode 100644 index 0000000..1acaf31 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_17-00-31_create_token.json @@ -0,0 +1,4 @@ +{ + "mint": "SHiEKwoeggb2Nd6AvkqKBFgh3ubBmW5YtVES4xu5j7h", + "txCreateMint": "64cHd8ez4EuPs5TbaVLEqyHjp6ufiuZUyoErg3X98XUPoysWnTgeq4FUafJUqS97Hq8C7AuwbiQZPBVwjdCfKF5W" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_17-07-22_create_dao.json b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_17-07-22_create_dao.json new file mode 100644 index 0000000..e2f879b --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/runs/2026-05-15_17-07-22_create_dao.json @@ -0,0 +1,16 @@ +{ + "createdAt": "2026-05-15T14:07:22.593Z", + "cluster": "devnet", + "realmName": "DAO-Cost-0515-B", + "governanceProgramId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", + "governingMint": "DrKQHbtacwD2jqgxZCatLk2PCbNvgPrNQFswVP72hC98", + "operator": "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", + "realm": "H35UPU98sC2bo265Q4R6PWdvbaotbbCvjDcuvwjDvewf", + "governance": "9aD18P5nun1RPVpEeeCeG5ensyry9WKrwjdX4stVa7qP", + "nativeTreasury": "FbupU7ivym2P2UFi4qwypNGu1eTPDyb7Uctha1XEALCy", + "tokenOwnerRecord": "GvVc8DDrDXbPtYWzsdvPD6SXzWp4cXVmvUQ9Y68rDKz6", + "txRealm": "Xz5GF29JtWHZV1eTuy1gZsRM5RuDGWoEjJtotwzJrJ6yrhrHD87miJjDcJrHfn95Dcu8ELtxaJScBKYpmnh8QjQ", + "txDeposit": "3CStA356srRnXREyEjSf6nbEKDqiwWj7tA3EvMqsHSAfj8d6rtm5MC1t7gNvtgAYH3GMVhdE7zHx1iR1pgE1TNMY", + "txGovernanceTreasury": "omxdAHQtrFgA6qaDzX7QWC51ofuFWbkDTUjweksXwBqeT7tJr84hRsR7FMKAr45mi6b7xoDR7B9chq1qhfygtVg", + "txSetRealmAuthority": "5E61sgqx6Ax3bThG6AVFjxfbqaoNCUXQuECSWDshgKVsnyxNT9NSmkpqD4Gu3tC9B8s5nmYLVTb6DtfepMZSj6y9" +} \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/runs/DAoHZ4bmVZU7Cx9xnSsc1xJJfTJnP2s4TeMgci7x6AsG.json b/shine/scripts/CreateGovernmentTokenAndDAO/runs/DAoHZ4bmVZU7Cx9xnSsc1xJJfTJnP2s4TeMgci7x6AsG.json new file mode 100644 index 0000000..e21c489 --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/runs/DAoHZ4bmVZU7Cx9xnSsc1xJJfTJnP2s4TeMgci7x6AsG.json @@ -0,0 +1 @@ +[63,203,53,184,240,127,53,242,32,8,61,128,100,43,24,37,2,59,78,168,105,230,234,235,6,193,28,26,127,173,235,154,180,206,206,50,137,55,225,129,136,21,78,124,42,104,92,200,135,65,248,101,101,217,247,196,235,54,104,253,117,198,199,95] \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/runs/SHiEKwoeggb2Nd6AvkqKBFgh3ubBmW5YtVES4xu5j7h.json b/shine/scripts/CreateGovernmentTokenAndDAO/runs/SHiEKwoeggb2Nd6AvkqKBFgh3ubBmW5YtVES4xu5j7h.json new file mode 100644 index 0000000..ef64c3d --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/runs/SHiEKwoeggb2Nd6AvkqKBFgh3ubBmW5YtVES4xu5j7h.json @@ -0,0 +1 @@ +[134,197,95,124,255,199,219,182,107,27,148,43,9,167,197,238,72,191,98,205,70,227,160,213,107,110,89,3,33,49,199,29,6,122,106,78,28,40,164,141,120,125,226,194,56,246,248,203,15,90,120,32,226,163,174,32,67,73,246,167,52,25,64,236] \ No newline at end of file diff --git a/shine/scripts/CreateGovernmentTokenAndDAO/runs/SHiwSvDUGjsye9ZE8YAttXdycuDcprWq95oqr69WP9f.json b/shine/scripts/CreateGovernmentTokenAndDAO/runs/SHiwSvDUGjsye9ZE8YAttXdycuDcprWq95oqr69WP9f.json new file mode 100644 index 0000000..b0e9e9a --- /dev/null +++ b/shine/scripts/CreateGovernmentTokenAndDAO/runs/SHiwSvDUGjsye9ZE8YAttXdycuDcprWq95oqr69WP9f.json @@ -0,0 +1 @@ +[175,188,30,40,32,154,227,126,97,66,48,147,223,9,161,80,124,65,129,226,43,249,24,216,42,36,22,39,172,158,72,190,6,122,109,215,230,183,230,136,221,4,43,131,22,137,145,82,134,161,14,135,252,49,35,44,166,15,180,139,72,11,94,118] \ No newline at end of file diff --git a/shine/scripts/governance_token/01_create_governance_token_exec.js b/shine/scripts/governance_token/01_create_governance_token_exec.js deleted file mode 100644 index ce88619..0000000 --- a/shine/scripts/governance_token/01_create_governance_token_exec.js +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env node -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const { - Connection, - Keypair, - SystemProgram, - Transaction, - sendAndConfirmTransaction, -} = require("@solana/web3.js"); -const { - TOKEN_2022_PROGRAM_ID, - ExtensionType, - getMintLen, - createInitializeMintInstruction, - createInitializeNonTransferableMintInstruction, - createInitializePermanentDelegateInstruction, -} = require("@solana/spl-token"); -const { - parseEnvConfig, - assertRequired, - resolveConfigPath, - loadKeypair, - saveKeypair, - parseCluster, - nowStamp, -} = require("./_common"); - -async function main() { - const configPath = resolveConfigPath(process.argv[2]); - const cfg = parseEnvConfig(configPath); - assertRequired(cfg, "GT_CLUSTER"); - assertRequired(cfg, "GT_OPERATOR_KEYPAIR_PATH"); - assertRequired(cfg, "GT_RUNS_DIR"); - - const operator = loadKeypair(path.resolve(cfg.GT_OPERATOR_KEYPAIR_PATH)); - const connection = new Connection(parseCluster(cfg.GT_CLUSTER), "confirmed"); - - let mint; - if (cfg.GT_MINT_KEYPAIR_PATH) { - mint = loadKeypair(path.resolve(cfg.GT_MINT_KEYPAIR_PATH)); - } else { - mint = Keypair.generate(); - } - - const extensions = [ExtensionType.NonTransferable, ExtensionType.PermanentDelegate]; - const mintLen = getMintLen(extensions); - const rent = await connection.getMinimumBalanceForRentExemption(mintLen, "confirmed"); - - const tx = new Transaction().add( - SystemProgram.createAccount({ - fromPubkey: operator.publicKey, - newAccountPubkey: mint.publicKey, - space: mintLen, - lamports: rent, - programId: TOKEN_2022_PROGRAM_ID, - }), - createInitializeNonTransferableMintInstruction(mint.publicKey, TOKEN_2022_PROGRAM_ID), - createInitializePermanentDelegateInstruction( - mint.publicKey, - operator.publicKey, - TOKEN_2022_PROGRAM_ID - ), - createInitializeMintInstruction( - mint.publicKey, - 0, - operator.publicKey, - operator.publicKey, - TOKEN_2022_PROGRAM_ID - ) - ); - - const sig = await sendAndConfirmTransaction(connection, tx, [operator, mint], { - commitment: "confirmed", - }); - - const runsDir = path.resolve(cfg.GT_RUNS_DIR); - fs.mkdirSync(runsDir, { recursive: true }); - - const outMintPath = - cfg.GT_MINT_KEYPAIR_PATH && cfg.GT_MINT_KEYPAIR_PATH.trim() - ? path.resolve(cfg.GT_MINT_KEYPAIR_PATH) - : path.join(runsDir, `${nowStamp()}_mint-keypair.json`); - saveKeypair(outMintPath, mint); - - const report = { - createdAt: new Date().toISOString(), - cluster: cfg.GT_CLUSTER, - operator: operator.publicKey.toBase58(), - mint: mint.publicKey.toBase58(), - tokenProgram: TOKEN_2022_PROGRAM_ID.toBase58(), - nonTransferable: true, - permanentDelegate: operator.publicKey.toBase58(), - mintAuthority: operator.publicKey.toBase58(), - freezeAuthority: operator.publicKey.toBase58(), - mintKeypairPath: outMintPath, - txCreateMint: sig, - }; - const reportPath = path.join(runsDir, `${nowStamp()}_create_token.json`); - fs.writeFileSync(reportPath, JSON.stringify(report, null, 2)); - - console.log("Governance token создан."); - console.log("Mint:", mint.publicKey.toBase58()); - console.log("Mint keypair:", outMintPath); - console.log("Tx:", sig); - console.log("Report:", reportPath); -} - -main().catch((e) => { - console.error("Ошибка создания governance token:", e?.message || e); - process.exit(1); -}); diff --git a/shine/scripts/governance_token/01_создать_governance_токен.sh b/shine/scripts/governance_token/01_создать_governance_токен.sh deleted file mode 100755 index b289676..0000000 --- a/shine/scripts/governance_token/01_создать_governance_токен.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -CONFIG_PATH="${1:-$SCRIPT_DIR/governance_token.config.env}" - -node "$SCRIPT_DIR/01_create_governance_token_exec.js" "$CONFIG_PATH" diff --git a/shine/scripts/governance_token/02_mint_membership_to_wallet_exec.js b/shine/scripts/governance_token/02_mint_membership_to_wallet_exec.js deleted file mode 100644 index 99de29e..0000000 --- a/shine/scripts/governance_token/02_mint_membership_to_wallet_exec.js +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env node -"use strict"; - -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 { - parseEnvConfig, - assertRequired, - resolveConfigPath, - loadKeypair, - parseCluster, -} = require("./_common"); - -async function main() { - const configPath = resolveConfigPath(process.argv[2]); - const mint = new PublicKey(process.argv[3]); - const receiver = new PublicKey(process.argv[4]); - const amount = BigInt(process.argv[5] || "1"); - if (!process.argv[4]) { - throw new Error( - "Использование: node 02_mint_membership_to_wallet_exec.js [amount]" - ); - } - if (amount <= 0n) throw new Error("amount должен быть > 0"); - - const cfg = parseEnvConfig(configPath); - assertRequired(cfg, "GT_CLUSTER"); - assertRequired(cfg, "GT_OPERATOR_KEYPAIR_PATH"); - const operator = loadKeypair(path.resolve(cfg.GT_OPERATOR_KEYPAIR_PATH)); - const connection = new Connection(parseCluster(cfg.GT_CLUSTER), "confirmed"); - - const ata = getAssociatedTokenAddressSync( - mint, - receiver, - false, - TOKEN_2022_PROGRAM_ID - ); - - const ix = [ - createAssociatedTokenAccountIdempotentInstruction( - operator.publicKey, - ata, - receiver, - mint, - TOKEN_2022_PROGRAM_ID - ), - createMintToInstruction( - mint, - ata, - operator.publicKey, - amount, - [], - TOKEN_2022_PROGRAM_ID - ), - ]; - - const sig = await sendAndConfirmTransaction(connection, new Transaction().add(...ix), [operator], { - commitment: "confirmed", - }); - - console.log("Mint выполнен."); - console.log("Receiver:", receiver.toBase58()); - console.log("ATA:", ata.toBase58()); - console.log("Amount:", amount.toString()); - console.log("Tx:", sig); -} - -main().catch((e) => { - console.error("Ошибка mint membership:", e?.message || e); - process.exit(1); -}); diff --git a/shine/scripts/governance_token/02_выпустить_токен_на_кошелек.sh b/shine/scripts/governance_token/02_выпустить_токен_на_кошелек.sh deleted file mode 100755 index 0277e11..0000000 --- a/shine/scripts/governance_token/02_выпустить_токен_на_кошелек.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -CONFIG_PATH="${1:-$SCRIPT_DIR/governance_token.config.env}" -MINT="${2:-}" -WALLET="${3:-}" -AMOUNT="${4:-1}" - -if [[ -z "$MINT" || -z "$WALLET" ]]; then - echo "Использование:" - echo " $0 [config.env] [amount]" - exit 1 -fi - -node "$SCRIPT_DIR/02_mint_membership_to_wallet_exec.js" "$CONFIG_PATH" "$MINT" "$WALLET" "$AMOUNT" diff --git a/shine/scripts/governance_token/03_force_burn_from_wallet_exec.js b/shine/scripts/governance_token/03_force_burn_from_wallet_exec.js deleted file mode 100644 index 0314518..0000000 --- a/shine/scripts/governance_token/03_force_burn_from_wallet_exec.js +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env node -"use strict"; - -const path = require("path"); -const { - Connection, - PublicKey, - Transaction, - sendAndConfirmTransaction, -} = require("@solana/web3.js"); -const { - TOKEN_2022_PROGRAM_ID, - getAssociatedTokenAddressSync, - createBurnCheckedInstruction, -} = require("@solana/spl-token"); -const { - parseEnvConfig, - assertRequired, - resolveConfigPath, - loadKeypair, - parseCluster, -} = require("./_common"); - -async function main() { - const configPath = resolveConfigPath(process.argv[2]); - const mint = new PublicKey(process.argv[3]); - const targetOwner = new PublicKey(process.argv[4]); - const amount = BigInt(process.argv[5] || "1"); - if (!process.argv[4]) { - throw new Error( - "Использование: node 03_force_burn_from_wallet_exec.js [amount]" - ); - } - if (amount <= 0n) throw new Error("amount должен быть > 0"); - - const cfg = parseEnvConfig(configPath); - assertRequired(cfg, "GT_CLUSTER"); - assertRequired(cfg, "GT_OPERATOR_KEYPAIR_PATH"); - const operator = loadKeypair(path.resolve(cfg.GT_OPERATOR_KEYPAIR_PATH)); - const connection = new Connection(parseCluster(cfg.GT_CLUSTER), "confirmed"); - - const targetAta = getAssociatedTokenAddressSync( - mint, - targetOwner, - false, - TOKEN_2022_PROGRAM_ID - ); - - const ix = createBurnCheckedInstruction( - targetAta, - mint, - operator.publicKey, - amount, - 0, - [], - TOKEN_2022_PROGRAM_ID - ); - - const sig = await sendAndConfirmTransaction(connection, new Transaction().add(ix), [operator], { - commitment: "confirmed", - }); - - console.log("Force burn выполнен."); - console.log("Target owner:", targetOwner.toBase58()); - console.log("Target ATA:", targetAta.toBase58()); - console.log("Amount:", amount.toString()); - console.log("Tx:", sig); -} - -main().catch((e) => { - console.error("Ошибка force burn:", e?.message || e); - process.exit(1); -}); diff --git a/shine/scripts/governance_token/03_принудительно_сжечь_токен.sh b/shine/scripts/governance_token/03_принудительно_сжечь_токен.sh deleted file mode 100755 index b06ca65..0000000 --- a/shine/scripts/governance_token/03_принудительно_сжечь_токен.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -CONFIG_PATH="${1:-$SCRIPT_DIR/governance_token.config.env}" -MINT="${2:-}" -WALLET="${3:-}" -AMOUNT="${4:-1}" - -if [[ -z "$MINT" || -z "$WALLET" ]]; then - echo "Использование:" - echo " $0 [config.env] [amount]" - exit 1 -fi - -node "$SCRIPT_DIR/03_force_burn_from_wallet_exec.js" "$CONFIG_PATH" "$MINT" "$WALLET" "$AMOUNT" diff --git a/shine/scripts/governance_token/04_transfer_rights_to_governance_pda_exec.js b/shine/scripts/governance_token/04_transfer_rights_to_governance_pda_exec.js deleted file mode 100644 index f370dd7..0000000 --- a/shine/scripts/governance_token/04_transfer_rights_to_governance_pda_exec.js +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env node -"use strict"; - -const path = require("path"); -const { - Connection, - PublicKey, - Transaction, - sendAndConfirmTransaction, -} = require("@solana/web3.js"); -const { - TOKEN_2022_PROGRAM_ID, - AuthorityType, - createSetAuthorityInstruction, -} = require("@solana/spl-token"); -const { - parseEnvConfig, - assertRequired, - resolveConfigPath, - loadKeypair, - parseCluster, - askYes, -} = require("./_common"); - -async function main() { - const configPath = resolveConfigPath(process.argv[2]); - const mint = new PublicKey(process.argv[3]); - if (!process.argv[3]) { - throw new Error( - "Использование: node 04_transfer_rights_to_governance_pda_exec.js " - ); - } - - const cfg = parseEnvConfig(configPath); - assertRequired(cfg, "GT_CLUSTER"); - assertRequired(cfg, "GT_OPERATOR_KEYPAIR_PATH"); - assertRequired(cfg, "GT_GOVERNANCE_PDA"); - - const operator = loadKeypair(path.resolve(cfg.GT_OPERATOR_KEYPAIR_PATH)); - const governancePda = new PublicKey(cfg.GT_GOVERNANCE_PDA); - const connection = new Connection(parseCluster(cfg.GT_CLUSTER), "confirmed"); - - console.log("============================================================"); - console.log("ПЕРЕДАЧА ПРАВ УПРАВЛЕНИЯ ТОКЕНОМ НА GOVERNANCE PDA"); - console.log("------------------------------------------------------------"); - console.log("Сеть: ", cfg.GT_CLUSTER); - console.log("Mint: ", mint.toBase58()); - console.log("Текущий оператор: ", operator.publicKey.toBase58()); - console.log("Новый authority (DAO PDA): ", governancePda.toBase58()); - console.log("Будет передано:"); - console.log(" 1) MintTokens authority"); - console.log(" 2) FreezeAccount authority"); - console.log(" 3) PermanentDelegate authority"); - console.log("После этого текущий оператор утратит эти права."); - console.log("============================================================"); - - const ok = await askYes("Введите yes для подтверждения: "); - if (!ok) { - console.log("Отменено пользователем."); - return; - } - - const ixs = [ - createSetAuthorityInstruction( - mint, - operator.publicKey, - AuthorityType.MintTokens, - governancePda, - [], - TOKEN_2022_PROGRAM_ID - ), - createSetAuthorityInstruction( - mint, - operator.publicKey, - AuthorityType.FreezeAccount, - governancePda, - [], - TOKEN_2022_PROGRAM_ID - ), - createSetAuthorityInstruction( - mint, - operator.publicKey, - AuthorityType.PermanentDelegate, - governancePda, - [], - TOKEN_2022_PROGRAM_ID - ), - ]; - - const sig = await sendAndConfirmTransaction(connection, new Transaction().add(...ixs), [operator], { - commitment: "confirmed", - }); - console.log("Готово. Tx:", sig); -} - -main().catch((e) => { - console.error("Ошибка передачи прав:", e?.message || e); - process.exit(1); -}); diff --git a/shine/scripts/governance_token/04_передать_права_governance_pda.sh b/shine/scripts/governance_token/04_передать_права_governance_pda.sh deleted file mode 100755 index 4e12f1b..0000000 --- a/shine/scripts/governance_token/04_передать_права_governance_pda.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -CONFIG_PATH="${1:-$SCRIPT_DIR/governance_token.config.env}" -MINT="${2:-}" - -if [[ -z "$MINT" ]]; then - echo "Использование:" - echo " $0 [config.env] " - exit 1 -fi - -node "$SCRIPT_DIR/04_transfer_rights_to_governance_pda_exec.js" "$CONFIG_PATH" "$MINT" diff --git a/shine/scripts/governance_token/05_vanity_mint_keypair_grind_exec.js b/shine/scripts/governance_token/05_vanity_mint_keypair_grind_exec.js deleted file mode 100644 index 4f35a0a..0000000 --- a/shine/scripts/governance_token/05_vanity_mint_keypair_grind_exec.js +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env node -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const { spawn } = require("child_process"); -const { - parseEnvConfig, - resolveConfigPath, - nowStamp, -} = require("./_common"); - -// Можно править прямо в начале файла -const DEFAULT_PREFIX = "sh"; -const DEFAULT_MATCH_COUNT = 1; -const DEFAULT_IGNORE_CASE = false; - -function expectedTries(prefixLen) { - return Math.pow(58, prefixLen); -} - -function ensureSolanaKeygen() { - return new Promise((resolve, reject) => { - const p = spawn("solana-keygen", ["--version"], { stdio: ["ignore", "pipe", "pipe"] }); - p.on("error", reject); - p.on("close", (code) => { - if (code === 0) resolve(); - else reject(new Error("solana-keygen не найден или недоступен")); - }); - }); -} - -async function main() { - const configPath = resolveConfigPath(process.argv[2]); - const cfg = parseEnvConfig(configPath); - const runsDir = path.resolve(cfg.GT_RUNS_DIR || path.join(__dirname, "runs")); - fs.mkdirSync(runsDir, { recursive: true }); - - const prefix = process.argv[3] || cfg.GT_VANITY_PREFIX || DEFAULT_PREFIX; - const count = Number(process.argv[4] || DEFAULT_MATCH_COUNT); - const ignoreCase = (process.argv[5] || "").toLowerCase() === "ignore-case" || DEFAULT_IGNORE_CASE; - if (!/^[1-9A-HJ-NP-Za-km-z]+$/.test(prefix)) { - throw new Error("Префикс должен быть в Base58 (без 0 O I l)"); - } - if (!Number.isInteger(count) || count <= 0) throw new Error("count должен быть целым > 0"); - - await ensureSolanaKeygen(); - - const expected = expectedTries(prefix.length); - console.log("============================================================"); - console.log("VANITY MINT KEYPAIR GRIND"); - console.log("------------------------------------------------------------"); - console.log("Prefix:", prefix); - console.log("Count:", count); - console.log("Runs dir:", runsDir); - console.log("Среднее ожидание на 1 совпадение:", expected.toLocaleString("en-US"), "попыток"); - console.log("============================================================"); - - const args = ["grind", "--starts-with", `${prefix}:${count}`]; - if (ignoreCase) args.push("--ignore-case"); - - const child = spawn("solana-keygen", args, { - cwd: runsDir, - stdio: ["ignore", "pipe", "pipe"], - }); - - const lines = []; - let attempts = 0; - let found = 0; - - const onLine = (raw) => { - const line = String(raw).trim(); - if (!line) return; - lines.push(line); - console.log(line); - - const mAttempts = line.match(/Searched\s+([0-9,]+)\s+keypairs/i); - if (mAttempts) { - attempts = Number(mAttempts[1].replace(/,/g, "")) || attempts; - const pct = Math.min(100, (attempts / expected) * 100); - console.log( - `[progress] attempts=${attempts.toLocaleString("en-US")} ~${pct.toFixed(2)}% от среднего ожидания` - ); - } - if (/Wrote keypair to/i.test(line) || /Found matching key/i.test(line)) found += 1; - }; - - child.stdout.on("data", (d) => String(d).split("\n").forEach(onLine)); - child.stderr.on("data", (d) => String(d).split("\n").forEach(onLine)); - - const code = await new Promise((resolve) => child.on("close", resolve)); - if (code !== 0) { - throw new Error(`solana-keygen grind завершился с кодом ${code}`); - } - - const report = { - createdAt: new Date().toISOString(), - prefix, - count, - ignoreCase, - runsDir, - avgExpectedTriesPerMatch: expected, - attemptsObserved: attempts, - foundHintsInOutput: found, - command: `solana-keygen ${args.join(" ")}`, - outputLog: lines, - }; - - const reportPath = path.join(runsDir, `${nowStamp()}_vanity_grind_report.json`); - fs.writeFileSync(reportPath, JSON.stringify(report, null, 2)); - console.log("Report:", reportPath); - console.log("Готово. Keypair-файлы сохранены в:", runsDir); -} - -main().catch((e) => { - console.error("Ошибка vanity grind:", e?.message || e); - process.exit(1); -}); diff --git a/shine/scripts/governance_token/05_подобрать_vanity_mint.sh b/shine/scripts/governance_token/05_подобрать_vanity_mint.sh deleted file mode 100755 index c143acd..0000000 --- a/shine/scripts/governance_token/05_подобрать_vanity_mint.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -CONFIG_PATH="${1:-$SCRIPT_DIR/governance_token.config.env}" -PREFIX="${2:-}" -COUNT="${3:-1}" -IGNORE_CASE="${4:-}" - -if [[ -n "$PREFIX" ]]; then - node "$SCRIPT_DIR/05_vanity_mint_keypair_grind_exec.js" "$CONFIG_PATH" "$PREFIX" "$COUNT" "$IGNORE_CASE" -else - node "$SCRIPT_DIR/05_vanity_mint_keypair_grind_exec.js" "$CONFIG_PATH" -fi diff --git a/shine/scripts/governance_token/README.md b/shine/scripts/governance_token/README.md deleted file mode 100644 index 8afc853..0000000 --- a/shine/scripts/governance_token/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Governance Token Scripts - -Скрипты для управления governance token на Token-2022: -- `NonTransferable` -- `PermanentDelegate` - -## Конфиг - -Файл: `scripts/governance_token/governance_token.config.env` - -Ключи: -- `GT_CLUSTER` (`devnet` / `mainnet-beta`) -- `GT_OPERATOR_KEYPAIR_PATH` -- `GT_GOVERNANCE_PDA` -- `GT_MINT_KEYPAIR_PATH` (опционально) -- `GT_RUNS_DIR` -- `GT_VANITY_PREFIX` - -## Скрипты - -1. Создать новый governance token: - -```bash -node scripts/governance_token/01_create_governance_token_exec.js scripts/governance_token/governance_token.config.env -``` - -2. Выпустить токен участнику: - -```bash -node scripts/governance_token/02_mint_membership_to_wallet_exec.js scripts/governance_token/governance_token.config.env [AMOUNT] -``` - -3. Принудительно сжечь токен у участника: - -```bash -node scripts/governance_token/03_force_burn_from_wallet_exec.js scripts/governance_token/governance_token.config.env [AMOUNT] -``` - -4. Передать права на Governance PDA (с подтверждением `yes`): - -```bash -node scripts/governance_token/04_transfer_rights_to_governance_pda_exec.js scripts/governance_token/governance_token.config.env -``` - -5. Vanity-подбор mint keypair через `solana-keygen grind`: - -```bash -node scripts/governance_token/05_vanity_mint_keypair_grind_exec.js scripts/governance_token/governance_token.config.env [PREFIX] [COUNT] [ignore-case] -``` - -Результаты сохраняются в `GT_RUNS_DIR`. diff --git a/shine/scripts/governance_token/_common.js b/shine/scripts/governance_token/_common.js deleted file mode 100644 index ed7e5c0..0000000 --- a/shine/scripts/governance_token/_common.js +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env node -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const readline = require("readline"); -const { Keypair, PublicKey, clusterApiUrl } = require("@solana/web3.js"); - -function parseEnvConfig(configPath) { - const raw = fs.readFileSync(configPath, "utf8"); - const out = {}; - for (const line of raw.split("\n")) { - const trimmed = line.trim(); - if (!trimmed || trimmed.startsWith("#")) continue; - const eq = trimmed.indexOf("="); - if (eq === -1) continue; - const key = trimmed.slice(0, eq).trim(); - let val = trimmed.slice(eq + 1).trim(); - if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) { - val = val.slice(1, -1); - } - val = val.replace(/\$HOME/g, process.env.HOME || ""); - out[key] = val; - } - return out; -} - -function assertRequired(cfg, key) { - if (!cfg[key]) throw new Error(`В конфиге отсутствует обязательный параметр: ${key}`); -} - -function resolveConfigPath(argvPath) { - return argvPath - ? path.resolve(argvPath) - : path.resolve(__dirname, "governance_token.config.env"); -} - -function loadKeypair(filePath) { - const arr = JSON.parse(fs.readFileSync(filePath, "utf8")); - return Keypair.fromSecretKey(Uint8Array.from(arr)); -} - -function saveKeypair(filePath, keypair) { - fs.mkdirSync(path.dirname(filePath), { recursive: true }); - fs.writeFileSync(filePath, JSON.stringify(Array.from(keypair.secretKey))); -} - -function parseCluster(cluster) { - if (cluster === "devnet" || cluster === "mainnet-beta" || cluster === "testnet") { - return clusterApiUrl(cluster); - } - return cluster; -} - -function nowStamp() { - const d = new Date(); - const p = (n) => String(n).padStart(2, "0"); - return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}_${p(d.getHours())}-${p( - d.getMinutes() - )}-${p(d.getSeconds())}`; -} - -async function askYes(prompt) { - const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); - const answer = await new Promise((resolve) => rl.question(prompt, resolve)); - rl.close(); - return answer.trim() === "yes"; -} - -function toPublicKey(v, fieldName) { - try { - return new PublicKey(v); - } catch (_) { - throw new Error(`Некорректный pubkey в ${fieldName}: ${v}`); - } -} - -module.exports = { - parseEnvConfig, - assertRequired, - resolveConfigPath, - loadKeypair, - saveKeypair, - parseCluster, - nowStamp, - askYes, - toPublicKey, -}; diff --git a/shine/scripts/governance_token/governance_token.config.env b/shine/scripts/governance_token/governance_token.config.env deleted file mode 100644 index 6e5801e..0000000 --- a/shine/scripts/governance_token/governance_token.config.env +++ /dev/null @@ -1,19 +0,0 @@ -# Конфиг governance token (Token-2022, non-transferable) - -# devnet | mainnet-beta -GT_CLUSTER="devnet" - -# Путь к keypair кошелька-оператора (issuer/admin) -GT_OPERATOR_KEYPAIR_PATH="$HOME/.config/solana/phantomWallet.json" - -# Governance PDA (сюда передаем управляющие права после создания DAO) -GT_GOVERNANCE_PDA="REPLACE_WITH_GOVERNANCE_PDA" - -# Если нужен vanity mint, этот файл можно заполнить автоматически 05-скриптом -GT_MINT_KEYPAIR_PATH="" - -# Папка для результатов/логов -GT_RUNS_DIR="./runs" - -# Дефолт для vanity-подбора (05) -GT_VANITY_PREFIX="DAo" diff --git a/shine/scripts/governance_token/runs/DAou7SeaykoMooghA5SURLYhkkU8NEhV5Y2T6fsXD7rn.json b/shine/scripts/governance_token/runs/DAou7SeaykoMooghA5SURLYhkkU8NEhV5Y2T6fsXD7rn.json deleted file mode 100644 index e8f47fb..0000000 --- a/shine/scripts/governance_token/runs/DAou7SeaykoMooghA5SURLYhkkU8NEhV5Y2T6fsXD7rn.json +++ /dev/null @@ -1 +0,0 @@ -[115,67,66,30,87,145,246,163,61,151,201,124,183,214,51,151,22,218,111,91,138,240,184,170,169,117,123,68,99,10,100,161,180,207,127,168,102,124,209,83,46,144,109,253,200,122,20,82,223,74,69,105,53,218,226,231,88,238,93,98,54,161,167,31] \ No newline at end of file diff --git a/shine/scripts/governance_token/scripts/governance_token/runs/2026-05-15_15-20-09_vanity_grind_report.json b/shine/scripts/governance_token/scripts/governance_token/runs/2026-05-15_15-20-09_vanity_grind_report.json deleted file mode 100644 index f1544cc..0000000 --- a/shine/scripts/governance_token/scripts/governance_token/runs/2026-05-15_15-20-09_vanity_grind_report.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "createdAt": "2026-05-15T12:20:09.856Z", - "prefix": "DAo", - "count": 1, - "ignoreCase": false, - "runsDir": "/home/ai/work/SOLANA/shine-solana/shine/scripts/governance_token/scripts/governance_token/runs", - "avgExpectedTriesPerMatch": 195112, - "attemptsObserved": 0, - "foundHintsInOutput": 1, - "command": "solana-keygen grind --starts-with DAo:1", - "outputLog": [ - "Searching with 16 threads for:", - "1 pubkey that starts with 'DAo' and ends with ''", - "Wrote keypair to DAoxa7YRxa8Se5KFQmkBbxBJ3Fo8FeY8N9td6NGz38zu.json" - ] -} \ No newline at end of file diff --git a/shine/scripts/governance_token/scripts/governance_token/runs/DAoxa7YRxa8Se5KFQmkBbxBJ3Fo8FeY8N9td6NGz38zu.json b/shine/scripts/governance_token/scripts/governance_token/runs/DAoxa7YRxa8Se5KFQmkBbxBJ3Fo8FeY8N9td6NGz38zu.json deleted file mode 100644 index c05ba78..0000000 --- a/shine/scripts/governance_token/scripts/governance_token/runs/DAoxa7YRxa8Se5KFQmkBbxBJ3Fo8FeY8N9td6NGz38zu.json +++ /dev/null @@ -1 +0,0 @@ -[127,127,232,76,177,128,31,8,169,68,116,117,202,252,44,203,63,250,250,210,30,186,201,66,110,211,193,137,111,186,165,157,180,207,144,236,191,129,155,243,98,167,69,154,219,76,79,141,228,101,60,132,255,6,252,105,99,29,143,220,127,120,236,26] \ No newline at end of file