@zemyth/raise-sdk 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -7
- package/dist/accounts/index.cjs +531 -3
- package/dist/accounts/index.cjs.map +1 -1
- package/dist/accounts/index.d.cts +307 -2
- package/dist/accounts/index.d.ts +307 -2
- package/dist/accounts/index.js +503 -4
- package/dist/accounts/index.js.map +1 -1
- package/dist/constants/index.cjs +41 -3
- package/dist/constants/index.cjs.map +1 -1
- package/dist/constants/index.d.cts +38 -3
- package/dist/constants/index.d.ts +38 -3
- package/dist/constants/index.js +40 -4
- package/dist/constants/index.js.map +1 -1
- package/dist/index.cjs +2297 -361
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +566 -7
- package/dist/index.d.ts +566 -7
- package/dist/index.js +2279 -379
- package/dist/index.js.map +1 -1
- package/dist/instructions/index.cjs +783 -40
- package/dist/instructions/index.cjs.map +1 -1
- package/dist/instructions/index.d.cts +492 -6
- package/dist/instructions/index.d.ts +492 -6
- package/dist/instructions/index.js +762 -42
- package/dist/instructions/index.js.map +1 -1
- package/dist/pdas/index.cjs +163 -1
- package/dist/pdas/index.cjs.map +1 -1
- package/dist/pdas/index.d.cts +131 -1
- package/dist/pdas/index.d.ts +131 -1
- package/dist/pdas/index.js +151 -2
- package/dist/pdas/index.js.map +1 -1
- package/dist/types/index.cjs +9 -0
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +586 -3
- package/dist/types/index.d.ts +586 -3
- package/dist/types/index.js +9 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +2 -1
- package/src/__tests__/dynamic-tokenomics.test.ts +358 -0
- package/src/accounts/index.ts +852 -1
- package/src/client.ts +1130 -1
- package/src/constants/index.ts +48 -2
- package/src/index.ts +58 -0
- package/src/instructions/index.ts +1383 -40
- package/src/pdas/index.ts +346 -0
- package/src/types/index.ts +698 -2
- package/src/utils/index.ts +90 -0
package/dist/accounts/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { PublicKey } from '@solana/web3.js';
|
|
2
1
|
import { BN } from '@coral-xyz/anchor';
|
|
2
|
+
import { PublicKey } from '@solana/web3.js';
|
|
3
3
|
|
|
4
|
-
// src/
|
|
4
|
+
// src/accounts/index.ts
|
|
5
5
|
|
|
6
6
|
// src/constants/index.ts
|
|
7
7
|
var SEEDS = {
|
|
@@ -11,7 +11,12 @@ var SEEDS = {
|
|
|
11
11
|
VOTE: "vote",
|
|
12
12
|
PIVOT: "pivot",
|
|
13
13
|
TGE_ESCROW: "tge_escrow",
|
|
14
|
-
ADMIN_CONFIG: "admin-config"
|
|
14
|
+
ADMIN_CONFIG: "admin-config",
|
|
15
|
+
FUTURE_ROUND_STATE: "future_round_state",
|
|
16
|
+
// Multi-Round Fundraising seeds
|
|
17
|
+
FUNDING_ROUND: "funding_round",
|
|
18
|
+
INVESTOR_MS_VESTING: "investor_ms_vesting"
|
|
19
|
+
};
|
|
15
20
|
|
|
16
21
|
// src/pdas/index.ts
|
|
17
22
|
function ensureBN(value) {
|
|
@@ -77,6 +82,13 @@ function getTgeEscrowPDA(projectPda, programId) {
|
|
|
77
82
|
);
|
|
78
83
|
return pda;
|
|
79
84
|
}
|
|
85
|
+
function getTokenVaultPDA(projectPda, programId) {
|
|
86
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
87
|
+
[Buffer.from("token_vault"), projectPda.toBuffer()],
|
|
88
|
+
programId
|
|
89
|
+
);
|
|
90
|
+
return pda;
|
|
91
|
+
}
|
|
80
92
|
function getAdminConfigPDA(programId) {
|
|
81
93
|
const [pda] = PublicKey.findProgramAddressSync(
|
|
82
94
|
[Buffer.from(SEEDS.ADMIN_CONFIG)],
|
|
@@ -84,6 +96,124 @@ function getAdminConfigPDA(programId) {
|
|
|
84
96
|
);
|
|
85
97
|
return pda;
|
|
86
98
|
}
|
|
99
|
+
function getTokenomicsPDA(projectPda, programId) {
|
|
100
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
101
|
+
[Buffer.from("tokenomics"), projectPda.toBuffer()],
|
|
102
|
+
programId
|
|
103
|
+
);
|
|
104
|
+
return pda;
|
|
105
|
+
}
|
|
106
|
+
function getAllocationProposalPDA(projectPda, proposalIndex, programId) {
|
|
107
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
108
|
+
[
|
|
109
|
+
Buffer.from("allocation_proposal"),
|
|
110
|
+
projectPda.toBuffer(),
|
|
111
|
+
Buffer.from([proposalIndex])
|
|
112
|
+
],
|
|
113
|
+
programId
|
|
114
|
+
);
|
|
115
|
+
return pda;
|
|
116
|
+
}
|
|
117
|
+
function getAllocationVotePDA(proposalPda, nftMint, programId) {
|
|
118
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
119
|
+
[
|
|
120
|
+
Buffer.from("allocation_vote"),
|
|
121
|
+
proposalPda.toBuffer(),
|
|
122
|
+
nftMint.toBuffer()
|
|
123
|
+
],
|
|
124
|
+
programId
|
|
125
|
+
);
|
|
126
|
+
return pda;
|
|
127
|
+
}
|
|
128
|
+
function getSubAllocationVestingPDA(projectPda, subAllocationId, programId) {
|
|
129
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
130
|
+
[
|
|
131
|
+
Buffer.from("sub_allocation_vesting"),
|
|
132
|
+
projectPda.toBuffer(),
|
|
133
|
+
Buffer.from([subAllocationId])
|
|
134
|
+
],
|
|
135
|
+
programId
|
|
136
|
+
);
|
|
137
|
+
return pda;
|
|
138
|
+
}
|
|
139
|
+
function getInvestorMilestoneVestingPDA(projectPda, milestoneIndex, investmentPda, programId) {
|
|
140
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
141
|
+
[
|
|
142
|
+
Buffer.from("investor_ms_vesting"),
|
|
143
|
+
projectPda.toBuffer(),
|
|
144
|
+
Buffer.from([milestoneIndex]),
|
|
145
|
+
investmentPda.toBuffer()
|
|
146
|
+
],
|
|
147
|
+
programId
|
|
148
|
+
);
|
|
149
|
+
return pda;
|
|
150
|
+
}
|
|
151
|
+
function getFounderMilestoneVestingPDA(projectPda, milestoneIndex, programId) {
|
|
152
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
153
|
+
[
|
|
154
|
+
Buffer.from("founder_ms_vesting"),
|
|
155
|
+
projectPda.toBuffer(),
|
|
156
|
+
Buffer.from([milestoneIndex])
|
|
157
|
+
],
|
|
158
|
+
programId
|
|
159
|
+
);
|
|
160
|
+
return pda;
|
|
161
|
+
}
|
|
162
|
+
function getFutureRoundVaultPDA(projectPda, programId) {
|
|
163
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
164
|
+
[Buffer.from(SEEDS.FUTURE_ROUND_STATE), projectPda.toBuffer()],
|
|
165
|
+
programId
|
|
166
|
+
);
|
|
167
|
+
return pda;
|
|
168
|
+
}
|
|
169
|
+
function getFundingRoundPDA(projectPda, roundNumber, programId) {
|
|
170
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
171
|
+
[
|
|
172
|
+
Buffer.from(SEEDS.FUNDING_ROUND),
|
|
173
|
+
projectPda.toBuffer(),
|
|
174
|
+
Buffer.from([roundNumber])
|
|
175
|
+
],
|
|
176
|
+
programId
|
|
177
|
+
);
|
|
178
|
+
return pda;
|
|
179
|
+
}
|
|
180
|
+
function getRoundMilestonePDA(projectPda, roundNumber, milestoneIndex, programId) {
|
|
181
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
182
|
+
[
|
|
183
|
+
Buffer.from(SEEDS.MILESTONE),
|
|
184
|
+
projectPda.toBuffer(),
|
|
185
|
+
Buffer.from([roundNumber]),
|
|
186
|
+
Buffer.from([milestoneIndex])
|
|
187
|
+
],
|
|
188
|
+
programId
|
|
189
|
+
);
|
|
190
|
+
return pda;
|
|
191
|
+
}
|
|
192
|
+
function getRoundInvestmentPDA(projectPda, roundNumber, nftMint, programId) {
|
|
193
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
194
|
+
[
|
|
195
|
+
Buffer.from(SEEDS.INVESTMENT),
|
|
196
|
+
projectPda.toBuffer(),
|
|
197
|
+
Buffer.from([roundNumber]),
|
|
198
|
+
nftMint.toBuffer()
|
|
199
|
+
],
|
|
200
|
+
programId
|
|
201
|
+
);
|
|
202
|
+
return pda;
|
|
203
|
+
}
|
|
204
|
+
function getRoundInvestorMilestoneVestingPDA(projectPda, roundNumber, milestoneIndex, investmentPda, programId) {
|
|
205
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
206
|
+
[
|
|
207
|
+
Buffer.from(SEEDS.INVESTOR_MS_VESTING),
|
|
208
|
+
projectPda.toBuffer(),
|
|
209
|
+
Buffer.from([roundNumber]),
|
|
210
|
+
Buffer.from([milestoneIndex]),
|
|
211
|
+
investmentPda.toBuffer()
|
|
212
|
+
],
|
|
213
|
+
programId
|
|
214
|
+
);
|
|
215
|
+
return pda;
|
|
216
|
+
}
|
|
87
217
|
|
|
88
218
|
// src/accounts/index.ts
|
|
89
219
|
function getAccountNamespace(program) {
|
|
@@ -231,6 +361,18 @@ async function fetchAdminConfig(program) {
|
|
|
231
361
|
const adminConfigPda = getAdminConfigPDA(program.programId);
|
|
232
362
|
return await getAccountNamespace(program).adminConfig.fetch(adminConfigPda);
|
|
233
363
|
}
|
|
364
|
+
async function fetchTokenVault(program, projectId) {
|
|
365
|
+
try {
|
|
366
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
367
|
+
const tokenVaultPda = getTokenVaultPDA(projectPda, program.programId);
|
|
368
|
+
return await getAccountNamespace(program).tokenVault.fetch(tokenVaultPda);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
234
376
|
async function accountExists(program, accountType, pda) {
|
|
235
377
|
try {
|
|
236
378
|
await program.account[accountType].fetch(pda);
|
|
@@ -239,7 +381,364 @@ async function accountExists(program, accountType, pda) {
|
|
|
239
381
|
return false;
|
|
240
382
|
}
|
|
241
383
|
}
|
|
384
|
+
async function fetchTokenomics(program, projectId) {
|
|
385
|
+
try {
|
|
386
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
387
|
+
const tokenomicsPda = getTokenomicsPDA(projectPda, program.programId);
|
|
388
|
+
return await getAccountNamespace(program).tokenomics.fetch(tokenomicsPda);
|
|
389
|
+
} catch (error) {
|
|
390
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function fetchAllocationProposal(program, projectId, proposalIndex) {
|
|
397
|
+
try {
|
|
398
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
399
|
+
const proposalPda = getAllocationProposalPDA(projectPda, proposalIndex, program.programId);
|
|
400
|
+
return await getAccountNamespace(program).allocationProposal.fetch(proposalPda);
|
|
401
|
+
} catch (error) {
|
|
402
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async function fetchAllAllocationProposals(program, projectId) {
|
|
409
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
410
|
+
const proposals = await getAccountNamespace(program).allocationProposal.all([
|
|
411
|
+
{
|
|
412
|
+
memcmp: {
|
|
413
|
+
offset: 8,
|
|
414
|
+
// Skip discriminator
|
|
415
|
+
bytes: projectPda.toBase58()
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
]);
|
|
419
|
+
return proposals.map((p) => ({
|
|
420
|
+
publicKey: p.publicKey,
|
|
421
|
+
account: p.account
|
|
422
|
+
}));
|
|
423
|
+
}
|
|
424
|
+
async function fetchAllocationVote(program, projectId, proposalIndex, nftMint) {
|
|
425
|
+
try {
|
|
426
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
427
|
+
const proposalPda = getAllocationProposalPDA(projectPda, proposalIndex, program.programId);
|
|
428
|
+
const votePda = getAllocationVotePDA(proposalPda, nftMint, program.programId);
|
|
429
|
+
return await getAccountNamespace(program).allocationVote.fetch(votePda);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
throw error;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
async function fetchSubAllocationVesting(program, projectId, subAllocationId) {
|
|
438
|
+
try {
|
|
439
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
440
|
+
const vestingPda = getSubAllocationVestingPDA(projectPda, subAllocationId, program.programId);
|
|
441
|
+
return await getAccountNamespace(program).subAllocationVesting.fetch(vestingPda);
|
|
442
|
+
} catch (error) {
|
|
443
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
throw error;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
async function fetchInvestorMilestoneVesting(program, projectId, milestoneIndex, nftMint) {
|
|
450
|
+
try {
|
|
451
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
452
|
+
const investmentPda = getInvestmentPDA(projectPda, nftMint, program.programId);
|
|
453
|
+
const vestingPda = getInvestorMilestoneVestingPDA(projectPda, milestoneIndex, investmentPda, program.programId);
|
|
454
|
+
return await getAccountNamespace(program).investorMilestoneVesting.fetch(vestingPda);
|
|
455
|
+
} catch (error) {
|
|
456
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
throw error;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
async function fetchFounderMilestoneVesting(program, projectId, milestoneIndex) {
|
|
463
|
+
try {
|
|
464
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
465
|
+
const vestingPda = getFounderMilestoneVestingPDA(projectPda, milestoneIndex, program.programId);
|
|
466
|
+
return await getAccountNamespace(program).founderMilestoneVesting.fetch(vestingPda);
|
|
467
|
+
} catch (error) {
|
|
468
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
throw error;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
var EARLY_TOKEN_COOLING_PERIOD_SECONDS = 86400;
|
|
475
|
+
var EARLY_TOKEN_COOLING_PERIOD_SECONDS_DEV = 10;
|
|
476
|
+
var EARLY_TOKEN_RELEASE_BPS = 500;
|
|
477
|
+
function canClaimEarlyTokens(investment, currentTimestamp, isDev = false) {
|
|
478
|
+
const now = currentTimestamp ?? Math.floor(Date.now() / 1e3);
|
|
479
|
+
const coolingPeriod = isDev ? EARLY_TOKEN_COOLING_PERIOD_SECONDS_DEV : EARLY_TOKEN_COOLING_PERIOD_SECONDS;
|
|
480
|
+
if (investment.earlyTokensClaimed) {
|
|
481
|
+
return { canClaim: false, reason: "Early tokens already claimed" };
|
|
482
|
+
}
|
|
483
|
+
if (investment.votingRightsActive === false) {
|
|
484
|
+
return { canClaim: false, reason: "Voting rights have been revoked" };
|
|
485
|
+
}
|
|
486
|
+
if (investment.withdrawnFromPivot === true) {
|
|
487
|
+
return { canClaim: false, reason: "Investment has been withdrawn from pivot" };
|
|
488
|
+
}
|
|
489
|
+
const investedAt = typeof investment.investedAt === "number" ? investment.investedAt : investment.investedAt.toNumber();
|
|
490
|
+
const coolingEndTime = investedAt + coolingPeriod;
|
|
491
|
+
if (now < coolingEndTime) {
|
|
492
|
+
const timeRemaining = coolingEndTime - now;
|
|
493
|
+
return {
|
|
494
|
+
canClaim: false,
|
|
495
|
+
reason: `Cooling period not expired. ${formatTimeRemaining(timeRemaining)} remaining.`,
|
|
496
|
+
timeRemainingSeconds: timeRemaining
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
return { canClaim: true };
|
|
500
|
+
}
|
|
501
|
+
function canClaimFounderEarlyTokens(project) {
|
|
502
|
+
if (project.founderEarlyTokensClaimed) {
|
|
503
|
+
return { canClaim: false, reason: "Founder early tokens already claimed" };
|
|
504
|
+
}
|
|
505
|
+
const stateStr = getProjectStateString(project.state);
|
|
506
|
+
const validStates = ["funded", "inProgress", "completed"];
|
|
507
|
+
if (!validStates.includes(stateStr)) {
|
|
508
|
+
return {
|
|
509
|
+
canClaim: false,
|
|
510
|
+
reason: `Project must be in Funded, InProgress, or Completed state. Current state: ${stateStr}`
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
return { canClaim: true };
|
|
514
|
+
}
|
|
515
|
+
function canClaimFounderMilestoneTokens(milestone) {
|
|
516
|
+
const stateStr = getMilestoneStateString(milestone.state);
|
|
517
|
+
if (stateStr !== "unlocked") {
|
|
518
|
+
return {
|
|
519
|
+
canClaim: false,
|
|
520
|
+
reason: `Milestone must be in Unlocked state. Current state: ${stateStr}`
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
return { canClaim: true };
|
|
524
|
+
}
|
|
525
|
+
function requiresBurnForRefund(project, investment) {
|
|
526
|
+
const isFullRefund = project.cumulativePercentage === 0;
|
|
527
|
+
const requiresBurn = isFullRefund && investment.earlyTokensClaimed;
|
|
528
|
+
if (!requiresBurn) {
|
|
529
|
+
return { requiresBurn: false, burnAmount: 0 };
|
|
530
|
+
}
|
|
531
|
+
const burnAmount = typeof investment.earlyTokensAmount === "number" ? investment.earlyTokensAmount : investment.earlyTokensAmount.toNumber();
|
|
532
|
+
return { requiresBurn: true, burnAmount };
|
|
533
|
+
}
|
|
534
|
+
function calculateEarlyTokenAmount(tokensAllocated) {
|
|
535
|
+
const allocated = typeof tokensAllocated === "number" ? new BN(tokensAllocated) : tokensAllocated;
|
|
536
|
+
return allocated.muln(EARLY_TOKEN_RELEASE_BPS).divn(1e4);
|
|
537
|
+
}
|
|
538
|
+
function calculateRemainingAllocation(tokensAllocated) {
|
|
539
|
+
const allocated = typeof tokensAllocated === "number" ? new BN(tokensAllocated) : tokensAllocated;
|
|
540
|
+
const remainingBps = 1e4 - EARLY_TOKEN_RELEASE_BPS;
|
|
541
|
+
return allocated.muln(remainingBps).divn(1e4);
|
|
542
|
+
}
|
|
543
|
+
function formatTimeRemaining(seconds) {
|
|
544
|
+
if (seconds <= 0) return "0 seconds";
|
|
545
|
+
const hours = Math.floor(seconds / 3600);
|
|
546
|
+
const minutes = Math.floor(seconds % 3600 / 60);
|
|
547
|
+
const secs = seconds % 60;
|
|
548
|
+
const parts = [];
|
|
549
|
+
if (hours > 0) parts.push(`${hours}h`);
|
|
550
|
+
if (minutes > 0) parts.push(`${minutes}m`);
|
|
551
|
+
if (secs > 0 && hours === 0) parts.push(`${secs}s`);
|
|
552
|
+
return parts.join(" ") || "0 seconds";
|
|
553
|
+
}
|
|
554
|
+
function getProjectStateString(state) {
|
|
555
|
+
if (typeof state === "object" && state !== null) {
|
|
556
|
+
const keys = Object.keys(state);
|
|
557
|
+
return keys[0]?.toLowerCase() ?? "unknown";
|
|
558
|
+
}
|
|
559
|
+
return "unknown";
|
|
560
|
+
}
|
|
561
|
+
function getMilestoneStateString(state) {
|
|
562
|
+
if (typeof state === "object" && state !== null) {
|
|
563
|
+
const keys = Object.keys(state);
|
|
564
|
+
return keys[0]?.toLowerCase() ?? "unknown";
|
|
565
|
+
}
|
|
566
|
+
return "unknown";
|
|
567
|
+
}
|
|
568
|
+
async function fetchFundingRound(program, projectId, roundNumber) {
|
|
569
|
+
try {
|
|
570
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
571
|
+
const fundingRoundPda = getFundingRoundPDA(projectPda, roundNumber, program.programId);
|
|
572
|
+
return await getAccountNamespace(program).fundingRound.fetch(fundingRoundPda);
|
|
573
|
+
} catch (error) {
|
|
574
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
throw error;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
async function fetchAllFundingRounds(program, projectId) {
|
|
581
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
582
|
+
const rounds = await getAccountNamespace(program).fundingRound.all([
|
|
583
|
+
{
|
|
584
|
+
memcmp: {
|
|
585
|
+
offset: 8,
|
|
586
|
+
// Skip discriminator
|
|
587
|
+
bytes: projectPda.toBase58()
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
]);
|
|
591
|
+
return rounds.map((r) => ({
|
|
592
|
+
publicKey: r.publicKey,
|
|
593
|
+
account: r.account
|
|
594
|
+
}));
|
|
595
|
+
}
|
|
596
|
+
async function fetchRoundMilestone(program, projectId, roundNumber, milestoneIndex) {
|
|
597
|
+
try {
|
|
598
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
599
|
+
const milestonePda = getRoundMilestonePDA(projectPda, roundNumber, milestoneIndex, program.programId);
|
|
600
|
+
return await getAccountNamespace(program).milestone.fetch(milestonePda);
|
|
601
|
+
} catch (error) {
|
|
602
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
throw error;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async function fetchRoundInvestment(program, projectId, roundNumber, nftMint) {
|
|
609
|
+
try {
|
|
610
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
611
|
+
const investmentPda = getRoundInvestmentPDA(projectPda, roundNumber, nftMint, program.programId);
|
|
612
|
+
return await getAccountNamespace(program).investment.fetch(investmentPda);
|
|
613
|
+
} catch (error) {
|
|
614
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
617
|
+
throw error;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
async function fetchRoundInvestorMilestoneVesting(program, projectId, roundNumber, milestoneIndex, nftMint) {
|
|
621
|
+
try {
|
|
622
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
623
|
+
const investmentPda = getRoundInvestmentPDA(projectPda, roundNumber, nftMint, program.programId);
|
|
624
|
+
const vestingPda = getRoundInvestorMilestoneVestingPDA(
|
|
625
|
+
projectPda,
|
|
626
|
+
roundNumber,
|
|
627
|
+
milestoneIndex,
|
|
628
|
+
investmentPda,
|
|
629
|
+
program.programId
|
|
630
|
+
);
|
|
631
|
+
return await getAccountNamespace(program).investorMilestoneVesting.fetch(vestingPda);
|
|
632
|
+
} catch (error) {
|
|
633
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
throw error;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
async function fetchFutureRoundVault(program, projectId) {
|
|
640
|
+
try {
|
|
641
|
+
const projectPda = getProjectPDA(projectId, program.programId);
|
|
642
|
+
const vaultPda = getFutureRoundVaultPDA(projectPda, program.programId);
|
|
643
|
+
return await getAccountNamespace(program).futureRoundVault.fetch(vaultPda);
|
|
644
|
+
} catch (error) {
|
|
645
|
+
if (error instanceof Error && error.message?.includes("Account does not exist")) {
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
throw error;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
function canClaimRoundEarlyTokens(investment, currentTimestamp, isDev = false) {
|
|
652
|
+
if (investment.roundNumber < 2) {
|
|
653
|
+
return { canClaim: false, reason: "R1 investments use claim_investor_early_tokens instruction" };
|
|
654
|
+
}
|
|
655
|
+
return canClaimEarlyTokens(investment, currentTimestamp, isDev);
|
|
656
|
+
}
|
|
657
|
+
function isR1FullyFunded(project) {
|
|
658
|
+
const raised = typeof project.totalRaised === "number" ? new BN(project.totalRaised) : project.totalRaised;
|
|
659
|
+
const goal = typeof project.fundingGoal === "number" ? new BN(project.fundingGoal) : project.fundingGoal;
|
|
660
|
+
return raised.gte(goal);
|
|
661
|
+
}
|
|
662
|
+
function isFundingRoundFullyFunded(fundingRound) {
|
|
663
|
+
const stateStr = getRoundStateString(fundingRound.state);
|
|
664
|
+
return ["funded", "inProgress", "inprogress", "completed"].includes(stateStr);
|
|
665
|
+
}
|
|
666
|
+
async function isRoundFullyFunded(program, projectId, roundNumber) {
|
|
667
|
+
if (roundNumber === 1) {
|
|
668
|
+
const project = await fetchProject(program, projectId);
|
|
669
|
+
if (!project) return false;
|
|
670
|
+
return isR1FullyFunded(project);
|
|
671
|
+
} else {
|
|
672
|
+
const fundingRound = await fetchFundingRound(program, projectId, roundNumber);
|
|
673
|
+
if (!fundingRound) return false;
|
|
674
|
+
return isFundingRoundFullyFunded(fundingRound);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
async function canOpenNextRound(program, projectId) {
|
|
678
|
+
const project = await fetchProject(program, projectId);
|
|
679
|
+
if (!project) {
|
|
680
|
+
return { canOpen: false, reason: "Project not found" };
|
|
681
|
+
}
|
|
682
|
+
const projectState = getProjectStateString(project.state);
|
|
683
|
+
if (!["inProgress", "inprogress", "completed"].includes(projectState)) {
|
|
684
|
+
return {
|
|
685
|
+
canOpen: false,
|
|
686
|
+
reason: `Project must be in InProgress or Completed state. Current: ${projectState}`
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
if (projectState !== "completed" && project.milestonesPassed === 0) {
|
|
690
|
+
return {
|
|
691
|
+
canOpen: false,
|
|
692
|
+
reason: "At least one milestone must pass before opening next round"
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
if (project.activeRound !== 0) {
|
|
696
|
+
return {
|
|
697
|
+
canOpen: false,
|
|
698
|
+
reason: `Round ${project.activeRound} is already active`
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
const currentRound = project.roundCount;
|
|
702
|
+
const isCurrentFunded = await isRoundFullyFunded(program, projectId, currentRound);
|
|
703
|
+
if (!isCurrentFunded) {
|
|
704
|
+
return {
|
|
705
|
+
canOpen: false,
|
|
706
|
+
reason: `Current round ${currentRound} must be fully funded before opening next round`
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
const tokenomics = await fetchTokenomics(program, projectId);
|
|
710
|
+
if (!tokenomics) {
|
|
711
|
+
return { canOpen: false, reason: "Tokenomics not found" };
|
|
712
|
+
}
|
|
713
|
+
if (tokenomics.futureRoundAllocationBps === 0) {
|
|
714
|
+
return {
|
|
715
|
+
canOpen: false,
|
|
716
|
+
reason: "No future round allocation configured"
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
const remainingAllocation = tokenomics.futureRoundAllocationBps - (tokenomics.usedFutureRoundBps || 0);
|
|
720
|
+
if (remainingAllocation <= 0) {
|
|
721
|
+
return {
|
|
722
|
+
canOpen: false,
|
|
723
|
+
reason: "Future round allocation pool exhausted"
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
return {
|
|
727
|
+
canOpen: true,
|
|
728
|
+
nextRoundNumber: currentRound + 1
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
function getRemainingFutureRoundAllocation(tokenomics) {
|
|
732
|
+
return tokenomics.futureRoundAllocationBps - (tokenomics.usedFutureRoundBps || 0);
|
|
733
|
+
}
|
|
734
|
+
function getRoundStateString(state) {
|
|
735
|
+
if (typeof state === "object" && state !== null) {
|
|
736
|
+
const keys = Object.keys(state);
|
|
737
|
+
return keys[0]?.toLowerCase() ?? "unknown";
|
|
738
|
+
}
|
|
739
|
+
return "unknown";
|
|
740
|
+
}
|
|
242
741
|
|
|
243
|
-
export { accountExists, fetchAdminConfig, fetchAllInvestments, fetchAllMilestones, fetchAllVotes, fetchInvestment, fetchMilestone, fetchPivotProposal, fetchProject, fetchProjectByPda, fetchTgeEscrow, fetchVote };
|
|
742
|
+
export { EARLY_TOKEN_COOLING_PERIOD_SECONDS, EARLY_TOKEN_COOLING_PERIOD_SECONDS_DEV, EARLY_TOKEN_RELEASE_BPS, accountExists, calculateEarlyTokenAmount, calculateRemainingAllocation, canClaimEarlyTokens, canClaimFounderEarlyTokens, canClaimFounderMilestoneTokens, canClaimRoundEarlyTokens, canOpenNextRound, fetchAdminConfig, fetchAllAllocationProposals, fetchAllFundingRounds, fetchAllInvestments, fetchAllMilestones, fetchAllVotes, fetchAllocationProposal, fetchAllocationVote, fetchFounderMilestoneVesting, fetchFundingRound, fetchFutureRoundVault, fetchInvestment, fetchInvestorMilestoneVesting, fetchMilestone, fetchPivotProposal, fetchProject, fetchProjectByPda, fetchRoundInvestment, fetchRoundInvestorMilestoneVesting, fetchRoundMilestone, fetchSubAllocationVesting, fetchTgeEscrow, fetchTokenVault, fetchTokenomics, fetchVote, getRemainingFutureRoundAllocation, isFundingRoundFullyFunded, isR1FullyFunded, isRoundFullyFunded, requiresBurnForRefund };
|
|
244
743
|
//# sourceMappingURL=index.js.map
|
|
245
744
|
//# sourceMappingURL=index.js.map
|