-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreflex.test.ts
More file actions
346 lines (285 loc) · 12 KB
/
Copy pathreflex.test.ts
File metadata and controls
346 lines (285 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
import {
MarketResolution,
MarketStatus,
fetchMarket,
fetchMaybeMarket,
} from "./utils/fetch/market";
import {
TOKEN_PROGRAM_ADDRESS,
findAssociatedTokenPda,
getCreateAssociatedTokenInstructionAsync,
getMintToInstruction,
} from "@solana-program/token";
import { fetchFarmerPosition, fetchMaybeFarmerPosition } from "./utils/fetch/farmer_position";
import { getConfigPda, getFarmerPositionPda, getMarketPda } from "./utils/pda";
import { KeyPairSigner } from "@solana/kit";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
import { buildAddIncentivesIx } from "./instructions/add_incentives";
import { buildAndSendTransaction } from "./utils/tx";
import { buildCancelMarketIx } from "./instructions/cancel_market";
import { buildClaimFeesIxs } from "./instructions/claim_fees";
import { buildClaimRewardsIxs } from "./instructions/claim_rewards";
import { buildCreateMarketIxs } from "./instructions/create_market";
import { buildInitializeIx } from "./instructions/initialize";
import { buildSettleMarketIx } from "./instructions/settle_market";
import { buildStakeOutcomeTokenIx } from "./instructions/stake_outcome_token";
import { buildUnstakeOutcomeTokenIx } from "./instructions/unstake_outcome_token";
import { buildUpdateConfigIx } from "./instructions/update_config";
import { buildWithdrawTreasuryIxs } from "./instructions/withdraw_treasury";
import { constants } from "./utils/constants";
import { createAccounts } from "./utils/accounts";
import { createClient } from "./utils/client";
import { createMint } from "./utils/mint";
import { expect } from "chai";
import { fetchConfig } from "./utils/fetch/config";
describe("reflex", () => {
let client: Awaited<ReturnType<typeof createClient>>;
let accounts: Awaited<ReturnType<typeof createAccounts>>;
let yesMint: KeyPairSigner;
let noMint: KeyPairSigner;
before(async () => {
client = await createClient();
accounts = await createAccounts(client);
[yesMint, noMint] = await Promise.all([createMint(client), createMint(client)]);
});
it("--- initialize ix ---", async () => {
const feeBps = 500;
const briberFeeBps = 500;
const ix = await buildInitializeIx(client, feeBps, briberFeeBps);
const txSig = await buildAndSendTransaction(client, [ix]);
console.log("initialize tx:", txSig);
const configAddress = await getConfigPda();
const config = await fetchConfig(client.rpc, configAddress);
expect(config.authority).to.equal(client.wallet.address);
expect(config.feeBps).to.equal(feeBps);
expect(config.briberFeeBps).to.equal(briberFeeBps);
});
it("--- update_config ix ---", async () => {
const newFeeBps = 300;
const newBriberFeeBps = 200;
const configAddress = await getConfigPda();
const configBefore = await fetchConfig(client.rpc, configAddress);
const ix = await buildUpdateConfigIx(client, {
newAuthority: client.wallet.address,
newFeeBps,
newBriberFeeBps,
});
const txSig = await buildAndSendTransaction(client, [ix]);
console.log("update_config tx:", txSig);
const configAfter = await fetchConfig(client.rpc, configAddress);
expect(configAfter.authority).to.equal(client.wallet.address);
expect(configAfter.feeBps).to.equal(newFeeBps);
expect(configAfter.briberFeeBps).to.equal(newBriberFeeBps);
// bump must not change
expect(configAfter.bump).to.equal(configBefore.bump);
});
it("--- create_market ix ---", async () => {
const id = "KXNCAAFGAME-26JAN19MIAIND-IND";
const amount = BigInt(10 * LAMPORTS_PER_SOL);
const ixs = await buildCreateMarketIxs(client, accounts, {
id,
amount,
yesMint,
noMint,
});
const txSig = await buildAndSendTransaction(client, ixs, {
feePayer: accounts.briber,
additionalSigners: [client.wallet],
});
console.log("create_market tx:", txSig);
const marketAddress = await getMarketPda(id);
const market = await fetchMarket(client.rpc, marketAddress);
expect(market.briber).to.equal(accounts.briber.address);
expect(market.totalIncentiveAmount).to.equal(amount);
expect(market.incentiveMint).to.equal(constants.WSOL_MINT);
});
it("--- add_incentives ix ---", async () => {
const id = "KXNCAAFGAME-26JAN19MIAIND-IND";
const amount = BigInt(5 * LAMPORTS_PER_SOL);
const marketAddress = await getMarketPda(id);
const marketBefore = await fetchMarket(client.rpc, marketAddress);
const ix = await buildAddIncentivesIx(accounts, { id, amount });
const txSig = await buildAndSendTransaction(client, [ix], {
feePayer: accounts.briber,
});
console.log("add_incentives tx:", txSig);
const market = await fetchMarket(client.rpc, marketAddress);
expect(market.totalIncentiveAmount).to.equal(marketBefore.totalIncentiveAmount + amount);
});
it("--- cancel_market ix ---", async () => {
// first create a new market since the main one will be settled in a later test
const id = "KXNCAAFGAME-26JAN19MIAIND-IND-2";
const amount = BigInt(1 * LAMPORTS_PER_SOL);
const ixs = await buildCreateMarketIxs(client, accounts, {
id,
amount,
yesMint,
noMint,
});
await buildAndSendTransaction(client, ixs, {
feePayer: accounts.briber,
additionalSigners: [client.wallet],
});
// cancel_market transfers all incentives back to the briber and closes
// market + vault accounts. It requires no pending fees on the market.
const ix = await buildCancelMarketIx(accounts, {
id,
yesMint: yesMint.address,
noMint: noMint.address,
});
const txSig = await buildAndSendTransaction(client, [ix], {
feePayer: accounts.briber,
});
console.log("cancel_market tx:", txSig);
const marketAddress = await getMarketPda(id);
const market = await fetchMaybeMarket(client.rpc, marketAddress);
expect(market).to.be.null;
});
it("--- stake_outcome_token ix ---", async () => {
const id = "KXNCAAFGAME-26JAN19MIAIND-IND";
const stakeAmount = BigInt(100_000_000); // 100 token (6 decimals)
// fetch market to get the yes mint
const marketAddress = await getMarketPda(id);
const market = await fetchMarket(client.rpc, marketAddress);
const outcomeMint = market.outcomeYesMint;
// create farmer ATA for yes mint and mint tokens into it
const [[farmerAta]] = await Promise.all([
findAssociatedTokenPda({
mint: outcomeMint,
owner: accounts.farmer.address,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
}),
]);
const createFarmerAtaIx = await getCreateAssociatedTokenInstructionAsync({
payer: client.wallet,
ata: farmerAta,
owner: accounts.farmer.address,
mint: outcomeMint,
});
const mintToIx = getMintToInstruction({
mint: outcomeMint,
token: farmerAta,
mintAuthority: client.wallet,
amount: stakeAmount,
});
await buildAndSendTransaction(client, [createFarmerAtaIx, mintToIx]);
// stake
const ix = await buildStakeOutcomeTokenIx({
id,
amount: stakeAmount,
outcomeMint,
farmer: accounts.farmer,
});
const txSig = await buildAndSendTransaction(client, [ix], {
additionalSigners: [accounts.farmer],
});
console.log("stake_outcome_token tx:", txSig);
const marketAfter = await fetchMarket(client.rpc, marketAddress);
expect(marketAfter.totalYesStaked > 0n).to.be.true;
expect(marketAfter.availableYesFees > 0n).to.be.true;
expect(marketAfter.totalNoStaked).to.equal(0n);
expect(marketAfter.availableNoFees).to.equal(0n);
});
it("--- unstake_outcome_token ix ---", async () => {
const id = "KXNCAAFGAME-26JAN19MIAIND-IND";
const marketAddress = await getMarketPda(id);
const market = await fetchMarket(client.rpc, marketAddress);
const outcomeMint = market.outcomeYesMint;
const farmerPositionAddress = await getFarmerPositionPda(
marketAddress,
accounts.farmer.address,
);
const positionBefore = await fetchFarmerPosition(client.rpc, farmerPositionAddress);
// unstake half of what this farmer has staked
const unstakeAmount = positionBefore.yesStaked / 2n;
const ix = await buildUnstakeOutcomeTokenIx({
id,
amount: unstakeAmount,
outcomeMint,
farmer: accounts.farmer,
});
const txSig = await buildAndSendTransaction(client, [ix], {
additionalSigners: [accounts.farmer],
});
console.log("unstake_outcome_token tx:", txSig);
const [marketAfter, positionAfter] = await Promise.all([
fetchMarket(client.rpc, marketAddress),
fetchFarmerPosition(client.rpc, farmerPositionAddress),
]);
// market totals decreased by the unstaked amount
expect(marketAfter.totalYesStaked).to.equal(market.totalYesStaked - unstakeAmount);
// fees are unaffected by unstaking
expect(marketAfter.availableYesFees).to.equal(market.availableYesFees);
// farmer position reflects the change
expect(positionAfter.yesStaked).to.equal(positionBefore.yesStaked - unstakeAmount);
expect(positionAfter.noStaked).to.equal(0n);
});
it("--- settle_market ix ---", async () => {
const id = "KXNCAAFGAME-26JAN19MIAIND-IND";
const ix = await buildSettleMarketIx(client, {
id,
resolution: MarketResolution.Yes,
});
const txSig = await buildAndSendTransaction(client, [ix]);
console.log("settle_market tx:", txSig);
const marketAddress = await getMarketPda(id);
const market = await fetchMarket(client.rpc, marketAddress);
expect(market.resolution).to.equal(MarketResolution.Yes);
expect(market.status).to.equal(MarketStatus.Settled);
});
// claim_fees requires a settled market. A fresh market is created here since
// the main one was already cancelled. It is settled inline before claiming.
it("--- claim_fees ix ---", async () => {
const id = "KXNCAAFGAME-26JAN19MIAIND-IND";
// read on-chain state to pick the correct outcome mint.
const marketAddress = await getMarketPda(id);
const market = await fetchMarket(client.rpc, marketAddress);
const outcomeMint =
market.resolution === MarketResolution.Yes ? market.outcomeYesMint : market.outcomeNoMint;
// claim fees.
const ixs = await buildClaimFeesIxs(accounts, { id, outcomeMint });
const txSig = await buildAndSendTransaction(client, ixs, {
feePayer: accounts.briber,
});
console.log("claim_fees tx:", txSig);
// after claiming, available fees must be 0.
const marketAfter = await fetchMarket(client.rpc, marketAddress);
expect(marketAfter.availableYesFees).to.equal(0n);
expect(marketAfter.availableNoFees).to.equal(0n);
});
it("--- claim_rewards ix ---", async () => {
const id = "KXNCAAFGAME-26JAN19MIAIND-IND";
const marketAddress = await getMarketPda(id);
const market = await fetchMarket(client.rpc, marketAddress);
// market is resolved Yes — reward_mint is the incentive mint (WSOL),
// outcome_mint is the winning side.
const outcomeMint = market.outcomeYesMint;
const rewardMint = market.incentiveMint;
const farmerPositionAddress = await getFarmerPositionPda(
marketAddress,
accounts.farmer.address,
);
const positionBefore = await fetchFarmerPosition(client.rpc, farmerPositionAddress);
const ixs = await buildClaimRewardsIxs({
id,
rewardMint,
outcomeMint,
farmer: accounts.farmer,
});
const txSig = await buildAndSendTransaction(client, ixs, {
additionalSigners: [accounts.farmer],
});
console.log("claim_rewards tx:", txSig);
// farmer_position must be closed after claiming
const positionAfter = await fetchMaybeFarmerPosition(client.rpc, farmerPositionAddress);
expect(positionAfter).to.be.null;
// the staked amount was non-zero before claiming
expect(positionBefore.yesStaked > 0n).to.be.true;
});
it("--- withdraw_treasury ix ---", async () => {
// treasury PDAs into them.
const ixs = await buildWithdrawTreasuryIxs(client);
const txSig = await buildAndSendTransaction(client, ixs);
console.log("withdraw_treasury tx:", txSig);
});
});