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