Skip to content

Commit 5b91a77

Browse files
committed
Add timer
1 parent 0cb6a34 commit 5b91a77

File tree

7 files changed

+96
-5
lines changed

7 files changed

+96
-5
lines changed

programs/launchpad/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ pub enum LaunchpadError {
88
SupplyNonZero,
99
#[msg("Invalid launch state")]
1010
InvalidLaunchState,
11+
#[msg("Launch period not over")]
12+
LaunchPeriodNotOver,
1113
}

programs/launchpad/src/instructions/complete_launch.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ pub struct CompleteLaunch<'info> {
2525

2626
impl CompleteLaunch<'_> {
2727
pub fn validate(&self) -> Result<()> {
28+
let clock = Clock::get()?;
29+
30+
// 7 days in slots (assuming 400ms per slot)
31+
const SLOTS_PER_DAY: u64 = 216_000; // (24 * 60 * 60 * 1000) / 400
32+
const REQUIRED_SLOTS: u64 = SLOTS_PER_DAY * 5;
33+
34+
require!(
35+
clock.slot >= self.launch.slot_initialized.saturating_add(REQUIRED_SLOTS),
36+
LaunchpadError::LaunchPeriodNotOver
37+
);
38+
2839
Ok(())
2940
}
3041

programs/launchpad/src/instructions/initialize_launch.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ impl InitializeLaunch<'_> {
8585
ctx: Context<Self>,
8686
args: InitializeLaunchArgs,
8787
) -> Result<()> {
88+
let clock = Clock::get()?;
8889
let (dao_treasury, _) = Pubkey::find_program_address(
8990
&[ctx.accounts.dao.key().as_ref()],
9091
&AUTOCRAT_PROGRAM_ID
@@ -102,6 +103,7 @@ impl InitializeLaunch<'_> {
102103
pda_bump: ctx.bumps.launch,
103104
seq_num: 0,
104105
state: LaunchState::Live,
106+
slot_initialized: clock.slot,
105107
});
106108

107109
let clock = Clock::get()?;

programs/launchpad/src/state/launch.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ pub struct Launch {
3131
/// The sequence number of this launch. Useful for sorting events.
3232
pub seq_num: u64,
3333
pub state: LaunchState,
34+
pub slot_initialized: u64,
3435
}

sdk/src/v0.4/types/launchpad.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ export type Launchpad = {
230230
type: {
231231
defined: "LaunchState";
232232
};
233+
},
234+
{
235+
name: "slotInitialized";
236+
type: "u64";
233237
}
234238
];
235239
};
@@ -346,6 +350,11 @@ export type Launchpad = {
346350
code: 6002;
347351
name: "InvalidLaunchState";
348352
msg: "Invalid launch state";
353+
},
354+
{
355+
code: 6003;
356+
name: "LaunchPeriodNotOver";
357+
msg: "Launch period not over";
349358
}
350359
];
351360
};
@@ -583,6 +592,10 @@ export const IDL: Launchpad = {
583592
defined: "LaunchState",
584593
},
585594
},
595+
{
596+
name: "slotInitialized",
597+
type: "u64",
598+
},
586599
],
587600
},
588601
},
@@ -699,5 +712,10 @@ export const IDL: Launchpad = {
699712
name: "InvalidLaunchState",
700713
msg: "Invalid launch state",
701714
},
715+
{
716+
code: 6003,
717+
name: "LaunchPeriodNotOver",
718+
msg: "Launch period not over",
719+
},
702720
],
703721
};

tests/launchpad/unit/completeLaunch.test.ts

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PublicKey } from "@solana/web3.js";
1+
import { ComputeBudgetProgram, PublicKey } from "@solana/web3.js";
22
import { assert } from "chai";
33
import {
44
AutocratClient,
@@ -21,6 +21,7 @@ export default function suite() {
2121
let treasuryUsdcAccount: PublicKey;
2222

2323
const minRaise = new BN(1000_000000); // 1000 USDC
24+
const SLOTS_PER_DAY = 216_000n; // (24 * 60 * 60 * 1000) / 400
2425

2526
before(async function () {
2627
autocratClient = this.autocratClient;
@@ -61,11 +62,10 @@ export default function suite() {
6162
]).rpc();
6263
});
6364

64-
it("completes launch successfully when minimum raise is met", async function () {
65+
it("completes launch successfully when minimum raise is met and time has passed", async function () {
6566
// Fund the launch with exactly minimum raise
6667
const userUsdcAccount = await this.createTokenAccount(USDC, this.payer.publicKey);
6768
const userTokenAccount = await this.createTokenAccount(META, this.payer.publicKey);
68-
// await this.mintTo(USDC, userUsdcAccount, this.payer, minRaise.toNumber());
6969
await this.mintTo(USDC, this.payer.publicKey, this.payer, minRaise.toNumber());
7070

7171
await launchpadClient.fundIx(
@@ -75,6 +75,9 @@ export default function suite() {
7575
META
7676
).rpc();
7777

78+
// Advance clock past 7 days
79+
await this.advanceBySlots(SLOTS_PER_DAY * 7n);
80+
7881
// Complete the launch
7982
await launchpadClient.completeLaunchIx(launch, USDC, daoTreasury).rpc();
8083

@@ -85,7 +88,39 @@ export default function suite() {
8588
assert.equal(treasuryBalance.toString(), minRaise.toString());
8689
});
8790

88-
it("moves to refunding state when minimum raise is not met", async function () {
91+
it("fails when launch period has not passed", async function () {
92+
// Fund the launch with exactly minimum raise
93+
const userUsdcAccount = await this.createTokenAccount(USDC, this.payer.publicKey);
94+
const userTokenAccount = await this.createTokenAccount(META, this.payer.publicKey);
95+
await this.mintTo(USDC, this.payer.publicKey, this.payer, minRaise.toNumber());
96+
97+
await launchpadClient.fundIx(
98+
launch,
99+
minRaise,
100+
USDC,
101+
META
102+
).rpc();
103+
104+
// Try to complete immediately (should fail)
105+
try {
106+
await launchpadClient.completeLaunchIx(launch, USDC, daoTreasury).rpc();
107+
assert.fail("Should have thrown error");
108+
} catch (e) {
109+
assert.include(e.message, "LaunchPeriodNotOver");
110+
}
111+
112+
// Advance by 6 days (still not enough)
113+
await this.advanceBySlots(SLOTS_PER_DAY * 3n);
114+
115+
try {
116+
await launchpadClient.completeLaunchIx(launch, USDC, daoTreasury).preInstructions([ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })]).rpc();
117+
assert.fail("Should have thrown error");
118+
} catch (e) {
119+
assert.include(e.message, "LaunchPeriodNotOver");
120+
}
121+
});
122+
123+
it("moves to refunding state when minimum raise is not met after period", async function () {
89124
// Fund the launch with less than minimum raise
90125
const userUsdcAccount = await this.createTokenAccount(USDC, this.payer.publicKey);
91126
const userTokenAccount = await this.createTokenAccount(META, this.payer.publicKey);
@@ -99,6 +134,9 @@ export default function suite() {
99134
META
100135
).rpc();
101136

137+
// Advance clock past 7 days
138+
await this.advanceBySlots(SLOTS_PER_DAY * 7n);
139+
102140
// Complete the launch
103141
await launchpadClient.completeLaunchIx(launch, USDC, daoTreasury).rpc();
104142

@@ -110,6 +148,9 @@ export default function suite() {
110148
});
111149

112150
it("fails when launch is not in live state", async function () {
151+
// Advance clock past 7 days
152+
await this.advanceBySlots(SLOTS_PER_DAY * 7n);
153+
113154
// Complete launch first time
114155
await launchpadClient.completeLaunchIx(launch, USDC, daoTreasury).rpc();
115156

tests/main.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import amm from "./amm/main.test.js";
33
import autocrat from "./autocrat/autocrat.js";
44
import launchpad from "./launchpad/main.test.js";
55

6-
import { startAnchor } from "solana-bankrun";
6+
import { Clock, startAnchor } from "solana-bankrun";
77
import { BankrunProvider } from "anchor-bankrun";
88
import * as anchor from "@coral-xyz/anchor";
99
import {
@@ -143,6 +143,22 @@ before(async function () {
143143
amount
144144
);
145145
};
146+
147+
this.advanceBySlots = async (
148+
slots: bigint
149+
) => {
150+
const currentClock = await this.context.banksClient.getClock();
151+
this.context.setClock(
152+
new Clock(
153+
currentClock.slot + slots,
154+
currentClock.epochStartTimestamp,
155+
currentClock.epoch,
156+
currentClock.leaderScheduleEpoch,
157+
50n
158+
)
159+
);
160+
};
161+
146162
});
147163

148164
describe("conditional_vault", conditionalVault);

0 commit comments

Comments
 (0)