Skip to content

Commit 0cb6a34

Browse files
committed
Test complete_launch
1 parent 756d1eb commit 0cb6a34

File tree

4 files changed

+148
-6
lines changed

4 files changed

+148
-6
lines changed

programs/launchpad/src/instructions/complete_launch.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ pub struct CompleteLaunch<'info> {
99
#[account(
1010
mut,
1111
constraint = launch.state == LaunchState::Live @ LaunchpadError::InvalidLaunchState,
12-
has_one = treasury_usdc_account
12+
has_one = treasury_usdc_account,
13+
has_one = usdc_vault
1314
)]
1415
pub launch: Account<'info, Launch>,
1516

16-
#[account(
17-
mut,
18-
constraint = usdc_vault.key() == launch.usdc_vault
19-
)]
17+
#[account(mut)]
2018
pub usdc_vault: Account<'info, TokenAccount>,
2119

2220
#[account(mut)]

sdk/src/v0.4/LaunchpadClient.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,23 @@ export class LaunchpadClient {
137137
funderTokenAccount,
138138
});
139139
}
140+
141+
completeLaunchIx(
142+
launch: PublicKey,
143+
usdcMint: PublicKey,
144+
daoTreasury: PublicKey
145+
) {
146+
const usdcVault = getAssociatedTokenAddressSync(usdcMint, launch, true);
147+
const treasuryUsdcAccount = getAssociatedTokenAddressSync(
148+
usdcMint,
149+
daoTreasury,
150+
true
151+
);
152+
153+
return this.launchpad.methods.completeLaunch().accounts({
154+
launch,
155+
usdcVault,
156+
treasuryUsdcAccount,
157+
});
158+
}
140159
}

tests/launchpad/main.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import initializeLaunch from "./unit/initializeLaunch.test.js";
22
import fund from "./unit/fund.test.js";
3-
3+
import completeLaunch from "./unit/completeLaunch.test.js";
44
// TODO add a many-outcome integration test
55
export default function suite() {
66
describe("#initialize_launch", initializeLaunch);
77
describe("#fund", fund);
8+
describe("#complete_launch", completeLaunch);
89
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { PublicKey } from "@solana/web3.js";
2+
import { assert } from "chai";
3+
import {
4+
AutocratClient,
5+
getLaunchAddr,
6+
LaunchpadClient,
7+
} from "@metadaoproject/futarchy/v0.4";
8+
import { createMint } from "spl-token-bankrun";
9+
import { BN } from "bn.js";
10+
import { createSetAuthorityInstruction, AuthorityType, getAssociatedTokenAddressSync } from "@solana/spl-token";
11+
12+
export default function suite() {
13+
let autocratClient: AutocratClient;
14+
let launchpadClient: LaunchpadClient;
15+
let dao: PublicKey;
16+
let daoTreasury: PublicKey;
17+
let META: PublicKey;
18+
let USDC: PublicKey;
19+
let launch: PublicKey;
20+
let usdcVault: PublicKey;
21+
let treasuryUsdcAccount: PublicKey;
22+
23+
const minRaise = new BN(1000_000000); // 1000 USDC
24+
25+
before(async function () {
26+
autocratClient = this.autocratClient;
27+
launchpadClient = this.launchpadClient;
28+
});
29+
30+
beforeEach(async function () {
31+
// Create test tokens
32+
META = await createMint(this.banksClient, this.payer, this.payer.publicKey, null, 6);
33+
USDC = await createMint(this.banksClient, this.payer, this.payer.publicKey, null, 6);
34+
35+
// Initialize DAO
36+
dao = await autocratClient.initializeDao(META, 400, 5, 5000, USDC);
37+
[daoTreasury] = PublicKey.findProgramAddressSync(
38+
[dao.toBuffer()],
39+
autocratClient.autocrat.programId
40+
);
41+
42+
// Get accounts
43+
[launch] = getLaunchAddr(launchpadClient.getProgramId(), dao);
44+
usdcVault = getAssociatedTokenAddressSync(USDC, launch, true);
45+
treasuryUsdcAccount = getAssociatedTokenAddressSync(USDC, daoTreasury, true);
46+
47+
// Initialize launch
48+
await launchpadClient.initializeLaunchIx(
49+
dao,
50+
minRaise,
51+
new BN(5000_000000),
52+
USDC,
53+
META
54+
).preInstructions([
55+
createSetAuthorityInstruction(
56+
META,
57+
this.payer.publicKey,
58+
AuthorityType.MintTokens,
59+
launch
60+
)
61+
]).rpc();
62+
});
63+
64+
it("completes launch successfully when minimum raise is met", async function () {
65+
// Fund the launch with exactly minimum raise
66+
const userUsdcAccount = await this.createTokenAccount(USDC, this.payer.publicKey);
67+
const userTokenAccount = await this.createTokenAccount(META, this.payer.publicKey);
68+
// await this.mintTo(USDC, userUsdcAccount, this.payer, minRaise.toNumber());
69+
await this.mintTo(USDC, this.payer.publicKey, this.payer, minRaise.toNumber());
70+
71+
await launchpadClient.fundIx(
72+
launch,
73+
minRaise,
74+
USDC,
75+
META
76+
).rpc();
77+
78+
// Complete the launch
79+
await launchpadClient.completeLaunchIx(launch, USDC, daoTreasury).rpc();
80+
81+
const launchAccount = await launchpadClient.fetchLaunch(launch);
82+
const treasuryBalance = await this.getTokenBalance(USDC, daoTreasury);
83+
84+
assert.exists(launchAccount.state.complete);
85+
assert.equal(treasuryBalance.toString(), minRaise.toString());
86+
});
87+
88+
it("moves to refunding state when minimum raise is not met", async function () {
89+
// Fund the launch with less than minimum raise
90+
const userUsdcAccount = await this.createTokenAccount(USDC, this.payer.publicKey);
91+
const userTokenAccount = await this.createTokenAccount(META, this.payer.publicKey);
92+
const partialAmount = minRaise.divn(2);
93+
await this.mintTo(USDC, this.payer.publicKey, this.payer, partialAmount.toNumber());
94+
95+
await launchpadClient.fundIx(
96+
launch,
97+
partialAmount,
98+
USDC,
99+
META
100+
).rpc();
101+
102+
// Complete the launch
103+
await launchpadClient.completeLaunchIx(launch, USDC, daoTreasury).rpc();
104+
105+
const launchAccount = await launchpadClient.fetchLaunch(launch);
106+
const treasuryBalance = await this.getTokenBalance(USDC, daoTreasury);
107+
108+
assert.exists(launchAccount.state.refunding);
109+
assert.equal(treasuryBalance.toString(), "0");
110+
});
111+
112+
it("fails when launch is not in live state", async function () {
113+
// Complete launch first time
114+
await launchpadClient.completeLaunchIx(launch, USDC, daoTreasury).rpc();
115+
116+
// Try to complete again
117+
try {
118+
await launchpadClient.completeLaunchIx(launch, USDC, daoTreasury).rpc();
119+
assert.fail("Should have thrown error");
120+
} catch (e) {
121+
assert.include(e.message, "InvalidLaunchState");
122+
}
123+
});
124+
}

0 commit comments

Comments
 (0)