SHA256
Подготовка перед деплоем в mainnet
This commit is contained in:
@@ -23,7 +23,7 @@ require_cmd solana
|
||||
require_cmd solana-keygen
|
||||
require_cmd node
|
||||
|
||||
if [[ -z "${DAO_REALM_NAME:-}" || -z "${DAO_CLUSTER:-}" || -z "${DAO_ISSUER_KEYPAIR:-}" || -z "${SPL_GOVERNANCE_PROGRAM_ID:-}" ]]; then
|
||||
if [[ -z "${DAO_REALM_NAME:-}" || -z "${DAO_CLUSTER:-}" || -z "${DAO_ISSUER_KEYPAIR:-}" || -z "${SPL_GOVERNANCE_PROGRAM_ID:-}" || -z "${DAO_GOV_TOKEN_MINT:-}" ]]; then
|
||||
echo "Ошибка: обязательные поля конфига пустые"
|
||||
exit 1
|
||||
fi
|
||||
@@ -33,7 +33,7 @@ if [[ ! -f "$DAO_ISSUER_KEYPAIR" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ "${DAO_VOTING_TIME_SEC}" =~ ^[0-9]+$ ]] || ! [[ "${DAO_GOV_NFT_SUPPLY}" =~ ^[0-9]+$ ]] || ! [[ "${DAO_APPROVAL_THRESHOLD_PERCENT}" =~ ^[0-9]+$ ]]; then
|
||||
if ! [[ "${DAO_VOTING_TIME_SEC}" =~ ^[0-9]+$ ]] || ! [[ "${DAO_APPROVAL_THRESHOLD_PERCENT}" =~ ^[0-9]+$ ]]; then
|
||||
echo "Ошибка: числовые параметры заданы некорректно"
|
||||
exit 1
|
||||
fi
|
||||
@@ -45,6 +45,12 @@ fi
|
||||
|
||||
ISSUER_PUBKEY="$(solana-keygen pubkey "$DAO_ISSUER_KEYPAIR")"
|
||||
ISSUER_BALANCE="$(solana balance "$ISSUER_PUBKEY" --url "$DAO_CLUSTER" 2>/dev/null || true)"
|
||||
MINT_PUBKEY="$DAO_GOV_TOKEN_MINT"
|
||||
|
||||
if ! node -e "const { PublicKey } = require('@solana/web3.js'); new PublicKey(process.argv[1])" "$MINT_PUBKEY" >/dev/null 2>&1; then
|
||||
echo "Ошибка: DAO_GOV_TOKEN_MINT не является валидным pubkey"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REALM_PDA="$(node - "$DAO_REALM_NAME" "$SPL_GOVERNANCE_PROGRAM_ID" <<'NODE'
|
||||
const { PublicKey } = require("@solana/web3.js");
|
||||
@@ -63,6 +69,11 @@ if solana account "$REALM_PDA" --url "$DAO_CLUSTER" >/dev/null 2>&1; then
|
||||
REALM_EXISTS="yes"
|
||||
fi
|
||||
|
||||
MINT_EXISTS="no"
|
||||
if solana account "$MINT_PUBKEY" --url "$DAO_CLUSTER" >/dev/null 2>&1; then
|
||||
MINT_EXISTS="yes"
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
============================================================
|
||||
ПРОВЕРКА create-SHiNE-DAO
|
||||
@@ -72,11 +83,12 @@ Realm name: $DAO_REALM_NAME
|
||||
Realm PDA: $REALM_PDA
|
||||
Realm уже существует: $REALM_EXISTS
|
||||
Governance program: $SPL_GOVERNANCE_PROGRAM_ID
|
||||
Governance mint: $MINT_PUBKEY
|
||||
Mint уже существует: $MINT_EXISTS
|
||||
Эмиттер (issuer): $ISSUER_PUBKEY
|
||||
Баланс эмиттера: ${ISSUER_BALANCE:-unknown}
|
||||
Governance NFT name: $DAO_GOV_NFT_NAME
|
||||
Governance NFT symbol: $DAO_GOV_NFT_SYMBOL
|
||||
Governance NFT supply: $DAO_GOV_NFT_SUPPLY
|
||||
Voting time (sec): $DAO_VOTING_TIME_SEC
|
||||
Threshold %: $DAO_APPROVAL_THRESHOLD_PERCENT
|
||||
Конфиг: $CONFIG_PATH
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const BN = require("bn.js");
|
||||
const {
|
||||
Connection,
|
||||
Keypair,
|
||||
SystemProgram,
|
||||
Transaction,
|
||||
sendAndConfirmTransaction,
|
||||
} = require("@solana/web3.js");
|
||||
const {
|
||||
TOKEN_PROGRAM_ID,
|
||||
AuthorityType,
|
||||
getMintLen,
|
||||
createInitializeMintInstruction,
|
||||
createSetAuthorityInstruction,
|
||||
} = require("@solana/spl-token");
|
||||
const { createUmi } = require("@metaplex-foundation/umi-bundle-defaults");
|
||||
const { createSignerFromKeypair, signerIdentity, percentAmount, none, some } = require("@metaplex-foundation/umi");
|
||||
const { fromWeb3JsKeypair, fromWeb3JsPublicKey } = require("@metaplex-foundation/umi-web3js-adapters");
|
||||
const { mplTokenMetadata, createV1, TokenStandard } = require("@metaplex-foundation/mpl-token-metadata");
|
||||
const {
|
||||
PublicKey,
|
||||
assertRequired,
|
||||
clusterUrl,
|
||||
ensureArweaveUri,
|
||||
ensureDir,
|
||||
loadKeypair,
|
||||
nowStamp,
|
||||
parseEnvConfig,
|
||||
resolveConfigPath,
|
||||
} = require("./js_common");
|
||||
|
||||
function lamportsToSol(lamports) {
|
||||
return Number(lamports) / 1_000_000_000;
|
||||
}
|
||||
|
||||
async function attachGovernanceMetadata(cfg, cluster, issuer, mintPubkey, mintKeypair) {
|
||||
ensureArweaveUri("DAO_GOV_TOKEN_METADATA_URI", cfg.DAO_GOV_TOKEN_METADATA_URI);
|
||||
ensureArweaveUri("DAO_GOV_TOKEN_IMAGE_URL", cfg.DAO_GOV_TOKEN_IMAGE_URL);
|
||||
|
||||
const umi = createUmi(clusterUrl(cluster));
|
||||
const issuerSigner = createSignerFromKeypair(umi, fromWeb3JsKeypair(issuer));
|
||||
const mintSigner = createSignerFromKeypair(umi, fromWeb3JsKeypair(mintKeypair));
|
||||
umi.use(signerIdentity(issuerSigner));
|
||||
umi.use(mplTokenMetadata());
|
||||
|
||||
const builder = createV1(umi, {
|
||||
mint: mintSigner,
|
||||
authority: issuerSigner,
|
||||
payer: issuerSigner,
|
||||
updateAuthority: issuerSigner,
|
||||
name: cfg.DAO_GOV_NFT_NAME,
|
||||
symbol: cfg.DAO_GOV_NFT_SYMBOL,
|
||||
uri: cfg.DAO_GOV_TOKEN_METADATA_URI,
|
||||
sellerFeeBasisPoints: percentAmount(0),
|
||||
tokenStandard: TokenStandard.Fungible,
|
||||
decimals: some(0),
|
||||
creators: none(),
|
||||
collection: none(),
|
||||
uses: none(),
|
||||
collectionDetails: none(),
|
||||
ruleSet: none(),
|
||||
printSupply: none(),
|
||||
primarySaleHappened: false,
|
||||
isMutable: true,
|
||||
isCollection: false,
|
||||
splTokenProgram: fromWeb3JsPublicKey(TOKEN_PROGRAM_ID),
|
||||
});
|
||||
|
||||
const result = await builder.sendAndConfirm(umi);
|
||||
return Buffer.from(result.signature).toString("base64");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const configPath = resolveConfigPath(process.argv[2], "dao.config.env");
|
||||
if (!fs.existsSync(configPath)) throw new Error(`Конфиг не найден: ${configPath}`);
|
||||
|
||||
const cfg = parseEnvConfig(configPath);
|
||||
[
|
||||
"DAO_CLUSTER",
|
||||
"DAO_GOV_NFT_NAME",
|
||||
"DAO_GOV_NFT_SYMBOL",
|
||||
"DAO_GOV_TOKEN_DESCRIPTION",
|
||||
"DAO_ISSUER_KEYPAIR",
|
||||
"SPL_GOVERNANCE_PROGRAM_ID",
|
||||
"DAO_GOV_TOKEN_METADATA_URI",
|
||||
"DAO_GOV_TOKEN_IMAGE_URL",
|
||||
].forEach((k) => assertRequired(cfg, k));
|
||||
|
||||
const cluster = cfg.DAO_CLUSTER;
|
||||
const connection = new Connection(clusterUrl(cluster), "confirmed");
|
||||
const issuer = loadKeypair(path.resolve(cfg.DAO_ISSUER_KEYPAIR));
|
||||
const mintKeypair = cfg.DAO_GOV_MINT_KEYPAIR
|
||||
? loadKeypair(path.resolve(cfg.DAO_GOV_MINT_KEYPAIR))
|
||||
: Keypair.generate();
|
||||
const governanceProgramId = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID);
|
||||
|
||||
const startBalance = await connection.getBalance(issuer.publicKey, "confirmed");
|
||||
console.log("============================================================");
|
||||
console.log("CREATE SHiNE GOVERNANCE MINT");
|
||||
console.log("------------------------------------------------------------");
|
||||
console.log("Сеть: ", cluster);
|
||||
console.log("Mint: ", mintKeypair.publicKey.toBase58());
|
||||
console.log("Issuer: ", issuer.publicKey.toBase58());
|
||||
console.log("Governance program:", governanceProgramId.toBase58());
|
||||
console.log("Баланс до: ", `${lamportsToSol(startBalance)} SOL`);
|
||||
console.log("Name: ", cfg.DAO_GOV_NFT_NAME);
|
||||
console.log("Symbol: ", cfg.DAO_GOV_NFT_SYMBOL);
|
||||
console.log("Metadata URI: ", cfg.DAO_GOV_TOKEN_METADATA_URI);
|
||||
console.log("Image URL: ", cfg.DAO_GOV_TOKEN_IMAGE_URL);
|
||||
console.log("============================================================");
|
||||
|
||||
const mintLen = getMintLen([]);
|
||||
const mintRent = await connection.getMinimumBalanceForRentExemption(mintLen);
|
||||
const txMint = new Transaction().add(
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: issuer.publicKey,
|
||||
newAccountPubkey: mintKeypair.publicKey,
|
||||
space: mintLen,
|
||||
lamports: mintRent,
|
||||
programId: TOKEN_PROGRAM_ID,
|
||||
}),
|
||||
createInitializeMintInstruction(mintKeypair.publicKey, 0, issuer.publicKey, issuer.publicKey, TOKEN_PROGRAM_ID)
|
||||
);
|
||||
const sigMint = await sendAndConfirmTransaction(connection, txMint, [issuer, mintKeypair], {
|
||||
commitment: "confirmed",
|
||||
});
|
||||
|
||||
const sigMetadata = await attachGovernanceMetadata(cfg, cluster, issuer, mintKeypair.publicKey, mintKeypair);
|
||||
|
||||
const report = {
|
||||
createdAt: new Date().toISOString(),
|
||||
cluster,
|
||||
mint: mintKeypair.publicKey.toBase58(),
|
||||
issuer: issuer.publicKey.toBase58(),
|
||||
mintAuthority: issuer.publicKey.toBase58(),
|
||||
freezeAuthority: issuer.publicKey.toBase58(),
|
||||
metadataUpdateAuthority: issuer.publicKey.toBase58(),
|
||||
governanceProgramId: governanceProgramId.toBase58(),
|
||||
metadataUri: cfg.DAO_GOV_TOKEN_METADATA_URI,
|
||||
imageUrl: cfg.DAO_GOV_TOKEN_IMAGE_URL,
|
||||
txMint: sigMint,
|
||||
txMetadata: sigMetadata,
|
||||
};
|
||||
const runsDir = path.resolve(__dirname, "..", "local", "runs");
|
||||
ensureDir(runsDir);
|
||||
const reportPath = path.join(runsDir, `${nowStamp()}_governance_mint.json`);
|
||||
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
||||
|
||||
console.log("Готово. Governance mint создан.");
|
||||
console.log("mint:", mintKeypair.publicKey.toBase58());
|
||||
console.log("report:", reportPath);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error("CreateMint error:", e?.message || e);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const path = require("path");
|
||||
const { Connection, PublicKey, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js");
|
||||
const {
|
||||
TOKEN_PROGRAM_ID,
|
||||
getAssociatedTokenAddressSync,
|
||||
createAssociatedTokenAccountIdempotentInstruction,
|
||||
createMintToInstruction,
|
||||
getMint,
|
||||
} = require("@solana/spl-token");
|
||||
const { clusterUrl, loadKeypair, parseEnvConfig, resolveConfigPath } = require("./js_common");
|
||||
|
||||
async function main() {
|
||||
const targetWallet = process.argv[3];
|
||||
const amountRaw = process.argv[4] || "1";
|
||||
if (!targetWallet) {
|
||||
throw new Error("Usage: node 01a_mint_governance_token.js <config.env> <target_wallet> [amount]");
|
||||
}
|
||||
|
||||
const cfg = parseEnvConfig(resolveConfigPath(process.argv[2], "dao.config.env"));
|
||||
if (!cfg.DAO_GOV_TOKEN_MINT) throw new Error("В конфиге пустой DAO_GOV_TOKEN_MINT");
|
||||
if (!cfg.DAO_ISSUER_KEYPAIR) throw new Error("В конфиге пустой DAO_ISSUER_KEYPAIR");
|
||||
|
||||
const conn = new Connection(clusterUrl(cfg.DAO_CLUSTER || "devnet"), "confirmed");
|
||||
const issuer = loadKeypair(path.resolve(cfg.DAO_ISSUER_KEYPAIR));
|
||||
const mint = new PublicKey(cfg.DAO_GOV_TOKEN_MINT);
|
||||
const target = new PublicKey(targetWallet);
|
||||
const amount = BigInt(amountRaw);
|
||||
|
||||
const mintInfo = await getMint(conn, mint, "confirmed", TOKEN_PROGRAM_ID);
|
||||
if (mintInfo.decimals !== 0) {
|
||||
throw new Error(`Ожидался governance mint с decimals=0, а получено ${mintInfo.decimals}`);
|
||||
}
|
||||
|
||||
const ata = getAssociatedTokenAddressSync(mint, target, false, TOKEN_PROGRAM_ID);
|
||||
const tx = new Transaction().add(
|
||||
createAssociatedTokenAccountIdempotentInstruction(
|
||||
issuer.publicKey,
|
||||
ata,
|
||||
target,
|
||||
mint,
|
||||
TOKEN_PROGRAM_ID
|
||||
),
|
||||
createMintToInstruction(
|
||||
mint,
|
||||
ata,
|
||||
issuer.publicKey,
|
||||
amount,
|
||||
[],
|
||||
TOKEN_PROGRAM_ID
|
||||
)
|
||||
);
|
||||
|
||||
const sig = await sendAndConfirmTransaction(conn, tx, [issuer], { commitment: "confirmed" });
|
||||
|
||||
console.log("Mint done.");
|
||||
console.log("target:", target.toBase58());
|
||||
console.log("ata:", ata.toBase58());
|
||||
console.log("amount:", amount.toString());
|
||||
console.log("signature:", sig);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error("MintGovernanceToken error:", e?.message || e);
|
||||
process.exit(1);
|
||||
});
|
||||
+68
-174
@@ -7,20 +7,14 @@ const readline = require("readline");
|
||||
const BN = require("bn.js");
|
||||
const {
|
||||
Connection,
|
||||
Keypair,
|
||||
SystemProgram,
|
||||
PublicKey,
|
||||
Transaction,
|
||||
sendAndConfirmTransaction,
|
||||
} = require("@solana/web3.js");
|
||||
const {
|
||||
TOKEN_PROGRAM_ID,
|
||||
AuthorityType,
|
||||
getMintLen,
|
||||
createInitializeMintInstruction,
|
||||
getAssociatedTokenAddressSync,
|
||||
createAssociatedTokenAccountIdempotentInstruction,
|
||||
createMintToInstruction,
|
||||
createSetAuthorityInstruction,
|
||||
getAccount,
|
||||
} = require("@solana/spl-token");
|
||||
const {
|
||||
MintMaxVoteWeightSource,
|
||||
@@ -32,17 +26,11 @@ const {
|
||||
GoverningTokenConfigAccountArgs,
|
||||
GoverningTokenType,
|
||||
withCreateRealm,
|
||||
withDepositGoverningTokens,
|
||||
withCreateTokenOwnerRecord,
|
||||
withCreateGovernance,
|
||||
withCreateNativeTreasury,
|
||||
withSetRealmAuthority,
|
||||
SetRealmAuthorityAction,
|
||||
} = require("@solana/spl-governance");
|
||||
const { createUmi } = require("@metaplex-foundation/umi-bundle-defaults");
|
||||
const { createSignerFromKeypair, signerIdentity, percentAmount, none, some } = require("@metaplex-foundation/umi");
|
||||
const { fromWeb3JsKeypair, fromWeb3JsPublicKey } = require("@metaplex-foundation/umi-web3js-adapters");
|
||||
const { mplTokenMetadata, createV1, TokenStandard } = require("@metaplex-foundation/mpl-token-metadata");
|
||||
const { PublicKey, assertRequired, clusterUrl, ensureArweaveUri, ensureDir, loadKeypair, parseEnvConfig, resolveConfigPath, nowStamp } = require("./js_common");
|
||||
const { clusterUrl, ensureDir, loadKeypair, parseEnvConfig, resolveConfigPath, nowStamp } = require("./js_common");
|
||||
|
||||
function lamportsToSol(lamports) {
|
||||
return Number(lamports) / 1_000_000_000;
|
||||
@@ -57,43 +45,6 @@ async function askYes() {
|
||||
return answer.trim() === "YES";
|
||||
}
|
||||
|
||||
async function attachGovernanceMetadata(cfg, cluster, issuer, mintPubkey, mintKeypair) {
|
||||
ensureArweaveUri("DAO_GOV_TOKEN_METADATA_URI", cfg.DAO_GOV_TOKEN_METADATA_URI);
|
||||
ensureArweaveUri("DAO_GOV_TOKEN_IMAGE_URL", cfg.DAO_GOV_TOKEN_IMAGE_URL);
|
||||
|
||||
const umi = createUmi(clusterUrl(cluster));
|
||||
const issuerSigner = createSignerFromKeypair(umi, fromWeb3JsKeypair(issuer));
|
||||
const mintSigner = createSignerFromKeypair(umi, fromWeb3JsKeypair(mintKeypair));
|
||||
umi.use(signerIdentity(issuerSigner));
|
||||
umi.use(mplTokenMetadata());
|
||||
|
||||
const builder = createV1(umi, {
|
||||
mint: mintSigner,
|
||||
authority: issuerSigner,
|
||||
payer: issuerSigner,
|
||||
updateAuthority: issuerSigner,
|
||||
name: cfg.DAO_GOV_NFT_NAME,
|
||||
symbol: cfg.DAO_GOV_NFT_SYMBOL,
|
||||
uri: cfg.DAO_GOV_TOKEN_METADATA_URI,
|
||||
sellerFeeBasisPoints: percentAmount(0),
|
||||
tokenStandard: TokenStandard.Fungible,
|
||||
decimals: some(0),
|
||||
creators: none(),
|
||||
collection: none(),
|
||||
uses: none(),
|
||||
collectionDetails: none(),
|
||||
ruleSet: none(),
|
||||
printSupply: none(),
|
||||
primarySaleHappened: false,
|
||||
isMutable: true,
|
||||
isCollection: false,
|
||||
splTokenProgram: fromWeb3JsPublicKey(TOKEN_PROGRAM_ID),
|
||||
});
|
||||
|
||||
const result = await builder.sendAndConfirm(umi);
|
||||
return Buffer.from(result.signature).toString("base64");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const configPath = resolveConfigPath(process.argv[2], "dao.config.env");
|
||||
if (!fs.existsSync(configPath)) throw new Error(`Конфиг не найден: ${configPath}`);
|
||||
@@ -104,26 +55,31 @@ async function main() {
|
||||
"DAO_REALM_NAME",
|
||||
"DAO_GOV_NFT_NAME",
|
||||
"DAO_GOV_NFT_SYMBOL",
|
||||
"DAO_GOV_NFT_SUPPLY",
|
||||
"DAO_VOTING_TIME_SEC",
|
||||
"DAO_APPROVAL_THRESHOLD_PERCENT",
|
||||
"DAO_ISSUER_KEYPAIR",
|
||||
"SPL_GOVERNANCE_PROGRAM_ID",
|
||||
"DAO_GOV_TOKEN_METADATA_URI",
|
||||
"DAO_GOV_TOKEN_IMAGE_URL",
|
||||
].forEach((k) => assertRequired(cfg, k));
|
||||
"DAO_GOV_TOKEN_MINT",
|
||||
].forEach((k) => {
|
||||
if (!cfg[k]) throw new Error(`В конфиге отсутствует обязательный параметр: ${k}`);
|
||||
});
|
||||
|
||||
const cluster = cfg.DAO_CLUSTER;
|
||||
const connection = new Connection(clusterUrl(cluster), "confirmed");
|
||||
const issuer = loadKeypair(path.resolve(cfg.DAO_ISSUER_KEYPAIR));
|
||||
const governanceProgramId = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID);
|
||||
const supply = Number(cfg.DAO_GOV_NFT_SUPPLY);
|
||||
const votingTimeSec = Number(cfg.DAO_VOTING_TIME_SEC);
|
||||
const thresholdPct = Number(cfg.DAO_APPROVAL_THRESHOLD_PERCENT);
|
||||
const mint = new PublicKey(cfg.DAO_GOV_TOKEN_MINT);
|
||||
|
||||
if (!Number.isInteger(supply) || supply <= 0) throw new Error("DAO_GOV_NFT_SUPPLY должен быть > 0");
|
||||
if (!Number.isInteger(votingTimeSec) || votingTimeSec < 3600) throw new Error("DAO_VOTING_TIME_SEC должен быть >= 3600");
|
||||
if (!Number.isInteger(thresholdPct) || thresholdPct < 51 || thresholdPct > 100) throw new Error("DAO_APPROVAL_THRESHOLD_PERCENT должен быть в диапазоне 51..100");
|
||||
const mintInfoBefore = await connection.getParsedAccountInfo(mint, "confirmed");
|
||||
if (!mintInfoBefore.value) throw new Error(`Governing mint не найден: ${mint.toBase58()}`);
|
||||
const mintAccountBefore = await connection.getAccountInfo(mint, "confirmed");
|
||||
if (!mintAccountBefore || !mintAccountBefore.owner.equals(TOKEN_PROGRAM_ID)) {
|
||||
throw new Error(`Mint должен быть классическим SPL Token mint: ${mint.toBase58()}`);
|
||||
}
|
||||
|
||||
const mintParsed = mintInfoBefore.value.data.parsed.info;
|
||||
const mintAuthorityBefore = mintParsed.mintAuthority || null;
|
||||
const freezeAuthorityBefore = mintParsed.freezeAuthority || null;
|
||||
const issuerAta = getAssociatedTokenAddressSync(mint, issuer.publicKey, false, TOKEN_PROGRAM_ID);
|
||||
const issuerAtaInfo = await getAccount(connection, issuerAta, "confirmed").catch(() => null);
|
||||
|
||||
const [realmPda] = PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("governance"), Buffer.from(cfg.DAO_REALM_NAME, "utf8")],
|
||||
@@ -135,18 +91,19 @@ async function main() {
|
||||
|
||||
const startBalance = await connection.getBalance(issuer.publicKey, "confirmed");
|
||||
console.log("============================================================");
|
||||
console.log("CREATE SHiNE DAO WITH GOVERNANCE NFT");
|
||||
console.log("CREATE SHiNE DAO FROM EXISTING GOVERNANCE MINT");
|
||||
console.log("------------------------------------------------------------");
|
||||
console.log("Сеть: ", cluster);
|
||||
console.log("Realm name: ", cfg.DAO_REALM_NAME);
|
||||
console.log("Realm PDA: ", realmPda.toBase58());
|
||||
console.log("Issuer: ", issuer.publicKey.toBase58());
|
||||
console.log("Supply: ", supply);
|
||||
console.log("Voting time: ", votingTimeSec);
|
||||
console.log("Threshold %: ", thresholdPct);
|
||||
console.log("Governance mint: ", mint.toBase58());
|
||||
console.log("Issuer ATA: ", issuerAta.toBase58());
|
||||
console.log("Issuer ATA есть: ", issuerAtaInfo ? "yes" : "no");
|
||||
console.log("Issuer ATA amount:", issuerAtaInfo ? issuerAtaInfo.amount.toString() : "0");
|
||||
console.log("Баланс до: ", `${lamportsToSol(startBalance)} SOL`);
|
||||
console.log("Metadata URI: ", cfg.DAO_GOV_TOKEN_METADATA_URI);
|
||||
console.log("Image URL: ", cfg.DAO_GOV_TOKEN_IMAGE_URL);
|
||||
console.log("Mint authority: ", mintAuthorityBefore || "null");
|
||||
console.log("Freeze authority:", freezeAuthorityBefore || "null");
|
||||
console.log("============================================================");
|
||||
|
||||
if (!(await askYes())) {
|
||||
@@ -154,40 +111,11 @@ async function main() {
|
||||
return;
|
||||
}
|
||||
|
||||
const mintKeypair = Keypair.generate();
|
||||
const mintLen = getMintLen([]);
|
||||
const mintRent = await connection.getMinimumBalanceForRentExemption(mintLen);
|
||||
const issuerAta = getAssociatedTokenAddressSync(mintKeypair.publicKey, issuer.publicKey, false, TOKEN_PROGRAM_ID);
|
||||
|
||||
const txMint = new Transaction().add(
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: issuer.publicKey,
|
||||
newAccountPubkey: mintKeypair.publicKey,
|
||||
space: mintLen,
|
||||
lamports: mintRent,
|
||||
programId: TOKEN_PROGRAM_ID,
|
||||
}),
|
||||
createInitializeMintInstruction(mintKeypair.publicKey, 0, issuer.publicKey, issuer.publicKey, TOKEN_PROGRAM_ID),
|
||||
createAssociatedTokenAccountIdempotentInstruction(
|
||||
issuer.publicKey,
|
||||
issuerAta,
|
||||
issuer.publicKey,
|
||||
mintKeypair.publicKey,
|
||||
TOKEN_PROGRAM_ID
|
||||
),
|
||||
createMintToInstruction(mintKeypair.publicKey, issuerAta, issuer.publicKey, supply, [], TOKEN_PROGRAM_ID)
|
||||
);
|
||||
const sigMint = await sendAndConfirmTransaction(connection, txMint, [issuer, mintKeypair], {
|
||||
commitment: "confirmed",
|
||||
});
|
||||
|
||||
const sigMetadata = await attachGovernanceMetadata(cfg, cluster, issuer, mintKeypair.publicKey, mintKeypair);
|
||||
|
||||
const ixRealm = [];
|
||||
const communityTokenConfig = new GoverningTokenConfigAccountArgs({
|
||||
voterWeightAddin: undefined,
|
||||
maxVoterWeightAddin: undefined,
|
||||
tokenType: GoverningTokenType.Membership,
|
||||
tokenType: GoverningTokenType.Liquid,
|
||||
});
|
||||
const realmPk = await withCreateRealm(
|
||||
ixRealm,
|
||||
@@ -195,7 +123,7 @@ async function main() {
|
||||
PROGRAM_VERSION_V3,
|
||||
cfg.DAO_REALM_NAME,
|
||||
issuer.publicKey,
|
||||
mintKeypair.publicKey,
|
||||
mint,
|
||||
issuer.publicKey,
|
||||
undefined,
|
||||
MintMaxVoteWeightSource.FULL_SUPPLY_FRACTION,
|
||||
@@ -203,29 +131,32 @@ async function main() {
|
||||
communityTokenConfig,
|
||||
undefined
|
||||
);
|
||||
const sigRealm = await sendAndConfirmTransaction(connection, new Transaction().add(...ixRealm), [issuer], { commitment: "confirmed" });
|
||||
const sigRealm = await sendAndConfirmTransaction(connection, new Transaction().add(...ixRealm), [issuer], {
|
||||
commitment: "confirmed",
|
||||
});
|
||||
|
||||
const ixDeposit = [];
|
||||
const tokenOwnerRecordPk = await withDepositGoverningTokens(
|
||||
ixDeposit,
|
||||
const ixTokenOwnerRecord = [];
|
||||
const tokenOwnerRecordPk = await withCreateTokenOwnerRecord(
|
||||
ixTokenOwnerRecord,
|
||||
governanceProgramId,
|
||||
PROGRAM_VERSION_V3,
|
||||
realmPk,
|
||||
issuerAta,
|
||||
mintKeypair.publicKey,
|
||||
issuer.publicKey,
|
||||
issuer.publicKey,
|
||||
issuer.publicKey,
|
||||
new BN(supply),
|
||||
true
|
||||
mint,
|
||||
issuer.publicKey
|
||||
);
|
||||
const sigTokenOwnerRecord = await sendAndConfirmTransaction(
|
||||
connection,
|
||||
new Transaction().add(...ixTokenOwnerRecord),
|
||||
[issuer],
|
||||
{ commitment: "confirmed" }
|
||||
);
|
||||
const sigDeposit = await sendAndConfirmTransaction(connection, new Transaction().add(...ixDeposit), [issuer], { commitment: "confirmed" });
|
||||
|
||||
const governanceConfig = new GovernanceConfig({
|
||||
communityVoteThreshold: new VoteThreshold({ type: VoteThresholdType.YesVotePercentage, value: thresholdPct }),
|
||||
communityVoteThreshold: new VoteThreshold({ type: VoteThresholdType.YesVotePercentage, value: Number(cfg.DAO_APPROVAL_THRESHOLD_PERCENT || 51) }),
|
||||
minCommunityTokensToCreateProposal: new BN(1),
|
||||
minInstructionHoldUpTime: 0,
|
||||
baseVotingTime: votingTimeSec,
|
||||
baseVotingTime: Number(cfg.DAO_VOTING_TIME_SEC || 86400),
|
||||
communityVoteTipping: VoteTipping.Early,
|
||||
minCouncilTokensToCreateProposal: new BN(0),
|
||||
councilVoteThreshold: new VoteThreshold({ type: VoteThresholdType.Disabled }),
|
||||
@@ -249,82 +180,45 @@ async function main() {
|
||||
issuer.publicKey
|
||||
);
|
||||
const treasuryPk = await withCreateNativeTreasury(ixGov, governanceProgramId, PROGRAM_VERSION_V3, governancePk, issuer.publicKey);
|
||||
const sigGov = await sendAndConfirmTransaction(connection, new Transaction().add(...ixGov), [issuer], { commitment: "confirmed" });
|
||||
const sigGov = await sendAndConfirmTransaction(connection, new Transaction().add(...ixGov), [issuer], {
|
||||
commitment: "confirmed",
|
||||
});
|
||||
|
||||
const ixSetMintAuthority = [
|
||||
createSetAuthorityInstruction(
|
||||
mintKeypair.publicKey,
|
||||
issuer.publicKey,
|
||||
AuthorityType.MintTokens,
|
||||
governancePk,
|
||||
[],
|
||||
TOKEN_PROGRAM_ID
|
||||
),
|
||||
];
|
||||
const sigSetMintAuthority = await sendAndConfirmTransaction(connection, new Transaction().add(...ixSetMintAuthority), [issuer], { commitment: "confirmed" });
|
||||
|
||||
const ixRealmAuthority = [];
|
||||
withSetRealmAuthority(
|
||||
ixRealmAuthority,
|
||||
governanceProgramId,
|
||||
PROGRAM_VERSION_V3,
|
||||
realmPk,
|
||||
issuer.publicKey,
|
||||
governancePk,
|
||||
SetRealmAuthorityAction.SetChecked
|
||||
);
|
||||
const sigSetRealmAuthority = await sendAndConfirmTransaction(connection, new Transaction().add(...ixRealmAuthority), [issuer], { commitment: "confirmed" });
|
||||
|
||||
const endBalance = await connection.getBalance(issuer.publicKey, "confirmed");
|
||||
const spentLamports = startBalance - endBalance;
|
||||
const report = {
|
||||
createdAt: new Date().toISOString(),
|
||||
cluster,
|
||||
configPath,
|
||||
realmName: cfg.DAO_REALM_NAME,
|
||||
governanceProgramId: governanceProgramId.toBase58(),
|
||||
governingMint: mint.toBase58(),
|
||||
issuer: issuer.publicKey.toBase58(),
|
||||
communityMint: mintKeypair.publicKey.toBase58(),
|
||||
issuerAta: issuerAta.toBase58(),
|
||||
realm: realmPk.toBase58(),
|
||||
tokenOwnerRecord: tokenOwnerRecordPk.toBase58(),
|
||||
governance: governancePk.toBase58(),
|
||||
nativeTreasury: treasuryPk.toBase58(),
|
||||
metadataUri: cfg.DAO_GOV_TOKEN_METADATA_URI,
|
||||
imageUrl: cfg.DAO_GOV_TOKEN_IMAGE_URL,
|
||||
txMint: sigMint,
|
||||
txMetadata: sigMetadata,
|
||||
tokenOwnerRecord: tokenOwnerRecordPk.toBase58(),
|
||||
mintAuthorityBefore,
|
||||
freezeAuthorityBefore,
|
||||
txRealm: sigRealm,
|
||||
txDeposit: sigDeposit,
|
||||
txTokenOwnerRecord: sigTokenOwnerRecord,
|
||||
txGovernanceTreasury: sigGov,
|
||||
txSetMintAuthorityToGovernance: sigSetMintAuthority,
|
||||
txSetRealmAuthority: sigSetRealmAuthority,
|
||||
tokenSupply: supply,
|
||||
votingTimeSec,
|
||||
thresholdPercent: thresholdPct,
|
||||
startBalanceSol: lamportsToSol(startBalance),
|
||||
endBalanceSol: lamportsToSol(endBalance),
|
||||
spentSol: lamportsToSol(spentLamports),
|
||||
communityTokenType: "Liquid",
|
||||
};
|
||||
const runsDir = path.resolve(__dirname, "..", "local", "runs");
|
||||
ensureDir(runsDir);
|
||||
const reportPath = path.join(runsDir, `${nowStamp()}_create_dao_from_existing_mint.json`);
|
||||
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
||||
|
||||
const reportsDir = path.resolve(__dirname, "..", "local", "runs");
|
||||
ensureDir(reportsDir);
|
||||
const base = `${nowStamp()}_${cfg.DAO_REALM_NAME.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 80)}_dao`;
|
||||
const jsonPath = path.join(reportsDir, `${base}.json`);
|
||||
const txtPath = path.join(reportsDir, `${base}.txt`);
|
||||
fs.writeFileSync(jsonPath, JSON.stringify(report, null, 2));
|
||||
fs.writeFileSync(txtPath, Object.entries(report).map(([k, v]) => `${k}: ${v}`).join("\n") + "\n");
|
||||
|
||||
console.log("DAO создан.");
|
||||
console.log("communityMint:", report.communityMint);
|
||||
console.log("realm:", report.realm);
|
||||
console.log("governance:", report.governance);
|
||||
console.log("nativeTreasury:", report.nativeTreasury);
|
||||
console.log("spentSol:", report.spentSol);
|
||||
console.log("report:", jsonPath);
|
||||
console.log("DAO created.");
|
||||
console.log("communityMint:", mint.toBase58());
|
||||
console.log("realm:", realmPk.toBase58());
|
||||
console.log("governance:", governancePk.toBase58());
|
||||
console.log("nativeTreasury:", treasuryPk.toBase58());
|
||||
console.log("tokenOwnerRecord:", tokenOwnerRecordPk.toBase58());
|
||||
console.log("spentSol:", "see report via tx fees + rent");
|
||||
console.log("report:", reportPath);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error("Ошибка создания DAO:", e?.message || e);
|
||||
console.error("CreateDAO error:", e?.message || e);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const readline = require("readline");
|
||||
const {
|
||||
Connection,
|
||||
PublicKey,
|
||||
Transaction,
|
||||
sendAndConfirmTransaction,
|
||||
} = require("@solana/web3.js");
|
||||
const {
|
||||
TOKEN_PROGRAM_ID,
|
||||
AuthorityType,
|
||||
createSetAuthorityInstruction,
|
||||
} = require("@solana/spl-token");
|
||||
const {
|
||||
PROGRAM_VERSION_V3,
|
||||
getRealm,
|
||||
withSetRealmAuthority,
|
||||
SetRealmAuthorityAction,
|
||||
} = require("@solana/spl-governance");
|
||||
const { createUmi } = require("@metaplex-foundation/umi-bundle-defaults");
|
||||
const {
|
||||
createSignerFromKeypair,
|
||||
signerIdentity,
|
||||
none,
|
||||
some,
|
||||
} = require("@metaplex-foundation/umi");
|
||||
const { fromWeb3JsKeypair, fromWeb3JsPublicKey } = require("@metaplex-foundation/umi-web3js-adapters");
|
||||
const {
|
||||
mplTokenMetadata,
|
||||
fetchMetadataFromSeeds,
|
||||
updateV1,
|
||||
} = require("@metaplex-foundation/mpl-token-metadata");
|
||||
const {
|
||||
clusterUrl,
|
||||
ensureDir,
|
||||
loadKeypair,
|
||||
nowStamp,
|
||||
parseEnvConfig,
|
||||
resolveConfigPath,
|
||||
} = require("./js_common");
|
||||
|
||||
function lamportsToSol(lamports) {
|
||||
return Number(lamports) / 1_000_000_000;
|
||||
}
|
||||
|
||||
async function askYes() {
|
||||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||||
const answer = await new Promise((resolve) =>
|
||||
rl.question("Введите YES для передачи authority на DAO: ", resolve)
|
||||
);
|
||||
rl.close();
|
||||
return answer.trim() === "YES";
|
||||
}
|
||||
|
||||
async function fetchMetadataState(cluster, issuer, mint) {
|
||||
const umi = createUmi(clusterUrl(cluster));
|
||||
const issuerSigner = createSignerFromKeypair(umi, fromWeb3JsKeypair(issuer));
|
||||
umi.use(signerIdentity(issuerSigner));
|
||||
umi.use(mplTokenMetadata());
|
||||
|
||||
const metadata = await fetchMetadataFromSeeds(umi, { mint: fromWeb3JsPublicKey(mint) });
|
||||
return {
|
||||
umi,
|
||||
issuerSigner,
|
||||
metadata,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchMetadataUpdateAuthority(cluster, mint) {
|
||||
const umi = createUmi(clusterUrl(cluster));
|
||||
umi.use(mplTokenMetadata());
|
||||
const metadata = await fetchMetadataFromSeeds(umi, { mint: fromWeb3JsPublicKey(mint) });
|
||||
return metadata.updateAuthority.toString();
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const configPath = resolveConfigPath(process.argv[2], "dao.config.env");
|
||||
if (!fs.existsSync(configPath)) throw new Error(`Конфиг не найден: ${configPath}`);
|
||||
|
||||
const cfg = parseEnvConfig(configPath);
|
||||
[
|
||||
"DAO_CLUSTER",
|
||||
"DAO_REALM_NAME",
|
||||
"DAO_ISSUER_KEYPAIR",
|
||||
"SPL_GOVERNANCE_PROGRAM_ID",
|
||||
"DAO_GOV_TOKEN_MINT",
|
||||
].forEach((k) => {
|
||||
if (!cfg[k]) throw new Error(`В конфиге отсутствует обязательный параметр: ${k}`);
|
||||
});
|
||||
|
||||
const cluster = cfg.DAO_CLUSTER;
|
||||
const connection = new Connection(clusterUrl(cluster), "confirmed");
|
||||
const issuer = loadKeypair(path.resolve(cfg.DAO_ISSUER_KEYPAIR));
|
||||
const governanceProgramId = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID);
|
||||
const mint = new PublicKey(cfg.DAO_GOV_TOKEN_MINT);
|
||||
|
||||
const [realmPk] = PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("governance"), Buffer.from(cfg.DAO_REALM_NAME, "utf8")],
|
||||
governanceProgramId
|
||||
);
|
||||
const [governancePk] = PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("account-governance"), realmPk.toBuffer(), realmPk.toBuffer()],
|
||||
governanceProgramId
|
||||
);
|
||||
|
||||
const realmInfo = await connection.getAccountInfo(realmPk, "confirmed");
|
||||
if (!realmInfo) throw new Error(`Realm не найден: ${realmPk.toBase58()}. Сначала выполните шаг 2.`);
|
||||
|
||||
const governanceInfo = await connection.getAccountInfo(governancePk, "confirmed");
|
||||
if (!governanceInfo) {
|
||||
throw new Error(`Governance PDA не найден: ${governancePk.toBase58()}. Сначала выполните шаг 2.`);
|
||||
}
|
||||
const realmBefore = await getRealm(connection, realmPk);
|
||||
const realmAuthorityBefore = realmBefore.account.authority?.toBase58?.() || null;
|
||||
if (realmAuthorityBefore !== issuer.publicKey.toBase58()) {
|
||||
throw new Error(`Realm authority уже не у issuer: ${realmAuthorityBefore}`);
|
||||
}
|
||||
|
||||
const mintInfoBefore = await connection.getParsedAccountInfo(mint, "confirmed");
|
||||
if (!mintInfoBefore.value) throw new Error(`Mint не найден: ${mint.toBase58()}`);
|
||||
const mintParsedBefore = mintInfoBefore.value.data.parsed.info;
|
||||
const mintAuthorityBefore = mintParsedBefore.mintAuthority || null;
|
||||
const freezeAuthorityBefore = mintParsedBefore.freezeAuthority || null;
|
||||
|
||||
if (mintAuthorityBefore && mintAuthorityBefore !== issuer.publicKey.toBase58()) {
|
||||
throw new Error(`Mint authority уже не у issuer: ${mintAuthorityBefore}`);
|
||||
}
|
||||
if (freezeAuthorityBefore && freezeAuthorityBefore !== issuer.publicKey.toBase58()) {
|
||||
throw new Error(`Freeze authority уже не у issuer: ${freezeAuthorityBefore}`);
|
||||
}
|
||||
|
||||
const { umi, metadata, issuerSigner } = await fetchMetadataState(cluster, issuer, mint);
|
||||
const metadataUpdateAuthorityBefore = metadata.updateAuthority.toString();
|
||||
if (metadataUpdateAuthorityBefore !== issuer.publicKey.toBase58()) {
|
||||
throw new Error(`Metadata update authority уже не у issuer: ${metadataUpdateAuthorityBefore}`);
|
||||
}
|
||||
|
||||
const startBalance = await connection.getBalance(issuer.publicKey, "confirmed");
|
||||
console.log("============================================================");
|
||||
console.log("TRANSFER DAO AUTHORITIES");
|
||||
console.log("------------------------------------------------------------");
|
||||
console.log("Сеть: ", cluster);
|
||||
console.log("Realm: ", realmPk.toBase58());
|
||||
console.log("Governance PDA: ", governancePk.toBase58());
|
||||
console.log("Issuer: ", issuer.publicKey.toBase58());
|
||||
console.log("Governance mint: ", mint.toBase58());
|
||||
console.log("Баланс до: ", `${lamportsToSol(startBalance)} SOL`);
|
||||
console.log("Mint authority до: ", mintAuthorityBefore || "null");
|
||||
console.log("Freeze authority до: ", freezeAuthorityBefore || "null");
|
||||
console.log("Realm authority до: ", realmAuthorityBefore || "null");
|
||||
console.log("Metadata authority до: ", metadataUpdateAuthorityBefore);
|
||||
console.log("============================================================");
|
||||
|
||||
if (!(await askYes())) {
|
||||
console.log("Отменено.");
|
||||
return;
|
||||
}
|
||||
|
||||
const ixTransferAuthorities = [
|
||||
createSetAuthorityInstruction(
|
||||
mint,
|
||||
issuer.publicKey,
|
||||
AuthorityType.MintTokens,
|
||||
governancePk,
|
||||
[],
|
||||
TOKEN_PROGRAM_ID
|
||||
),
|
||||
createSetAuthorityInstruction(
|
||||
mint,
|
||||
issuer.publicKey,
|
||||
AuthorityType.FreezeAccount,
|
||||
governancePk,
|
||||
[],
|
||||
TOKEN_PROGRAM_ID
|
||||
),
|
||||
];
|
||||
const sigTransferAuthorities = await sendAndConfirmTransaction(
|
||||
connection,
|
||||
new Transaction().add(...ixTransferAuthorities),
|
||||
[issuer],
|
||||
{ commitment: "confirmed" }
|
||||
);
|
||||
|
||||
const ixSetRealmAuthority = [];
|
||||
withSetRealmAuthority(
|
||||
ixSetRealmAuthority,
|
||||
governanceProgramId,
|
||||
PROGRAM_VERSION_V3,
|
||||
realmPk,
|
||||
issuer.publicKey,
|
||||
governancePk,
|
||||
SetRealmAuthorityAction.SetChecked
|
||||
);
|
||||
const sigSetRealmAuthority = await sendAndConfirmTransaction(
|
||||
connection,
|
||||
new Transaction().add(...ixSetRealmAuthority),
|
||||
[issuer],
|
||||
{ commitment: "confirmed" }
|
||||
);
|
||||
|
||||
const metadataTx = await updateV1(umi, {
|
||||
authority: issuerSigner,
|
||||
mint: fromWeb3JsPublicKey(mint),
|
||||
newUpdateAuthority: some(fromWeb3JsPublicKey(governancePk)),
|
||||
data: none(),
|
||||
primarySaleHappened: none(),
|
||||
isMutable: none(),
|
||||
collection: { __kind: "None" },
|
||||
collectionDetails: { __kind: "None" },
|
||||
uses: { __kind: "None" },
|
||||
ruleSet: { __kind: "None" },
|
||||
authorizationData: none(),
|
||||
}).sendAndConfirm(umi);
|
||||
const sigMetadataAuthority = Buffer.from(metadataTx.signature).toString("base64");
|
||||
|
||||
const mintInfoAfter = await connection.getParsedAccountInfo(mint, "confirmed");
|
||||
const mintParsedAfter = mintInfoAfter.value.data.parsed.info;
|
||||
const mintAuthorityAfter = mintParsedAfter.mintAuthority || null;
|
||||
const freezeAuthorityAfter = mintParsedAfter.freezeAuthority || null;
|
||||
const realmAfter = await getRealm(connection, realmPk);
|
||||
const realmAuthorityAfter = realmAfter.account.authority?.toBase58?.() || null;
|
||||
|
||||
const metadataUpdateAuthorityAfter = await fetchMetadataUpdateAuthority(cluster, mint);
|
||||
|
||||
const report = {
|
||||
createdAt: new Date().toISOString(),
|
||||
cluster,
|
||||
realmName: cfg.DAO_REALM_NAME,
|
||||
governanceProgramId: governanceProgramId.toBase58(),
|
||||
issuer: issuer.publicKey.toBase58(),
|
||||
governingMint: mint.toBase58(),
|
||||
realm: realmPk.toBase58(),
|
||||
governance: governancePk.toBase58(),
|
||||
mintAuthorityBefore,
|
||||
freezeAuthorityBefore,
|
||||
metadataUpdateAuthorityBefore,
|
||||
realmAuthorityBefore,
|
||||
mintAuthorityAfter,
|
||||
freezeAuthorityAfter,
|
||||
realmAuthorityAfter,
|
||||
metadataUpdateAuthorityAfter,
|
||||
txTransferAuthorities: sigTransferAuthorities,
|
||||
txSetRealmAuthority: sigSetRealmAuthority,
|
||||
txSetMetadataUpdateAuthority: sigMetadataAuthority,
|
||||
};
|
||||
const runsDir = path.resolve(__dirname, "..", "local", "runs");
|
||||
ensureDir(runsDir);
|
||||
const reportPath = path.join(runsDir, `${nowStamp()}_transfer_dao_authorities.json`);
|
||||
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
||||
|
||||
console.log("Все authority переданы на DAO.");
|
||||
console.log("realm:", realmPk.toBase58());
|
||||
console.log("governance:", governancePk.toBase58());
|
||||
console.log("mintAuthority после:", mintAuthorityAfter || "null");
|
||||
console.log("freezeAuthority после:", freezeAuthorityAfter || "null");
|
||||
console.log("realmAuthority после:", realmAuthorityAfter || "null");
|
||||
console.log("metadataAuthority после:", metadataUpdateAuthorityAfter);
|
||||
console.log("report:", reportPath);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error("TransferAuthorities error:", e?.message || e);
|
||||
process.exit(1);
|
||||
});
|
||||
+1
-1
@@ -22,7 +22,7 @@ const { clusterUrl, ensureDir, loadKeypair, nowStamp, parseEnvConfig, resolveCon
|
||||
async function main() {
|
||||
const targetWallet = process.argv[3];
|
||||
const nftMintStr = process.argv[4];
|
||||
if (!targetWallet || !nftMintStr) throw new Error("Usage: node 05_propose_vote_mint_nft.js <config.env> <target_wallet> <nft_mint>");
|
||||
if (!targetWallet || !nftMintStr) throw new Error("Usage: node 06_propose_vote_mint_nft.js <config.env> <target_wallet> <nft_mint>");
|
||||
|
||||
const cfg = parseEnvConfig(resolveConfigPath(process.argv[2], "nft-flow.config.env"));
|
||||
const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed");
|
||||
+1
-1
@@ -13,7 +13,7 @@ async function main() {
|
||||
const proposalTx = new PublicKey(process.argv[4]);
|
||||
const target = new PublicKey(process.argv[5]);
|
||||
const nftMint = new PublicKey(process.argv[6]);
|
||||
if (!process.argv[6]) throw new Error("Usage: node 06_execute_mint_nft.js <config.env> <proposal> <proposalTx> <targetWallet> <nftMint>");
|
||||
if (!process.argv[6]) throw new Error("Usage: node 07_execute_mint_nft.js <config.env> <proposal> <proposalTx> <targetWallet> <nftMint>");
|
||||
|
||||
const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed");
|
||||
const main = loadKeypair(path.resolve(__dirname, "..", cfg.MAIN_KEYPAIR));
|
||||
+1
-1
@@ -21,7 +21,7 @@ const { clusterUrl, ensureDir, loadKeypair, nowStamp, parseEnvConfig, resolveCon
|
||||
async function main() {
|
||||
const currentOwnerWallet = process.argv[3];
|
||||
const nftMintStr = process.argv[4];
|
||||
if (!currentOwnerWallet || !nftMintStr) throw new Error("Usage: node 07_propose_vote_burn_nft.js <config.env> <current_owner_wallet> <nft_mint>");
|
||||
if (!currentOwnerWallet || !nftMintStr) throw new Error("Usage: node 08_propose_vote_burn_nft.js <config.env> <current_owner_wallet> <nft_mint>");
|
||||
|
||||
const cfg = parseEnvConfig(resolveConfigPath(process.argv[2], "nft-flow.config.env"));
|
||||
const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed");
|
||||
+1
-1
@@ -13,7 +13,7 @@ async function main() {
|
||||
const proposalTx = new PublicKey(process.argv[4]);
|
||||
const currentOwner = new PublicKey(process.argv[5]);
|
||||
const nftMint = new PublicKey(process.argv[6]);
|
||||
if (!process.argv[6]) throw new Error("Usage: node 08_execute_burn_nft.js <config.env> <proposal> <proposalTx> <currentOwnerWallet> <nftMint>");
|
||||
if (!process.argv[6]) throw new Error("Usage: node 09_execute_burn_nft.js <config.env> <proposal> <proposalTx> <currentOwnerWallet> <nftMint>");
|
||||
|
||||
const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed");
|
||||
const main = loadKeypair(path.resolve(__dirname, "..", cfg.MAIN_KEYPAIR));
|
||||
Reference in New Issue
Block a user