Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ v07-dump-launches-funding-records = "yarn run tsx scripts/v0.7/dumpLaunchesAndFu
v07-resize-launches-funding-records = "yarn run tsx scripts/v0.7/resizeLaunchesAndFundingRecords.ts"
v07-dump-launches = "yarn run tsx scripts/v0.7/dumpLaunches.ts"
v07-resize-launches = "yarn run tsx scripts/v0.7/resizeLaunches.ts"
v07-extend-launch = "yarn run tsx scripts/v0.7/extendLaunch.ts"

[test]
startup_wait = 5000
Expand Down
2 changes: 2 additions & 0 deletions programs/v07_launchpad/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,6 @@ pub enum LaunchpadError {
InvalidDao,
#[msg("Accumulator activation delay must be less than the launch duration")]
InvalidAccumulatorActivationDelaySeconds,
#[msg("The extend duration would exceed the maximum allowed launch duration")]
ExtendDurationExceedsMax,
}
8 changes: 8 additions & 0 deletions programs/v07_launchpad/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,11 @@ pub struct LaunchPerformancePackageInitializedEvent {
pub launch: Pubkey,
pub performance_package: Pubkey,
}

#[event]
pub struct LaunchExtendedEvent {
pub common: CommonFields,
pub launch: Pubkey,
pub old_seconds_for_launch: u32,
pub new_seconds_for_launch: u32,
}
7 changes: 4 additions & 3 deletions programs/v07_launchpad/src/instructions/complete_launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ use crate::error::LaunchpadError;
use crate::events::{CommonFields, LaunchCloseEvent, LaunchCompletedEvent};
use crate::state::{Launch, LaunchState};
use crate::{
fee_recipient, PRICE_SCALE, PROPOSAL_MIN_STAKE_TOKENS, TOKENS_TO_DAMM_V2_LIQUIDITY_UNSCALED,
TOKENS_TO_FUTARCHY_LIQUIDITY, TOKENS_TO_PARTICIPANTS, TOKEN_SCALE,
metadao_multisig_vault, PRICE_SCALE, PROPOSAL_MIN_STAKE_TOKENS,
TOKENS_TO_DAMM_V2_LIQUIDITY_UNSCALED, TOKENS_TO_FUTARCHY_LIQUIDITY, TOKENS_TO_PARTICIPANTS,
TOKEN_SCALE,
};
use anchor_spl::metadata::{
mpl_token_metadata::ID as MPL_TOKEN_METADATA_PROGRAM_ID, update_metadata_accounts_v2, Metadata,
Expand Down Expand Up @@ -213,7 +214,7 @@ pub struct CompleteLaunch<'info> {
pub bid_wall_quote_token_account: UncheckedAccount<'info>,

/// CHECK: The fee recipient of bid wall fees, a fixed address
#[account(address = fee_recipient::id())]
#[account(address = metadao_multisig_vault::id())]
pub fee_recipient: AccountInfo<'info>,

pub system_program: Program<'info, System>,
Expand Down
69 changes: 69 additions & 0 deletions programs/v07_launchpad/src/instructions/extend_launch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use anchor_lang::prelude::*;

use crate::error::LaunchpadError;
use crate::events::{CommonFields, LaunchExtendedEvent};
use crate::state::{Launch, LaunchState};

#[cfg(feature = "production")]
use crate::metadao_multisig_vault;

#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub struct ExtendLaunchArgs {
pub duration_seconds: u32,
}

#[event_cpi]
#[derive(Accounts)]
pub struct ExtendLaunch<'info> {
#[account(mut)]
pub launch: Account<'info, Launch>,

pub admin: Signer<'info>,
}

impl ExtendLaunch<'_> {
pub fn validate(&self, args: &ExtendLaunchArgs) -> Result<()> {
#[cfg(feature = "production")]
require_keys_eq!(self.admin.key(), metadao_multisig_vault::ID);

require!(
self.launch.state == LaunchState::Live,
LaunchpadError::InvalidLaunchState
);

require_gt!(args.duration_seconds, 0, LaunchpadError::InvalidAmount);

require!(
self.launch
.seconds_for_launch
.checked_add(args.duration_seconds)
.is_some(),
LaunchpadError::ExtendDurationExceedsMax
);

Ok(())
}

pub fn handle(ctx: Context<Self>, args: ExtendLaunchArgs) -> Result<()> {
let launch = &mut ctx.accounts.launch;
let clock = Clock::get()?;

let old_seconds_for_launch = launch.seconds_for_launch;

launch.seconds_for_launch = launch
.seconds_for_launch
.checked_add(args.duration_seconds)
.unwrap();

launch.seq_num += 1;

emit_cpi!(LaunchExtendedEvent {
common: CommonFields::new(&clock, launch.seq_num),
launch: launch.key(),
old_seconds_for_launch,
new_seconds_for_launch: launch.seconds_for_launch,
});

Ok(())
}
}
2 changes: 2 additions & 0 deletions programs/v07_launchpad/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod claim;
pub mod claim_additional_token_allocation;
pub mod close_launch;
pub mod complete_launch;
pub mod extend_launch;
pub mod fund;
pub mod initialize_launch;
pub mod initialize_performance_package;
Expand All @@ -15,6 +16,7 @@ pub use claim::*;
pub use claim_additional_token_allocation::*;
pub use close_launch::*;
pub use complete_launch::*;
pub use extend_launch::*;
pub use fund::*;
pub use initialize_launch::*;
pub use initialize_performance_package::*;
Expand Down
10 changes: 7 additions & 3 deletions programs/v07_launchpad/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,10 @@ pub mod usdc_mint {
declare_id!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
}

// TODO - Pileks: Set this to the correct fee recipient address
pub mod fee_recipient {
pub mod metadao_multisig_vault {
use anchor_lang::prelude::declare_id;

// MetaDAO multisig vault
// MetaDAO operations multisig vault
declare_id!("6awyHMshBGVjJ3ozdSJdyyDE1CTAXUwrpNMaRGMsb4sf");
}

Expand Down Expand Up @@ -129,4 +128,9 @@ pub mod launchpad_v7 {
pub fn resize_launch(ctx: Context<ResizeLaunch>) -> Result<()> {
ResizeLaunch::handle(ctx)
}

#[access_control(ctx.accounts.validate(&args))]
pub fn extend_launch(ctx: Context<ExtendLaunch>, args: ExtendLaunchArgs) -> Result<()> {
ExtendLaunch::handle(ctx, args)
}
}
118 changes: 118 additions & 0 deletions scripts/v0.7/extendLaunch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import * as anchor from "@coral-xyz/anchor";
import * as multisig from "@sqds/multisig";
import {
LaunchpadClient,
METADAO_MULTISIG_VAULT,
} from "@metadaoproject/futarchy/v0.7";
import { PublicKey, Transaction, TransactionMessage } from "@solana/web3.js";

// Set the launch address before running the script
const launch = new PublicKey("");

// Set the number of seconds to extend the launch by
const durationSeconds = 60 * 60 * 24; // 1 day

const provider = anchor.AnchorProvider.env();

// Payer MUST be a signer with permissions to propose transactions on MetaDAO's multisig
const payer = provider.wallet["payer"];

const launchpad: LaunchpadClient = LaunchpadClient.createClient({ provider });

// MetaDAO Squads multisig and vault addresses
const metadaoSquadsMultisig = new PublicKey(
"8N3Tvc6B1wEVKVC6iD4s6eyaCNqX2ovj2xze2q3Q9DWH",
);
const metadaoSquadsMultisigVault = METADAO_MULTISIG_VAULT;

export const extendLaunch = async () => {
const launchAccount = await launchpad.getLaunch(launch);

console.log(`Extending launch: ${launch.toBase58()}`);
console.log(`Current seconds_for_launch: ${launchAccount.secondsForLaunch}`);
console.log(`Extension: ${durationSeconds} seconds`);
console.log(
`New seconds_for_launch: ${launchAccount.secondsForLaunch + durationSeconds}`,
);

// Build the extend_launch instruction
const extendLaunchIx = await launchpad
.extendLaunchIx({
launch,
durationSeconds,
admin: metadaoSquadsMultisigVault,
})
.instruction();

// Build the transaction message with the multisig vault as payer
const transactionMessage = new TransactionMessage({
instructions: [extendLaunchIx],
payerKey: metadaoSquadsMultisigVault,
recentBlockhash: (await provider.connection.getLatestBlockhash()).blockhash,
});

// Log the transaction message as base64
const compiledMessage = transactionMessage.compileToLegacyMessage();
const base64Message = Buffer.from(compiledMessage.serialize()).toString(
"base64",
);
console.log("\nTransaction message (base64):");
console.log(base64Message);

// TODO: Uncomment this when ready to extend the launch
return;

// Fetch the current multisig state to get the next transaction index
const metaDaoSquadsMultisigAccount =
await multisig.accounts.Multisig.fromAccountAddress(
provider.connection,
metadaoSquadsMultisig,
);

const transactionIndex =
BigInt(metaDaoSquadsMultisigAccount.transactionIndex.toString()) + 1n;

// Create vault transaction instruction
const vaultTxCreateIx = multisig.instructions.vaultTransactionCreate({
multisigPda: metadaoSquadsMultisig,
transactionIndex,
creator: payer.publicKey,
rentPayer: payer.publicKey,
vaultIndex: 0,
ephemeralSigners: 0,
transactionMessage,
});

// Create proposal instruction
const proposalCreateIx = multisig.instructions.proposalCreate({
multisigPda: metadaoSquadsMultisig,
transactionIndex,
creator: payer.publicKey,
rentPayer: payer.publicKey,
isDraft: false,
});

// Build, sign, and send the transaction
const tx = new Transaction().add(vaultTxCreateIx, proposalCreateIx);
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 [proposalPda] = multisig.getProposalPda({
multisigPda: metadaoSquadsMultisig,
transactionIndex,
});

console.log("\nVault transaction + proposal created successfully!");
console.log("Transaction signature:", txHash);
console.log("Proposal index:", transactionIndex.toString());
console.log("Proposal PDA:", proposalPda.toBase58());
console.log("Go ahead and approve/execute the transaction through Squads.");
};

extendLaunch().catch(console.error);
15 changes: 15 additions & 0 deletions sdk/src/v0.7/LaunchpadClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,21 @@ export class LaunchpadClient {
});
}

extendLaunchIx({
launch,
durationSeconds,
admin = METADAO_MULTISIG_VAULT,
}: {
launch: PublicKey;
durationSeconds: number;
admin?: PublicKey;
}) {
return this.launchpad.methods.extendLaunch({ durationSeconds }).accounts({
launch,
admin,
});
}

getLaunchAddress({ baseMint }: { baseMint: PublicKey }): PublicKey {
return getLaunchAddr(this.launchpad.programId, baseMint)[0];
}
Expand Down
Loading
Loading