diff --git a/Anchor.toml b/Anchor.toml index 7a97719b..74463e62 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -50,6 +50,14 @@ v06-create-dao = "yarn run tsx scripts/v0.6/createDao.ts" v06-provide-liquidity = "yarn run tsx scripts/v0.6/provideLiquidity.ts" v06-collect-meteora-damm-fees = "yarn run tsx scripts/v0.6/collectMeteoraDammFees.ts" v07-collect-meteora-damm-fees = "yarn run tsx scripts/v0.7/collectMeteoraDammFees.ts" +v07-launch-template = "yarn run tsx scripts/v0.7/launchTemplate.ts" +v07-start-launch = "yarn run tsx scripts/v0.7/startLaunch.ts" +v07-complete-launch = "yarn run tsx scripts/v0.7/completeLaunch.ts" +v07-claim-all-launch = "yarn run tsx scripts/v0.7/claimAllLaunch.ts" +v07-approve-points-based = "yarn run tsx scripts/v0.7/pointsBased/approveWithPointsWeightedPhase.ts" +v07-close-launch = "yarn run tsx scripts/v0.7/closeLaunch.ts" +v07-initialize-performance-package = "yarn run tsx scripts/v0.7/initializePerformancePackage.ts" +v07-claim-launch-additional-tokens = "yarn run tsx scripts/v0.7/claimLaunchAdditionalTokens.ts" [test] startup_wait = 5000 diff --git a/scripts/assets/RNGR/RNGR.json b/scripts/assets/RNGR/RNGR.json new file mode 100644 index 00000000..adce18bd --- /dev/null +++ b/scripts/assets/RNGR/RNGR.json @@ -0,0 +1,6 @@ +{ + "name": "Ranger", + "symbol": "RNGR", + "description": "Futarchy ownership token of Ranger", + "image": "https://raw.githubusercontent.com/metaDAOproject/programs/refs/heads/develop/scripts/assets/RNGR/RNGR.png" +} \ No newline at end of file diff --git a/scripts/assets/RNGR/RNGR.png b/scripts/assets/RNGR/RNGR.png new file mode 100644 index 00000000..cdd46209 Binary files /dev/null and b/scripts/assets/RNGR/RNGR.png differ diff --git a/scripts/v0.7/claimAllLaunch.ts b/scripts/v0.7/claimAllLaunch.ts index 2aa5e7eb..5e11dba3 100644 --- a/scripts/v0.7/claimAllLaunch.ts +++ b/scripts/v0.7/claimAllLaunch.ts @@ -13,9 +13,7 @@ dotenv.config(); const provider = anchor.AnchorProvider.env(); const payer = provider.wallet["payer"]; -const launchAddr = new PublicKey( - "9kx7UDFzFt7e2V4pFtawnupKKvRR3EhV7P1Pxmc5XCQj", -); +const launchAddr = new PublicKey("111111111111111111111111111111111"); const launchpad: LaunchpadClient = LaunchpadClient.createClient({ provider }); @@ -54,32 +52,37 @@ async function main() { const tx = new Transaction(); // Add compute budget instruction to handle multiple claims - tx.add(ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 })); + // tx.add(ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 })); // Add claim instructions for each record in the batch for (const record of batch) { - const claimIx = await launchpad - .claimIx(launchAddr, launch.baseMint, record.account.funder) - .transaction(); + if (!record.account.isTokensClaimed) { + const claimIx = await launchpad + .claimIx(launchAddr, launch.baseMint, record.account.funder) + .transaction(); - tx.add(claimIx); + tx.add(claimIx); + } } - await sendAndConfirmTransaction(tx, `Claim batch ${i / batchSize + 1}`); - for (const record of batch) { - const refundIx = await launchpad - .refundIx({ - launch: launchAddr, - funder: record.account.funder, - quoteMint: launch.baseMint, - }) - .transaction(); - - tx.add(refundIx); + if (!record.account.isUsdcRefunded) { + const refundIx = await launchpad + .refundIx({ + launch: launchAddr, + funder: record.account.funder, + quoteMint: launch.quoteMint, + }) + .transaction(); + + tx.add(refundIx); + } } - await sendAndConfirmTransaction(tx, `Refund batch ${i / batchSize + 1}`); + await sendAndConfirmTransaction( + tx, + `Claim and refund batch ${i / batchSize + 1}`, + ); } console.log("All claims processed successfully!"); diff --git a/scripts/v0.7/claimLaunchAdditionalTokens.ts b/scripts/v0.7/claimLaunchAdditionalTokens.ts new file mode 100644 index 00000000..e29090f4 --- /dev/null +++ b/scripts/v0.7/claimLaunchAdditionalTokens.ts @@ -0,0 +1,39 @@ +import * as anchor from "@coral-xyz/anchor"; +import { LaunchpadClient } from "@metadaoproject/futarchy/v0.7"; +import { PublicKey } from "@solana/web3.js"; + +const provider = anchor.AnchorProvider.env(); +const payer = provider.wallet["payer"]; + +const launchpad: LaunchpadClient = LaunchpadClient.createClient({ provider }); + +const launch = new PublicKey("HUM66D4uRnWTFHm3fJkA2rvQWqpHnJZWgRUt5QNxWF6E"); + +export const closeLaunch = async () => { + console.log( + `Claiming additional token allocation for launch at address: ${launch.toBase58()}`, + ); + + const launchAccount = await launchpad.fetchLaunch(launch); + + console.log( + `Additional tokens recipient: ${launchAccount.additionalTokensRecipient.toBase58()}`, + ); + console.log( + `Additional tokens amount: ${launchAccount.additionalTokensAmount.toString()}`, + ); + + const txSignature = await launchpad + .claimAdditionalTokenAllocationIx({ + launch, + baseMint: launchAccount.baseMint, + additionalTokensRecipient: launchAccount.additionalTokensRecipient, + }) + .rpc(); + + console.log( + `Additional token allocation claimed successfully: ${txSignature}`, + ); +}; + +closeLaunch().catch(console.error); diff --git a/scripts/v0.7/closeLaunch.ts b/scripts/v0.7/closeLaunch.ts index 8439d415..6d985a77 100644 --- a/scripts/v0.7/closeLaunch.ts +++ b/scripts/v0.7/closeLaunch.ts @@ -7,12 +7,10 @@ const payer = provider.wallet["payer"]; const launchpad: LaunchpadClient = LaunchpadClient.createClient({ provider }); -export const closeLaunch = async () => { - const mintKp = new PublicKey("PRVT6TB7uss3FrUd2D9xs2zqDBsa3GbMJMwCQsgmeta"); - - const [launch] = getLaunchAddr(undefined, mintKp); +const launch = new PublicKey("111111111111111111111111111111111"); - console.log(`Closing launch at address: ${launch.toString()}`); +export const closeLaunch = async () => { + console.log(`Closing launch at address: ${launch.toBase58()}`); await launchpad.closeLaunchIx({ launch }).rpc(); diff --git a/scripts/v0.7/completeLaunch.ts b/scripts/v0.7/completeLaunch.ts index 5be54a8b..a471513a 100644 --- a/scripts/v0.7/completeLaunch.ts +++ b/scripts/v0.7/completeLaunch.ts @@ -7,30 +7,29 @@ import { } from "@solana/web3.js"; import { createLookupTableForTransaction } from "../utils/utils.js"; +const LAUNCH_TO_COMPLETE: PublicKey | undefined = new PublicKey( + "111111111111111111111111111111111", +); + const provider = anchor.AnchorProvider.env(); const payer = provider.wallet["payer"]; const launchpad: LaunchpadClient = LaunchpadClient.createClient({ provider }); -const BID_WALL_FEE_RECIPIENT: PublicKey | undefined = undefined; - export const completeLaunch = async () => { - if (BID_WALL_FEE_RECIPIENT === undefined) { + if (LAUNCH_TO_COMPLETE === undefined) { throw new Error( - "BID_WALL_FEE_RECIPIENT is not set. Please set it in the script.", + "LAUNCH_TO_COMPLETE is not set. Please set it in the script.", ); } - const mintKp = new PublicKey("PRVT6TB7uss3FrUd2D9xs2zqDBsa3GbMJMwCQsgmeta"); - - const [launch] = getLaunchAddr(undefined, mintKp); + let launchAccount = await launchpad.fetchLaunch(LAUNCH_TO_COMPLETE); const tx = await launchpad .completeLaunchIx({ - launch, - baseMint: mintKp, + launch: LAUNCH_TO_COMPLETE, + baseMint: launchAccount.baseMint, launchAuthority: payer.publicKey, - feeRecipient: BID_WALL_FEE_RECIPIENT, }) .transaction(); @@ -51,9 +50,7 @@ export const completeLaunch = async () => { const vtx = new VersionedTransaction(message); vtx.sign([payer]); - const completeTxHash = await provider.connection.sendTransaction(vtx, { - skipPreflight: true, - }); + const completeTxHash = await provider.connection.sendTransaction(vtx); console.log(`Complete launch transaction sent: ${completeTxHash}`); @@ -61,10 +58,13 @@ export const completeLaunch = async () => { console.log("Setting up performance package..."); + // Refresh launch account to get the updated base mint + launchAccount = await launchpad.fetchLaunch(LAUNCH_TO_COMPLETE); + const initializePerformancePackageTxHash = await launchpad .initializePerformancePackageIx({ - launch, - baseMint: mintKp, + launch: LAUNCH_TO_COMPLETE, + baseMint: launchAccount.baseMint, payer: payer.publicKey, }) .rpc(); diff --git a/scripts/v0.7/initializePerformancePackage.ts b/scripts/v0.7/initializePerformancePackage.ts new file mode 100644 index 00000000..b1faf5a0 --- /dev/null +++ b/scripts/v0.7/initializePerformancePackage.ts @@ -0,0 +1,40 @@ +import * as anchor from "@coral-xyz/anchor"; +import { LaunchpadClient } from "@metadaoproject/futarchy/v0.7"; +import { PublicKey } from "@solana/web3.js"; + +const LAUNCH_TO_COMPLETE: PublicKey | undefined = new PublicKey( + "111111111111111111111111111111111", +); + +const provider = anchor.AnchorProvider.env(); +const payer = provider.wallet["payer"]; + +const launchpad: LaunchpadClient = LaunchpadClient.createClient({ provider }); + +export const completeLaunch = async () => { + if (LAUNCH_TO_COMPLETE === undefined) { + throw new Error( + "LAUNCH_TO_COMPLETE is not set. Please set it in the script.", + ); + } + + const launchAccount = await launchpad.fetchLaunch(LAUNCH_TO_COMPLETE); + + console.log("Setting up performance package..."); + + const initializePerformancePackageTxHash = await launchpad + .initializePerformancePackageIx({ + launch: LAUNCH_TO_COMPLETE, + baseMint: launchAccount.baseMint, + payer: payer.publicKey, + }) + .rpc(); + + console.log( + `Initialize performance package transaction sent: ${initializePerformancePackageTxHash}`, + ); + + console.log("Performance package set up successfully!"); +}; + +completeLaunch().catch(console.error); diff --git a/scripts/v0.7/launchRNGR.ts b/scripts/v0.7/launchRNGR.ts new file mode 100644 index 00000000..5c0381ee --- /dev/null +++ b/scripts/v0.7/launchRNGR.ts @@ -0,0 +1,115 @@ +import * as anchor from "@coral-xyz/anchor"; +import { + LaunchpadClient, + getLaunchAddr, + getLaunchSignerAddr, +} from "@metadaoproject/futarchy/v0.7"; +import { PublicKey, SystemProgram, Transaction } from "@solana/web3.js"; +import BN from "bn.js"; +import * as token from "@solana/spl-token"; + +const provider = anchor.AnchorProvider.env(); +const payer = provider.wallet["payer"]; + +const LAUNCH_AUTHORITY = payer.publicKey; + +const TEAM_ADDRESS = new PublicKey( + "CHQXGzhweUQ4wrWCta6koL4iQGZNdWQuJmGHxHpRzjBq", +); // Ranger team address + +// Launch details +const MIN_GOAL = 6_000_000; // 6M USDC + +const SPENDING_MEMBERS = [ + new PublicKey("BXDKN7Bb9s5TPUJksT1HfGgDpYvEGnPwagostU79hAM5"), + new PublicKey("5aaFjteWwEeDPZ69j8BmqsGS1KviqzG4V8CMedCkbkhe"), +]; +const SPENDING_LIMIT = 250_000; // 250k USDC + +const PERFORMANCE_PACKAGE_GRANTEE = TEAM_ADDRESS; +const PERFORMANCE_PACKAGE_TOKEN_AMOUNT = 7_600_000; // 7.6M RNGR +const PERFORMANCE_PACKAGE_UNLOCK_MONTHS = 18; // 18 months + +// Additional carveout details +const ADDITIONAL_CARVEOUT = 5_125_000; // 5.125M RNGR +const ADDITIONAL_CARVEOUT_RECIPIENT = new PublicKey( + "6awyHMshBGVjJ3ozdSJdyyDE1CTAXUwrpNMaRGMsb4sf", +); // MetaDAO operational multisig vault + +const TOKEN_SEED = "S6Bc84f7fzY6eviV"; +const TOKEN_NAME = "Ranger"; +const TOKEN_SYMBOL = "RNGR"; +const TOKEN_URI = + "https://raw.githubusercontent.com/metaDAOproject/programs/refs/heads/develop/scripts/assets/RNGR/RNGR.json"; + +const secondsPerDay = 86_400; +const numberOfDays = 4; +const launchDurationSeconds = secondsPerDay * numberOfDays; // 4 days + +const launchpad: LaunchpadClient = LaunchpadClient.createClient({ provider }); + +export const launch = async () => { + const lamports = await provider.connection.getMinimumBalanceForRentExemption( + token.MINT_SIZE, + ); + + const TOKEN = await PublicKey.createWithSeed( + payer.publicKey, + TOKEN_SEED, + token.TOKEN_PROGRAM_ID, + ); + console.log("Token address:", TOKEN.toBase58()); + + const [launch] = getLaunchAddr(undefined, TOKEN); + const [launchSigner] = getLaunchSignerAddr(undefined, launch); + + const tx = new Transaction().add( + SystemProgram.createAccountWithSeed({ + fromPubkey: payer.publicKey, + newAccountPubkey: TOKEN, + basePubkey: payer.publicKey, + seed: TOKEN_SEED, + lamports: lamports, + space: token.MINT_SIZE, + programId: token.TOKEN_PROGRAM_ID, + }), + token.createInitializeMint2Instruction(TOKEN, 6, launchSigner, null), + ); + tx.recentBlockhash = ( + await provider.connection.getLatestBlockhash() + ).blockhash; + tx.feePayer = payer.publicKey; + tx.sign(payer); + + const txHash = await provider.connection.sendRawTransaction(tx.serialize()); + await provider.connection.confirmTransaction(txHash, "confirmed"); + + const launchIx = await launchpad + .initializeLaunchIx({ + tokenName: TOKEN_NAME, + tokenSymbol: TOKEN_SYMBOL, + tokenUri: TOKEN_URI, + minimumRaiseAmount: new BN(MIN_GOAL * 10 ** 6), + baseMint: TOKEN, + monthlySpendingLimitAmount: new BN(SPENDING_LIMIT * 10 ** 6), + monthlySpendingLimitMembers: SPENDING_MEMBERS, + performancePackageGrantee: PERFORMANCE_PACKAGE_GRANTEE, + performancePackageTokenAmount: new BN( + PERFORMANCE_PACKAGE_TOKEN_AMOUNT * 10 ** 6, + ), + monthsUntilInsidersCanUnlock: PERFORMANCE_PACKAGE_UNLOCK_MONTHS, + secondsForLaunch: launchDurationSeconds, + teamAddress: TEAM_ADDRESS, + additionalTokensAmount: ADDITIONAL_CARVEOUT + ? new BN(ADDITIONAL_CARVEOUT * 10 ** 6) + : undefined, + additionalTokensRecipient: ADDITIONAL_CARVEOUT_RECIPIENT, + launchAuthority: LAUNCH_AUTHORITY, + }) + .rpc(); + + console.log("Launch initialized", launchIx); + // await launchpad.startLaunchIx({ launch }).rpc(); +}; + +launch().catch(console.error); diff --git a/scripts/v0.7/launchTemplate.ts b/scripts/v0.7/launchTemplate.ts index 3b41fa24..9ade1d75 100644 --- a/scripts/v0.7/launchTemplate.ts +++ b/scripts/v0.7/launchTemplate.ts @@ -13,31 +13,32 @@ const payer = provider.wallet["payer"]; const LAUNCH_AUTHORITY = payer.publicKey; -const TEAM_ADDRESS = PublicKey.default; +const TEAM_ADDRESS = new PublicKey("111111111111111111111111111111111"); // Launch details -const MIN_GOAL = 500_000; +const MIN_GOAL = 10; const SPENDING_MEMBERS = [TEAM_ADDRESS]; -const SPENDING_LIMIT = 60_000; +const SPENDING_LIMIT = 1; const PERFORMANCE_PACKAGE_GRANTEE = TEAM_ADDRESS; -const PERFORMANCE_PACKAGE_TOKEN_AMOUNT = 8_076_923; +const PERFORMANCE_PACKAGE_TOKEN_AMOUNT = 7_600_000; const PERFORMANCE_PACKAGE_UNLOCK_MONTHS = 18; // Additional carveout details - leave undefined if not used -const ADDITIONAL_CARVEOUT = undefined; -const ADDITIONAL_CARVEOUT_RECIPIENT = undefined; +const ADDITIONAL_CARVEOUT: number | undefined = undefined; +const ADDITIONAL_CARVEOUT_RECIPIENT: PublicKey | undefined = undefined; -const TOKEN_SEED = "YacrMS3w7lcgi44t"; -const TOKEN_NAME = "Loyal"; -const TOKEN_SYMBOL = "LOYAL"; +const TOKEN_SEED = "YacrMS3w7lcgi44d"; +const TOKEN_NAME = "Test"; +const TOKEN_SYMBOL = "TEST"; const TOKEN_URI = "https://raw.githubusercontent.com/metaDAOproject/futarchy/refs/heads/develop/scripts/assets/LOYAL/LOYAL.json"; const secondsPerDay = 86_400; const numberOfDays = 4; -const launchDurationSeconds = secondsPerDay * numberOfDays; +// const launchDurationSeconds = secondsPerDay * numberOfDays; +const launchDurationSeconds = 3600; const launchpad: LaunchpadClient = LaunchpadClient.createClient({ provider }); @@ -77,7 +78,7 @@ export const launch = async () => { const txHash = await provider.connection.sendRawTransaction(tx.serialize()); await provider.connection.confirmTransaction(txHash, "confirmed"); - const launchIx = await launchpad + const initializeLaunchTxSignature = await launchpad .initializeLaunchIx({ tokenName: TOKEN_NAME, tokenSymbol: TOKEN_SYMBOL, @@ -101,7 +102,9 @@ export const launch = async () => { }) .rpc(); - console.log("Launch initialized", launchIx); + console.log("Launch initialized", initializeLaunchTxSignature); + + console.log("Launch address:", launch.toBase58()); // await launchpad.startLaunchIx({ launch }).rpc(); }; diff --git a/scripts/v0.7/pointsBased/.gitignore b/scripts/v0.7/pointsBased/.gitignore new file mode 100644 index 00000000..2a85ed5c --- /dev/null +++ b/scripts/v0.7/pointsBased/.gitignore @@ -0,0 +1 @@ +points.json \ No newline at end of file diff --git a/scripts/v0.7/pointsBased/approveOnlyPointsOwnersProRata.ts b/scripts/v0.7/pointsBased/approveWithPointsWeightedPhase.ts similarity index 81% rename from scripts/v0.7/pointsBased/approveOnlyPointsOwnersProRata.ts rename to scripts/v0.7/pointsBased/approveWithPointsWeightedPhase.ts index ce0147b5..9e384a97 100644 --- a/scripts/v0.7/pointsBased/approveOnlyPointsOwnersProRata.ts +++ b/scripts/v0.7/pointsBased/approveWithPointsWeightedPhase.ts @@ -26,19 +26,20 @@ const payer = provider.wallet["payer"]; const launchpad: LaunchpadClient = LaunchpadClient.createClient({ provider }); // How many approvals to perform per transaction -const batchSize = 5; +const batchSize = 20; // The launch address -const launchAddr = new PublicKey( - "9kx7UDFzFt7e2V4pFtawnupKKvRR3EhV7P1Pxmc5XCQj", -); +const launchAddr = new PublicKey("111111111111111111111111111111111"); // The final raise amount (USDC, in atoms) -const finalRaiseAmount = 1_000_000_000000; +const finalRaiseAmount = 20_000000; async function main() { const pointsAllocations: PointsAllocation[] = JSON.parse( - fs.readFileSync(path.join(__dirname, "pointsAllocations.json"), "utf8"), + fs.readFileSync( + path.join(process.cwd(), "scripts/v0.7/pointsBased/points.json"), + "utf8", + ), ).map((x) => ({ user: new PublicKey(x.user), points: x.points, @@ -95,6 +96,8 @@ async function main() { if (fundingRecordsWithExistingPointsOwners.length === 0) { console.log("No funding records with points owners found"); + + // Remove this if you want to continue with the script despite no funding records with points owners found return; } @@ -114,10 +117,15 @@ async function main() { ); // Assign amount to approve to each record - for (const record of allFundingRecordsWithPointsOwners) { - record.amountToApprove = record.account.committedAmount - .mul(new BN(record.pointsOwner?.points ?? 0)) // In this phase, if there is no points owner, then they get no allocation, so we multiply by 0. - .div(totalPointsWithinLaunch); + if (totalPointsWithinLaunch.gt(new BN(0))) { + for (const record of allFundingRecordsWithPointsOwners) { + record.amountToApprove = BN.min( + record.account.committedAmount, + new BN(finalRaiseAmount) + .mul(new BN(record.pointsOwner?.points ?? 0)) // In this phase, if there is no points owner, then they get no allocation, so we multiply by 0. + .div(totalPointsWithinLaunch), + ); + } } // Sum up total amount to approve @@ -152,14 +160,21 @@ async function main() { } } - // // Uncomment this if we want the final raise amount to be a bit over the total committed amount - // // This might be important if we want to ensure that the launch is successful in cases where the final raise amount is exactly equal to the minimum raise amount. - // // A small dust difference will occur due to rounding which could normally fail the launch, so we need to add 1 to all of the records that have an amount to approve that is not equal to the committed amount. - // for (const record of allFundingRecordsWithPointsOwners) { - // if (record.amountToApprove.lt(record.account.committedAmount)) { - // record.amountToApprove = record.amountToApprove.add(new BN(1)); - // } - // } + // IMPORTANT - PLEASE READ + // Uncomment this if we want the final raise amount to be a bit over the total committed amount + // This might be important if we want to ensure that the launch is successful in cases where the final raise amount is exactly equal to the minimum raise amount. + // A small dust difference will occur due to rounding which could normally fail the launch, so we need to add 1 atom to all of the records that have an amount to approve that is not equal to the committed amount. + for (const record of allFundingRecordsWithPointsOwners) { + if (record.amountToApprove.lt(record.account.committedAmount)) { + record.amountToApprove = record.amountToApprove.add(new BN(1)); + } + } + + for (const record of allFundingRecordsWithPointsOwners) { + console.log( + `${record.account.funder.toBase58()}:\t${record.amountToApprove.toString()}`, + ); + } // Sum up total amount to approve and render it to the user const finalAmountToApprove = allFundingRecordsWithPointsOwners.reduce( diff --git a/scripts/v0.7/pointsBased/points.json.example b/scripts/v0.7/pointsBased/points.json.example new file mode 100644 index 00000000..942b1385 --- /dev/null +++ b/scripts/v0.7/pointsBased/points.json.example @@ -0,0 +1,4 @@ +[ + { "user": "11111111111111111111111111111111", "points": 100 }, + { "user": "22222222222222222222222222222222", "points": 2 } +] diff --git a/scripts/v0.7/startLaunch.ts b/scripts/v0.7/startLaunch.ts new file mode 100644 index 00000000..e898dcd8 --- /dev/null +++ b/scripts/v0.7/startLaunch.ts @@ -0,0 +1,72 @@ +import { Keypair, PublicKey, Transaction } from "@solana/web3.js"; +import * as anchor from "@coral-xyz/anchor"; +import { LaunchpadClient } from "@metadaoproject/futarchy/v0.7"; + +import dotenv from "dotenv"; + +dotenv.config(); + +const provider = anchor.AnchorProvider.env(); +const payer = provider.wallet["payer"]; + +const LAUNCH_TO_START = new PublicKey("111111111111111111111111111111111"); + +const launchpad: LaunchpadClient = LaunchpadClient.createClient({ provider }); + +async function main() { + const launchAuthorityKeypair = payer; + + console.log( + "Launch authority public key:", + launchAuthorityKeypair.publicKey.toBase58(), + ); + + console.log("Starting launch..."); + + const tx = await launchpad + .startLaunchIx({ + launch: LAUNCH_TO_START, + launchAuthority: launchAuthorityKeypair.publicKey, + }) + .transaction(); + + await sendAndConfirmTransaction(tx, "Start launch", [launchAuthorityKeypair]); + + console.log("Launch started!"); + console.log("Launch address:", LAUNCH_TO_START.toBase58()); +} + +// Make sure the promise rejection is handled +main().catch((error) => { + console.error("Fatal error:", error); + process.exit(1); +}); + +async function sendAndConfirmTransaction( + tx: Transaction, + label: string, + signers: Keypair[] = [], +) { + tx.feePayer = payer.publicKey; + tx.recentBlockhash = ( + await provider.connection.getLatestBlockhash() + ).blockhash; + tx.partialSign(payer, ...signers); + const txHash = await provider.connection.sendRawTransaction(tx.serialize()); + console.log(`${label} transaction sent:`, txHash); + + await provider.connection.confirmTransaction(txHash, "confirmed"); + const txStatus = await provider.connection.getTransaction(txHash, { + maxSupportedTransactionVersion: 0, + commitment: "confirmed", + }); + if (txStatus?.meta?.err) { + throw new Error( + `Transaction failed: ${txHash}\nError: ${JSON.stringify( + txStatus?.meta?.err, + )}\n\n${txStatus?.meta?.logMessages?.join("\n")}`, + ); + } + console.log(`${label} transaction confirmed`); + return txHash; +}