@zemyth/raise-sdk 0.1.1 → 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.
Files changed (47) hide show
  1. package/README.md +11 -9
  2. package/dist/accounts/index.cjs +531 -3
  3. package/dist/accounts/index.cjs.map +1 -1
  4. package/dist/accounts/index.d.cts +307 -2
  5. package/dist/accounts/index.d.ts +307 -2
  6. package/dist/accounts/index.js +503 -4
  7. package/dist/accounts/index.js.map +1 -1
  8. package/dist/constants/index.cjs +41 -3
  9. package/dist/constants/index.cjs.map +1 -1
  10. package/dist/constants/index.d.cts +38 -3
  11. package/dist/constants/index.d.ts +38 -3
  12. package/dist/constants/index.js +40 -4
  13. package/dist/constants/index.js.map +1 -1
  14. package/dist/index.cjs +2297 -361
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +566 -7
  17. package/dist/index.d.ts +566 -7
  18. package/dist/index.js +2279 -379
  19. package/dist/index.js.map +1 -1
  20. package/dist/instructions/index.cjs +783 -40
  21. package/dist/instructions/index.cjs.map +1 -1
  22. package/dist/instructions/index.d.cts +492 -6
  23. package/dist/instructions/index.d.ts +492 -6
  24. package/dist/instructions/index.js +762 -42
  25. package/dist/instructions/index.js.map +1 -1
  26. package/dist/pdas/index.cjs +163 -1
  27. package/dist/pdas/index.cjs.map +1 -1
  28. package/dist/pdas/index.d.cts +131 -1
  29. package/dist/pdas/index.d.ts +131 -1
  30. package/dist/pdas/index.js +151 -2
  31. package/dist/pdas/index.js.map +1 -1
  32. package/dist/types/index.cjs +9 -0
  33. package/dist/types/index.cjs.map +1 -1
  34. package/dist/types/index.d.cts +586 -3
  35. package/dist/types/index.d.ts +586 -3
  36. package/dist/types/index.js +9 -1
  37. package/dist/types/index.js.map +1 -1
  38. package/package.json +5 -3
  39. package/src/__tests__/dynamic-tokenomics.test.ts +358 -0
  40. package/src/accounts/index.ts +852 -1
  41. package/src/client.ts +1130 -1
  42. package/src/constants/index.ts +48 -2
  43. package/src/index.ts +58 -0
  44. package/src/instructions/index.ts +1383 -40
  45. package/src/pdas/index.ts +346 -0
  46. package/src/types/index.ts +698 -2
  47. package/src/utils/index.ts +90 -0
@@ -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/pdas/index.ts
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