@zemyth/raise-sdk 0.1.0

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 (54) hide show
  1. package/README.md +416 -0
  2. package/dist/accounts/index.cjs +258 -0
  3. package/dist/accounts/index.cjs.map +1 -0
  4. package/dist/accounts/index.d.cts +115 -0
  5. package/dist/accounts/index.d.ts +115 -0
  6. package/dist/accounts/index.js +245 -0
  7. package/dist/accounts/index.js.map +1 -0
  8. package/dist/constants/index.cjs +174 -0
  9. package/dist/constants/index.cjs.map +1 -0
  10. package/dist/constants/index.d.cts +143 -0
  11. package/dist/constants/index.d.ts +143 -0
  12. package/dist/constants/index.js +158 -0
  13. package/dist/constants/index.js.map +1 -0
  14. package/dist/errors/index.cjs +177 -0
  15. package/dist/errors/index.cjs.map +1 -0
  16. package/dist/errors/index.d.cts +83 -0
  17. package/dist/errors/index.d.ts +83 -0
  18. package/dist/errors/index.js +170 -0
  19. package/dist/errors/index.js.map +1 -0
  20. package/dist/index.cjs +2063 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +680 -0
  23. package/dist/index.d.ts +680 -0
  24. package/dist/index.js +1926 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/instructions/index.cjs +852 -0
  27. package/dist/instructions/index.cjs.map +1 -0
  28. package/dist/instructions/index.d.cts +452 -0
  29. package/dist/instructions/index.d.ts +452 -0
  30. package/dist/instructions/index.js +809 -0
  31. package/dist/instructions/index.js.map +1 -0
  32. package/dist/pdas/index.cjs +241 -0
  33. package/dist/pdas/index.cjs.map +1 -0
  34. package/dist/pdas/index.d.cts +171 -0
  35. package/dist/pdas/index.d.ts +171 -0
  36. package/dist/pdas/index.js +217 -0
  37. package/dist/pdas/index.js.map +1 -0
  38. package/dist/types/index.cjs +44 -0
  39. package/dist/types/index.cjs.map +1 -0
  40. package/dist/types/index.d.cts +229 -0
  41. package/dist/types/index.d.ts +229 -0
  42. package/dist/types/index.js +39 -0
  43. package/dist/types/index.js.map +1 -0
  44. package/package.json +130 -0
  45. package/src/accounts/index.ts +329 -0
  46. package/src/client.ts +715 -0
  47. package/src/constants/index.ts +205 -0
  48. package/src/errors/index.ts +222 -0
  49. package/src/events/index.ts +256 -0
  50. package/src/index.ts +253 -0
  51. package/src/instructions/index.ts +1504 -0
  52. package/src/pdas/index.ts +404 -0
  53. package/src/types/index.ts +267 -0
  54. package/src/utils/index.ts +277 -0
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Raise Constants
3
+ *
4
+ * Mirrors the on-chain program constants for client-side validation
5
+ * and display purposes.
6
+ */
7
+
8
+ // =============================================================================
9
+ // PDA Seeds
10
+ // =============================================================================
11
+
12
+ export const SEEDS = {
13
+ PROJECT: 'project',
14
+ MILESTONE: 'milestone',
15
+ INVESTMENT: 'investment',
16
+ VOTE: 'vote',
17
+ ESCROW: 'escrow',
18
+ PIVOT: 'pivot',
19
+ PIVOT_PROPOSAL: 'pivot_proposal',
20
+ TGE_ESCROW: 'tge_escrow',
21
+ TGE_ESCROW_VAULT: 'tge_escrow_vault',
22
+ SCAM_REPORT: 'scam_report',
23
+ ADMIN_CONFIG: 'admin-config',
24
+ NFT_MINT: 'nft_mint',
25
+ AUTHORITY: 'authority',
26
+ } as const;
27
+
28
+ // =============================================================================
29
+ // Validation Constants
30
+ // =============================================================================
31
+
32
+ export const VALIDATION = {
33
+ /** Minimum number of milestones per project */
34
+ MIN_MILESTONES: 2,
35
+ /** Maximum number of milestones per project */
36
+ MAX_MILESTONES: 10,
37
+ /** Milestone percentages must sum to this value */
38
+ MILESTONE_PERCENTAGE_SUM: 100,
39
+ /** Maximum funding buffer (110% of goal) */
40
+ MAX_FUNDING_BUFFER_PERCENT: 110,
41
+ /** Maximum metadata URI length */
42
+ MAX_METADATA_URI_LENGTH: 200,
43
+ /** Maximum pivot description length */
44
+ MAX_PIVOT_DESCRIPTION_LEN: 256,
45
+ /** Maximum pivot vision length */
46
+ MAX_PIVOT_VISION_LEN: 512,
47
+ /** Maximum pivot justification length */
48
+ MAX_PIVOT_JUSTIFICATION_LEN: 512,
49
+ } as const;
50
+
51
+ // =============================================================================
52
+ // Timing Constants (in seconds)
53
+ // =============================================================================
54
+
55
+ export const TIMING = {
56
+ /** Production voting period (14 days) */
57
+ VOTING_PERIOD_SECONDS: 1_209_600,
58
+ /** Production hold period (7 days) */
59
+ HOLD_PERIOD_SECONDS: 604_800,
60
+ /** Inactivity timeout (90 days) */
61
+ INACTIVITY_TIMEOUT_SECONDS: 7_776_000,
62
+ /** Abandonment timeout (90 days) */
63
+ ABANDONMENT_TIMEOUT_SECONDS: 7_776_000,
64
+ /** Refund window (14 days) */
65
+ REFUND_WINDOW_SECONDS: 1_209_600,
66
+ /** Pivot withdrawal window (7 days) */
67
+ PIVOT_WITHDRAWAL_WINDOW_SECONDS: 604_800,
68
+ /** Minimum TGE date (15 days from now) */
69
+ TGE_MIN_DAYS: 1_296_000,
70
+ /** Maximum TGE date (90 days from now) */
71
+ TGE_MAX_DAYS: 7_776_000,
72
+ /** Post-TGE holdback period (30 days) */
73
+ POST_TGE_HOLDBACK_DAYS: 2_592_000,
74
+ } as const;
75
+
76
+ // =============================================================================
77
+ // Tier Configuration Constraints
78
+ // =============================================================================
79
+
80
+ export const TIER_CONSTRAINTS = {
81
+ /** Minimum number of tiers */
82
+ MIN_TIERS: 1,
83
+ /** Maximum number of tiers */
84
+ MAX_TIERS: 10,
85
+ /** Minimum tier amount (10 USDC in lamports) */
86
+ MIN_TIER_AMOUNT: 10_000_000n,
87
+ /** Minimum max_lots per tier */
88
+ MIN_TIER_MAX_LOTS: 1,
89
+ /** Minimum token ratio */
90
+ MIN_TIER_TOKEN_RATIO: 1n,
91
+ /** Minimum vote multiplier (100 = 1.0x) */
92
+ MIN_TIER_VOTE_MULTIPLIER: 100,
93
+ } as const;
94
+
95
+ // =============================================================================
96
+ // Investment Tiers (Legacy - kept for backwards compatibility)
97
+ // =============================================================================
98
+
99
+ export enum InvestmentTier {
100
+ Bronze = 'Bronze',
101
+ Silver = 'Silver',
102
+ Gold = 'Gold',
103
+ Platinum = 'Platinum',
104
+ Diamond = 'Diamond',
105
+ }
106
+
107
+ /** Investment tier minimum amounts in USDC lamports (6 decimals) - LEGACY */
108
+ export const TIER_MINIMUMS = {
109
+ [InvestmentTier.Bronze]: 100_000_000n, // 100 USDC
110
+ [InvestmentTier.Silver]: 500_000_000n, // 500 USDC
111
+ [InvestmentTier.Gold]: 1_000_000_000n, // 1,000 USDC
112
+ [InvestmentTier.Platinum]: 5_000_000_000n, // 5,000 USDC
113
+ [InvestmentTier.Diamond]: 10_000_000_000n, // 10,000 USDC
114
+ } as const;
115
+
116
+ /** Vote weight multipliers (scaled by 100) - LEGACY */
117
+ export const TIER_VOTE_MULTIPLIERS = {
118
+ [InvestmentTier.Bronze]: 100, // 1.0x
119
+ [InvestmentTier.Silver]: 120, // 1.2x
120
+ [InvestmentTier.Gold]: 150, // 1.5x
121
+ [InvestmentTier.Platinum]: 200, // 2.0x
122
+ [InvestmentTier.Diamond]: 300, // 3.0x
123
+ } as const;
124
+
125
+ /** Token allocation multipliers (same as vote multipliers) - LEGACY */
126
+ export const TIER_TOKEN_MULTIPLIERS = {
127
+ [InvestmentTier.Bronze]: 100,
128
+ [InvestmentTier.Silver]: 120,
129
+ [InvestmentTier.Gold]: 150,
130
+ [InvestmentTier.Platinum]: 200,
131
+ [InvestmentTier.Diamond]: 300,
132
+ } as const;
133
+
134
+ /** Get tier from investment amount (in lamports) - LEGACY, use project.tiers instead */
135
+ export function getTierFromAmount(amount: bigint): InvestmentTier {
136
+ if (amount >= TIER_MINIMUMS[InvestmentTier.Diamond]) return InvestmentTier.Diamond;
137
+ if (amount >= TIER_MINIMUMS[InvestmentTier.Platinum]) return InvestmentTier.Platinum;
138
+ if (amount >= TIER_MINIMUMS[InvestmentTier.Gold]) return InvestmentTier.Gold;
139
+ if (amount >= TIER_MINIMUMS[InvestmentTier.Silver]) return InvestmentTier.Silver;
140
+ return InvestmentTier.Bronze;
141
+ }
142
+
143
+ /** Get vote multiplier for an investment amount - LEGACY */
144
+ export function getVoteMultiplier(amount: bigint): number {
145
+ const tier = getTierFromAmount(amount);
146
+ return TIER_VOTE_MULTIPLIERS[tier] / 100;
147
+ }
148
+
149
+ /** Get token multiplier for an investment amount - LEGACY */
150
+ export function getTokenMultiplier(amount: bigint): number {
151
+ const tier = getTierFromAmount(amount);
152
+ return TIER_TOKEN_MULTIPLIERS[tier] / 100;
153
+ }
154
+
155
+ /**
156
+ * Find matching tier index for an investment amount (threshold-based)
157
+ * Returns the highest tier where amount >= tier.amount
158
+ */
159
+ export function findTierIndex(tiers: Array<{ amount: bigint }>, amount: bigint): number | null {
160
+ for (let i = tiers.length - 1; i >= 0; i--) {
161
+ if (amount >= tiers[i].amount) {
162
+ return i;
163
+ }
164
+ }
165
+ return null;
166
+ }
167
+
168
+ // =============================================================================
169
+ // Voting and Governance
170
+ // =============================================================================
171
+
172
+ export const GOVERNANCE = {
173
+ /** Scam report threshold (30%) */
174
+ SCAM_THRESHOLD_PERCENT: 30,
175
+ /** Consecutive milestone failures before exit window eligible */
176
+ CONSECUTIVE_FAILURES_THRESHOLD: 3,
177
+ /** Milestone approval threshold (>50% weighted approval per whitepaper voting.md:100-101) */
178
+ MILESTONE_APPROVAL_THRESHOLD_PERCENT: 50,
179
+ } as const;
180
+
181
+ // =============================================================================
182
+ // NFT Constants
183
+ // =============================================================================
184
+
185
+ export const NFT = {
186
+ /** NFT symbol */
187
+ SYMBOL: 'SNI',
188
+ /** NFT name prefix */
189
+ NAME_PREFIX: 'Raise Investment #',
190
+ /** Royalty basis points (2%) */
191
+ ROYALTY_BASIS_POINTS: 200,
192
+ } as const;
193
+
194
+ // =============================================================================
195
+ // USDC Constants
196
+ // =============================================================================
197
+
198
+ export const USDC = {
199
+ /** USDC decimals */
200
+ DECIMALS: 6,
201
+ /** Convert USDC to lamports */
202
+ toAmount: (usdc: number): bigint => BigInt(Math.floor(usdc * 10 ** 6)),
203
+ /** Convert lamports to USDC */
204
+ fromAmount: (lamports: bigint): number => Number(lamports) / 10 ** 6,
205
+ } as const;
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Raise Error Handling
3
+ *
4
+ * Maps program error codes to SDK errors with helpful messages.
5
+ */
6
+
7
+ import { AnchorError } from '@coral-xyz/anchor';
8
+
9
+ // =============================================================================
10
+ // Error Codes (matching Rust program)
11
+ // =============================================================================
12
+
13
+ export const ERROR_CODES = {
14
+ // State Transition Errors (6000-6099)
15
+ InvalidStateTransition: 6000,
16
+ ProjectNotInOpenState: 6001,
17
+ ProjectNotInProgress: 6002,
18
+ MilestoneNotUnderReview: 6003,
19
+ VotingPeriodEnded: 6004,
20
+ VotingPeriodNotEnded: 6005,
21
+ MilestoneNotPassed: 6006,
22
+ MilestoneAlreadyUnlocked: 6007,
23
+ ProjectAlreadyFunded: 6008,
24
+ ProjectNotFunded: 6009,
25
+
26
+ // Authorization Errors (6100-6199)
27
+ UnauthorizedFounder: 6100,
28
+ UnauthorizedAdmin: 6101,
29
+ NotInvestor: 6102,
30
+ AlreadyVoted: 6103,
31
+
32
+ // Investment Errors (6200-6299)
33
+ InvestmentBelowMinimum: 6200,
34
+ FundingGoalExceeded: 6201,
35
+ InvalidTier: 6202,
36
+ CoolingOffPeriodActive: 6203,
37
+ CoolingOffPeriodExpired: 6204,
38
+
39
+ // Milestone Errors (6300-6399)
40
+ InvalidMilestoneIndex: 6300,
41
+ MilestonePercentageInvalid: 6301,
42
+ TotalPercentageExceeded: 6302,
43
+ MilestoneNotInProgress: 6303,
44
+ MilestoneNotApproved: 6304,
45
+
46
+ // TGE Errors (6400-6499)
47
+ TgeDateNotSet: 6400,
48
+ TgeDateAlreadySet: 6401,
49
+ TgeDateTooSoon: 6402,
50
+ TgeDateTooLate: 6403,
51
+ TgeNotReached: 6404,
52
+ TokensAlreadyClaimed: 6405,
53
+ InsufficientTokensDeposited: 6406,
54
+
55
+ // Pivot Errors (6500-6599)
56
+ PivotAlreadyProposed: 6500,
57
+ NoPivotProposed: 6501,
58
+ PivotNotApproved: 6502,
59
+ PivotWindowNotEnded: 6503,
60
+ PivotWindowEnded: 6504,
61
+ AlreadyWithdrawnFromPivot: 6505,
62
+
63
+ // Refund Errors (6800-6899)
64
+ RefundAlreadyClaimed: 6800,
65
+ RefundNotAvailable: 6801,
66
+ ProjectNotAbandoned: 6802,
67
+
68
+ // Scam Errors (6900-6999)
69
+ ScamReportPeriodEnded: 6900,
70
+ ScamAlreadyReported: 6901,
71
+ ScamNotConfirmed: 6902,
72
+ HoldbackAlreadyReleased: 6903,
73
+ HoldbackPeriodNotEnded: 6904,
74
+ } as const;
75
+
76
+ // =============================================================================
77
+ // Error Messages
78
+ // =============================================================================
79
+
80
+ export const ERROR_MESSAGES: Record<number, string> = {
81
+ // State Transition Errors
82
+ [ERROR_CODES.InvalidStateTransition]: 'Invalid project state transition',
83
+ [ERROR_CODES.ProjectNotInOpenState]: 'Project must be in Open state to accept investments',
84
+ [ERROR_CODES.ProjectNotInProgress]: 'Project must be InProgress to perform this action',
85
+ [ERROR_CODES.MilestoneNotUnderReview]: 'Milestone must be under review to vote',
86
+ [ERROR_CODES.VotingPeriodEnded]: 'Voting period has ended',
87
+ [ERROR_CODES.VotingPeriodNotEnded]: 'Voting period has not ended yet',
88
+ [ERROR_CODES.MilestoneNotPassed]: 'Milestone did not pass voting',
89
+ [ERROR_CODES.MilestoneAlreadyUnlocked]: 'Milestone funds already unlocked',
90
+ [ERROR_CODES.ProjectAlreadyFunded]: 'Project has already reached funding goal',
91
+ [ERROR_CODES.ProjectNotFunded]: 'Project has not reached funding goal',
92
+
93
+ // Authorization Errors
94
+ [ERROR_CODES.UnauthorizedFounder]: 'Only the project founder can perform this action',
95
+ [ERROR_CODES.UnauthorizedAdmin]: 'Only the admin can perform this action',
96
+ [ERROR_CODES.NotInvestor]: 'You must be an investor to perform this action',
97
+ [ERROR_CODES.AlreadyVoted]: 'You have already voted on this',
98
+
99
+ // Investment Errors
100
+ [ERROR_CODES.InvestmentBelowMinimum]: 'Investment amount below minimum tier requirement',
101
+ [ERROR_CODES.FundingGoalExceeded]: 'Investment would exceed funding goal',
102
+ [ERROR_CODES.InvalidTier]: 'Invalid investment tier',
103
+ [ERROR_CODES.CoolingOffPeriodActive]: 'Investment is within 24-hour cooling-off period',
104
+ [ERROR_CODES.CoolingOffPeriodExpired]: 'Cooling-off period has expired, cannot cancel',
105
+
106
+ // Milestone Errors
107
+ [ERROR_CODES.InvalidMilestoneIndex]: 'Invalid milestone index',
108
+ [ERROR_CODES.MilestonePercentageInvalid]: 'Milestone percentage must be between 1-100',
109
+ [ERROR_CODES.TotalPercentageExceeded]: 'Total milestone percentages exceed 100%',
110
+ [ERROR_CODES.MilestoneNotInProgress]: 'Milestone must be in progress',
111
+ [ERROR_CODES.MilestoneNotApproved]: 'Milestone must be approved first',
112
+
113
+ // TGE Errors
114
+ [ERROR_CODES.TgeDateNotSet]: 'TGE date has not been set',
115
+ [ERROR_CODES.TgeDateAlreadySet]: 'TGE date has already been set',
116
+ [ERROR_CODES.TgeDateTooSoon]: 'TGE date must be at least 15 days in the future',
117
+ [ERROR_CODES.TgeDateTooLate]: 'TGE date must be within 90 days',
118
+ [ERROR_CODES.TgeNotReached]: 'TGE date has not been reached',
119
+ [ERROR_CODES.TokensAlreadyClaimed]: 'Tokens have already been claimed',
120
+ [ERROR_CODES.InsufficientTokensDeposited]: 'Insufficient tokens deposited by founder',
121
+
122
+ // Pivot Errors
123
+ [ERROR_CODES.PivotAlreadyProposed]: 'A pivot is already pending',
124
+ [ERROR_CODES.NoPivotProposed]: 'No pivot has been proposed',
125
+ [ERROR_CODES.PivotNotApproved]: 'Pivot has not been approved by admin',
126
+ [ERROR_CODES.PivotWindowNotEnded]: '7-day withdrawal window has not ended',
127
+ [ERROR_CODES.PivotWindowEnded]: '7-day withdrawal window has ended',
128
+ [ERROR_CODES.AlreadyWithdrawnFromPivot]: 'Already withdrawn from this pivot',
129
+
130
+ // Refund Errors
131
+ [ERROR_CODES.RefundAlreadyClaimed]: 'Refund has already been claimed',
132
+ [ERROR_CODES.RefundNotAvailable]: 'Refund is not available',
133
+ [ERROR_CODES.ProjectNotAbandoned]: 'Project has not been abandoned',
134
+
135
+ // Scam Errors
136
+ [ERROR_CODES.ScamReportPeriodEnded]: '30-day scam report period has ended',
137
+ [ERROR_CODES.ScamAlreadyReported]: 'Already reported this project for scam',
138
+ [ERROR_CODES.ScamNotConfirmed]: 'Scam has not been confirmed',
139
+ [ERROR_CODES.HoldbackAlreadyReleased]: 'Holdback has already been released',
140
+ [ERROR_CODES.HoldbackPeriodNotEnded]: '30-day holdback period has not ended',
141
+ };
142
+
143
+ // =============================================================================
144
+ // SDK Error Class
145
+ // =============================================================================
146
+
147
+ export class RaiseError extends Error {
148
+ constructor(
149
+ public readonly code: number,
150
+ message: string,
151
+ public readonly logs?: string[]
152
+ ) {
153
+ super(message);
154
+ this.name = 'RaiseError';
155
+ }
156
+
157
+ /**
158
+ * Create from an Anchor error
159
+ */
160
+ static fromAnchorError(error: AnchorError): RaiseError {
161
+ const code = error.error.errorCode.number;
162
+ const message = ERROR_MESSAGES[code] || error.error.errorMessage;
163
+ return new RaiseError(code, message, error.logs);
164
+ }
165
+
166
+ /**
167
+ * Check if this is a specific error type
168
+ */
169
+ is(errorCode: number): boolean {
170
+ return this.code === errorCode;
171
+ }
172
+ }
173
+
174
+ // =============================================================================
175
+ // Error Handling Utilities
176
+ // =============================================================================
177
+
178
+ /**
179
+ * Parse an error and return a RaiseError if it's a program error
180
+ */
181
+ export function parseError(error: unknown): RaiseError | Error {
182
+ if (error instanceof AnchorError) {
183
+ return RaiseError.fromAnchorError(error);
184
+ }
185
+ if (error instanceof Error) {
186
+ return error;
187
+ }
188
+ return new Error(String(error));
189
+ }
190
+
191
+ /**
192
+ * Check if an error is a specific Raise error
193
+ */
194
+ export function isRaiseError(
195
+ error: unknown,
196
+ code?: number
197
+ ): error is RaiseError {
198
+ if (!(error instanceof RaiseError)) {
199
+ return false;
200
+ }
201
+ if (code !== undefined) {
202
+ return error.code === code;
203
+ }
204
+ return true;
205
+ }
206
+
207
+ /**
208
+ * Get a user-friendly error message
209
+ */
210
+ export function getErrorMessage(error: unknown): string {
211
+ if (error instanceof RaiseError) {
212
+ return error.message;
213
+ }
214
+ if (error instanceof AnchorError) {
215
+ const code = error.error.errorCode.number;
216
+ return ERROR_MESSAGES[code] || error.error.errorMessage;
217
+ }
218
+ if (error instanceof Error) {
219
+ return error.message;
220
+ }
221
+ return 'An unknown error occurred';
222
+ }
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Raise Event Parsing
3
+ *
4
+ * Helpers for parsing program events from transaction logs.
5
+ */
6
+
7
+ import { BN } from '@coral-xyz/anchor';
8
+ import { PublicKey } from '@solana/web3.js';
9
+
10
+ // =============================================================================
11
+ // Event Types
12
+ // =============================================================================
13
+
14
+ export interface ProjectCreatedEvent {
15
+ name: 'ProjectCreated';
16
+ data: {
17
+ projectId: BN;
18
+ founder: PublicKey;
19
+ fundingGoal: BN;
20
+ metadataUri: string;
21
+ };
22
+ }
23
+
24
+ export interface ProjectApprovedEvent {
25
+ name: 'ProjectApproved';
26
+ data: {
27
+ projectId: BN;
28
+ };
29
+ }
30
+
31
+ export interface ProjectFundedEvent {
32
+ name: 'ProjectFunded';
33
+ data: {
34
+ projectId: BN;
35
+ amountRaised: BN;
36
+ };
37
+ }
38
+
39
+ export interface InvestmentMadeEvent {
40
+ name: 'InvestmentMade';
41
+ data: {
42
+ projectId: BN;
43
+ investor: PublicKey;
44
+ amount: BN;
45
+ nftMint: PublicKey;
46
+ tier: number;
47
+ voteWeight: BN;
48
+ };
49
+ }
50
+
51
+ export interface InvestmentCancelledEvent {
52
+ name: 'InvestmentCancelled';
53
+ data: {
54
+ projectId: BN;
55
+ investor: PublicKey;
56
+ amount: BN;
57
+ nftMint: PublicKey;
58
+ };
59
+ }
60
+
61
+ export interface MilestoneCreatedEvent {
62
+ name: 'MilestoneCreated';
63
+ data: {
64
+ projectId: BN;
65
+ milestoneIndex: number;
66
+ percentage: number;
67
+ description: string;
68
+ };
69
+ }
70
+
71
+ export interface MilestoneSubmittedEvent {
72
+ name: 'MilestoneSubmitted';
73
+ data: {
74
+ projectId: BN;
75
+ milestoneIndex: number;
76
+ votingEndsAt: BN;
77
+ };
78
+ }
79
+
80
+ export interface VoteCastEvent {
81
+ name: 'VoteCast';
82
+ data: {
83
+ projectId: BN;
84
+ milestoneIndex: number;
85
+ voter: PublicKey;
86
+ choice: { good: object } | { bad: object };
87
+ weight: BN;
88
+ };
89
+ }
90
+
91
+ export interface MilestoneVoteFinalizedEvent {
92
+ name: 'MilestoneVoteFinalized';
93
+ data: {
94
+ projectId: BN;
95
+ milestoneIndex: number;
96
+ passed: boolean;
97
+ yesVotes: BN;
98
+ noVotes: BN;
99
+ };
100
+ }
101
+
102
+ export interface FundsUnlockedEvent {
103
+ name: 'FundsUnlocked';
104
+ data: {
105
+ projectId: BN;
106
+ milestoneIndex: number;
107
+ amount: BN;
108
+ };
109
+ }
110
+
111
+ export interface TgeDateSetEvent {
112
+ name: 'TgeDateSet';
113
+ data: {
114
+ projectId: BN;
115
+ tgeDate: BN;
116
+ tokenMint: PublicKey;
117
+ };
118
+ }
119
+
120
+ export interface TokensDepositedEvent {
121
+ name: 'TokensDeposited';
122
+ data: {
123
+ projectId: BN;
124
+ amount: BN;
125
+ };
126
+ }
127
+
128
+ export interface TokensClaimedEvent {
129
+ name: 'TokensClaimed';
130
+ data: {
131
+ projectId: BN;
132
+ investor: PublicKey;
133
+ amount: BN;
134
+ };
135
+ }
136
+
137
+ export interface RefundClaimedEvent {
138
+ name: 'RefundClaimed';
139
+ data: {
140
+ projectId: BN;
141
+ investor: PublicKey;
142
+ amount: BN;
143
+ };
144
+ }
145
+
146
+ export interface PivotProposedEvent {
147
+ name: 'PivotProposed';
148
+ data: {
149
+ projectId: BN;
150
+ newMetadataUri: string;
151
+ };
152
+ }
153
+
154
+ export interface PivotApprovedEvent {
155
+ name: 'PivotApproved';
156
+ data: {
157
+ projectId: BN;
158
+ withdrawalWindowEndsAt: BN;
159
+ };
160
+ }
161
+
162
+ export interface PivotFinalizedEvent {
163
+ name: 'PivotFinalized';
164
+ data: {
165
+ projectId: BN;
166
+ withdrawnAmount: BN;
167
+ withdrawnCount: number;
168
+ };
169
+ }
170
+
171
+ export interface MilestoneReworkedEvent {
172
+ name: 'MilestoneReworked';
173
+ data: {
174
+ projectId: BN;
175
+ milestoneIndex: number;
176
+ milestoneKey: PublicKey;
177
+ consecutiveFailures: number;
178
+ reworkedAt: BN;
179
+ };
180
+ }
181
+
182
+ export type RaiseEvent =
183
+ | ProjectCreatedEvent
184
+ | ProjectApprovedEvent
185
+ | ProjectFundedEvent
186
+ | InvestmentMadeEvent
187
+ | InvestmentCancelledEvent
188
+ | MilestoneCreatedEvent
189
+ | MilestoneSubmittedEvent
190
+ | VoteCastEvent
191
+ | MilestoneVoteFinalizedEvent
192
+ | FundsUnlockedEvent
193
+ | TgeDateSetEvent
194
+ | TokensDepositedEvent
195
+ | TokensClaimedEvent
196
+ | RefundClaimedEvent
197
+ | PivotProposedEvent
198
+ | PivotApprovedEvent
199
+ | PivotFinalizedEvent
200
+ | MilestoneReworkedEvent;
201
+
202
+ // =============================================================================
203
+ // Event Parsing
204
+ // =============================================================================
205
+
206
+ /**
207
+ * Event name constants
208
+ */
209
+ export const EVENT_NAMES = {
210
+ ProjectCreated: 'ProjectCreated',
211
+ ProjectApproved: 'ProjectApproved',
212
+ ProjectFunded: 'ProjectFunded',
213
+ InvestmentMade: 'InvestmentMade',
214
+ InvestmentCancelled: 'InvestmentCancelled',
215
+ MilestoneCreated: 'MilestoneCreated',
216
+ MilestoneSubmitted: 'MilestoneSubmitted',
217
+ VoteCast: 'VoteCast',
218
+ MilestoneVoteFinalized: 'MilestoneVoteFinalized',
219
+ FundsUnlocked: 'FundsUnlocked',
220
+ TgeDateSet: 'TgeDateSet',
221
+ TokensDeposited: 'TokensDeposited',
222
+ TokensClaimed: 'TokensClaimed',
223
+ RefundClaimed: 'RefundClaimed',
224
+ PivotProposed: 'PivotProposed',
225
+ PivotApproved: 'PivotApproved',
226
+ PivotFinalized: 'PivotFinalized',
227
+ MilestoneReworked: 'MilestoneReworked',
228
+ } as const;
229
+
230
+ /**
231
+ * Filter events by name
232
+ *
233
+ * @param events - Array of events
234
+ * @param name - Event name to filter
235
+ * @returns Filtered events
236
+ */
237
+ export function filterEventsByName<T extends RaiseEvent>(
238
+ events: RaiseEvent[],
239
+ name: T['name']
240
+ ): T[] {
241
+ return events.filter((e) => e.name === name) as T[];
242
+ }
243
+
244
+ /**
245
+ * Get the first event of a specific type
246
+ *
247
+ * @param events - Array of events
248
+ * @param name - Event name to find
249
+ * @returns First matching event or undefined
250
+ */
251
+ export function findEvent<T extends RaiseEvent>(
252
+ events: RaiseEvent[],
253
+ name: T['name']
254
+ ): T | undefined {
255
+ return events.find((e) => e.name === name) as T | undefined;
256
+ }