@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 +1 @@
1
- {"version":3,"sources":["../../src/types/index.ts"],"names":["ProjectState","MilestoneState","VoteChoice","PivotState"],"mappings":";AAaO,IAAK,YAAA,qBAAAA,aAAAA,KAAL;AACL,EAAAA,cAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,cAAA,iBAAA,CAAA,GAAkB,iBAAA;AAClB,EAAAA,cAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,cAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,cAAA,YAAA,CAAA,GAAa,YAAA;AACb,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AAVF,EAAA,OAAAA,aAAAA;AAAA,CAAA,EAAA,YAAA,IAAA,EAAA;AAaL,IAAK,cAAA,qBAAAC,eAAAA,KAAL;AACL,EAAAA,gBAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,gBAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,gBAAA,YAAA,CAAA,GAAa,YAAA;AACb,EAAAA,gBAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,gBAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,gBAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,gBAAA,UAAA,CAAA,GAAW,UAAA;AAPD,EAAA,OAAAA,eAAAA;AAAA,CAAA,EAAA,cAAA,IAAA,EAAA;AAUL,IAAK,UAAA,qBAAAC,WAAAA,KAAL;AACL,EAAAA,YAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,YAAA,KAAA,CAAA,GAAM,KAAA;AAFI,EAAA,OAAAA,WAAAA;AAAA,CAAA,EAAA,UAAA,IAAA,EAAA;AAKL,IAAK,UAAA,qBAAAC,WAAAA,KAAL;AACL,EAAAA,YAAA,0BAAA,CAAA,GAA2B,0BAAA;AAC3B,EAAAA,YAAA,gCAAA,CAAA,GAAiC,gCAAA;AACjC,EAAAA,YAAA,WAAA,CAAA,GAAY,WAAA;AAHF,EAAA,OAAAA,WAAAA;AAAA,CAAA,EAAA,UAAA,IAAA,EAAA","file":"index.js","sourcesContent":["/**\n * Raise Type Definitions\n *\n * Re-exports types from the IDL and provides additional utility types.\n */\n\nimport { PublicKey } from '@solana/web3.js';\nimport { BN } from '@coral-xyz/anchor';\n\n// =============================================================================\n// Project State Enums\n// =============================================================================\n\nexport enum ProjectState {\n Draft = 'draft',\n PendingApproval = 'pendingApproval',\n Open = 'open',\n Funded = 'funded',\n InProgress = 'inProgress',\n Completed = 'completed',\n Abandoned = 'abandoned',\n Failed = 'failed',\n TGEFailed = 'tgeFailed',\n Cancelled = 'cancelled',\n}\n\nexport enum MilestoneState {\n Proposed = 'proposed',\n Approved = 'approved',\n InProgress = 'inProgress',\n UnderReview = 'underReview',\n Passed = 'passed',\n Failed = 'failed',\n Unlocked = 'unlocked',\n}\n\nexport enum VoteChoice {\n Good = 'good',\n Bad = 'bad',\n}\n\nexport enum PivotState {\n PendingModeratorApproval = 'pendingModeratorApproval',\n ApprovedAwaitingInvestorWindow = 'approvedAwaitingInvestorWindow',\n Finalized = 'finalized',\n}\n\n// =============================================================================\n// Account Types\n// =============================================================================\n\n/**\n * Tier configuration stored on-chain (includes filled_lots)\n */\nexport interface Tier {\n /** USDC amount per lot */\n amount: BN;\n /** Maximum lots available */\n maxLots: number;\n /** Currently filled lots */\n filledLots: number;\n /** Token allocation per $1 invested */\n tokenRatio: BN;\n /** Vote weight multiplier (basis points, 100 = 1.0x) */\n voteMultiplier: number;\n}\n\n/**\n * Tier configuration input for project initialization\n * Founders pass these when creating a project\n */\nexport interface TierConfig {\n /** USDC amount per lot (must be >= 10 USDC = 10_000_000 lamports) */\n amount: BN;\n /** Maximum lots available (must be >= 1) */\n maxLots: number;\n /** Token allocation per $1 invested (must be >= 1) */\n tokenRatio: BN;\n /** Vote weight multiplier (basis points, must be >= 100 = 1.0x) */\n voteMultiplier: number;\n}\n\nexport interface ProjectAccount {\n founder: PublicKey;\n projectId: BN;\n fundingGoal: BN;\n amountRaised: BN;\n state: ProjectState;\n metadataUri: string;\n escrow: PublicKey;\n currentMilestone: number;\n totalMilestones: number;\n /** Number of active tiers (1-10) */\n tierCount: number;\n /** All tier slots (only first tierCount are active) */\n tiers: Tier[];\n tokenMint: PublicKey | null;\n tgeDate: BN | null;\n tokensDeposited: BN;\n tokenAllocationBps: number;\n totalTokenAllocation: BN;\n consecutiveFailures: number;\n investorCount: number;\n bump: number;\n}\n\nexport interface MilestoneAccount {\n project: PublicKey;\n milestoneIndex: number;\n percentage: number;\n description: string;\n state: MilestoneState;\n yesVotes: BN;\n noVotes: BN;\n totalWeight: BN;\n voterCount: number;\n votingEndsAt: BN | null;\n bump: number;\n}\n\nexport interface InvestmentAccount {\n project: PublicKey;\n investor: PublicKey;\n nftMint: PublicKey;\n amount: BN;\n voteWeight: BN;\n tokenAllocation: BN;\n tier: number;\n investedAt: BN;\n tokensClaimed: boolean;\n withdrawnFromPivot: boolean;\n refundClaimed: boolean;\n bump: number;\n}\n\nexport interface VoteAccount {\n milestone: PublicKey;\n voter: PublicKey;\n choice: VoteChoice;\n weight: BN;\n votedAt: BN;\n bump: number;\n}\n\nexport interface AdminConfigAccount {\n admin: PublicKey;\n pendingAdmin: PublicKey | null;\n bump: number;\n}\n\nexport interface PivotProposalAccount {\n project: PublicKey;\n newMetadataUri: string;\n newMilestones: Array<{ percentage: number; description: string }>;\n state: PivotState;\n proposedAt: BN;\n approvedAt: BN | null;\n withdrawalWindowEndsAt: BN | null;\n withdrawnAmount: BN;\n withdrawnCount: number;\n bump: number;\n}\n\nexport interface TgeEscrowAccount {\n project: PublicKey;\n holdbackAmount: BN;\n scamReports: BN;\n scamWeight: BN;\n scamConfirmed: boolean;\n holdbackReleased: boolean;\n bump: number;\n}\n\n// =============================================================================\n// Instruction Arguments\n// =============================================================================\n\nexport interface InitializeProjectArgs {\n projectId: BN;\n fundingGoal: BN;\n metadataUri: string;\n /** Founder-configured tiers (1-10 tiers) */\n tiers: TierConfig[];\n}\n\nexport interface CreateMilestoneArgs {\n milestoneIndex: number;\n percentage: number;\n description: string;\n}\n\nexport interface InvestArgs {\n amount: BN;\n}\n\nexport interface VoteOnMilestoneArgs {\n choice: VoteChoice;\n}\n\nexport interface SetTgeDateArgs {\n tgeDate: BN;\n tokenMint: PublicKey;\n}\n\nexport interface DepositTokensArgs {\n amount: BN;\n}\n\nexport interface ProposePivotArgs {\n newMetadataUri: string;\n newMilestones: Array<{ percentage: number; description: string }>;\n}\n\n// =============================================================================\n// Event Types\n// =============================================================================\n\nexport interface ProjectCreatedEvent {\n projectId: BN;\n founder: PublicKey;\n fundingGoal: BN;\n metadataUri: string;\n}\n\nexport interface InvestmentMadeEvent {\n projectId: BN;\n investor: PublicKey;\n amount: BN;\n nftMint: PublicKey;\n tier: number;\n voteWeight: BN;\n}\n\nexport interface MilestoneVoteCastEvent {\n projectId: BN;\n milestoneIndex: number;\n voter: PublicKey;\n choice: VoteChoice;\n weight: BN;\n}\n\nexport interface MilestoneVoteFinalizedEvent {\n projectId: BN;\n milestoneIndex: number;\n passed: boolean;\n yesVotes: BN;\n noVotes: BN;\n}\n\n// =============================================================================\n// Utility Types\n// =============================================================================\n\nexport interface InvestmentWithKey {\n publicKey: PublicKey;\n account: InvestmentAccount;\n}\n\nexport interface MilestoneWithKey {\n publicKey: PublicKey;\n account: MilestoneAccount;\n}\n\nexport interface VoteWithKey {\n publicKey: PublicKey;\n account: VoteAccount;\n}\n"]}
1
+ {"version":3,"sources":["../../src/types/index.ts"],"names":["ProjectState","MilestoneState","VoteChoice","PivotState","FundingRoundState"],"mappings":";AAaO,IAAK,YAAA,qBAAAA,aAAAA,KAAL;AACL,EAAAA,cAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,cAAA,iBAAA,CAAA,GAAkB,iBAAA;AAClB,EAAAA,cAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,cAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,cAAA,YAAA,CAAA,GAAa,YAAA;AACb,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AAVF,EAAA,OAAAA,aAAAA;AAAA,CAAA,EAAA,YAAA,IAAA,EAAA;AAaL,IAAK,cAAA,qBAAAC,eAAAA,KAAL;AACL,EAAAA,gBAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,gBAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,gBAAA,YAAA,CAAA,GAAa,YAAA;AACb,EAAAA,gBAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,gBAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,gBAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,gBAAA,UAAA,CAAA,GAAW,UAAA;AAPD,EAAA,OAAAA,eAAAA;AAAA,CAAA,EAAA,cAAA,IAAA,EAAA;AAUL,IAAK,UAAA,qBAAAC,WAAAA,KAAL;AACL,EAAAA,YAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,YAAA,KAAA,CAAA,GAAM,KAAA;AAFI,EAAA,OAAAA,WAAAA;AAAA,CAAA,EAAA,UAAA,IAAA,EAAA;AAKL,IAAK,UAAA,qBAAAC,WAAAA,KAAL;AACL,EAAAA,YAAA,0BAAA,CAAA,GAA2B,0BAAA;AAC3B,EAAAA,YAAA,gCAAA,CAAA,GAAiC,gCAAA;AACjC,EAAAA,YAAA,WAAA,CAAA,GAAY,WAAA;AAHF,EAAA,OAAAA,WAAAA;AAAA,CAAA,EAAA,UAAA,IAAA,EAAA;AAotBL,IAAK,iBAAA,qBAAAC,kBAAAA,KAAL;AACL,EAAAA,mBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,mBAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,mBAAA,YAAA,CAAA,GAAa,YAAA;AACb,EAAAA,mBAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,mBAAA,QAAA,CAAA,GAAS,QAAA;AALC,EAAA,OAAAA,kBAAAA;AAAA,CAAA,EAAA,iBAAA,IAAA,EAAA","file":"index.js","sourcesContent":["/**\n * Raise Type Definitions\n *\n * Re-exports types from the IDL and provides additional utility types.\n */\n\nimport { PublicKey } from '@solana/web3.js';\nimport { BN } from '@coral-xyz/anchor';\n\n// =============================================================================\n// Project State Enums\n// =============================================================================\n\nexport enum ProjectState {\n Draft = 'draft',\n PendingApproval = 'pendingApproval',\n Open = 'open',\n Funded = 'funded',\n InProgress = 'inProgress',\n Completed = 'completed',\n Abandoned = 'abandoned',\n Failed = 'failed',\n TGEFailed = 'tgeFailed',\n Cancelled = 'cancelled',\n}\n\nexport enum MilestoneState {\n Proposed = 'proposed',\n Approved = 'approved',\n InProgress = 'inProgress',\n UnderReview = 'underReview',\n Passed = 'passed',\n Failed = 'failed',\n Unlocked = 'unlocked',\n}\n\nexport enum VoteChoice {\n Good = 'good',\n Bad = 'bad',\n}\n\nexport enum PivotState {\n PendingModeratorApproval = 'pendingModeratorApproval',\n ApprovedAwaitingInvestorWindow = 'approvedAwaitingInvestorWindow',\n Finalized = 'finalized',\n}\n\n// =============================================================================\n// Account Types\n// =============================================================================\n\n/**\n * Tier configuration stored on-chain (includes filled_lots)\n */\nexport interface Tier {\n /** USDC amount per lot */\n amount: BN;\n /** Maximum lots available */\n maxLots: number;\n /** Currently filled lots */\n filledLots: number;\n /** Token allocation per $1 invested */\n tokenRatio: BN;\n /** Vote weight multiplier (basis points, 100 = 1.0x) */\n voteMultiplier: number;\n}\n\n/**\n * Tier configuration input for project initialization\n * Founders pass these when creating a project\n * Note: voteMultiplier is auto-calculated on-chain using logarithmic formula\n */\nexport interface TierConfig {\n /** USDC amount per lot (must be >= 10 USDC = 10_000_000 lamports) */\n amount: BN;\n /** Maximum lots available (must be >= 1) */\n maxLots: number;\n /** Token allocation per $1 invested (must be >= 1) */\n tokenRatio: BN;\n}\n\nexport interface ProjectAccount {\n founder: PublicKey;\n projectId: BN;\n fundingGoal: BN;\n amountRaised: BN;\n state: ProjectState;\n metadataUri: string;\n escrow: PublicKey;\n currentMilestone: number;\n totalMilestones: number;\n /** Number of active tiers (1-10) */\n tierCount: number;\n /** All tier slots (only first tierCount are active) */\n tiers: Tier[];\n tokenMint: PublicKey | null;\n tgeDate: BN | null;\n tokensDeposited: BN;\n tokenAllocationBps: number;\n totalTokenAllocation: BN;\n consecutiveFailures: number;\n investorCount: number;\n /** Milestone Price Increases: BPS multipliers per milestone (10000 = 1.0x)\n * NOTE: If priceMultipliers[0] === 0, project uses dynamic mode (ZEMYTH formula)\n * Otherwise uses static mode (legacy pre-configured multipliers) */\n priceMultipliers: number[];\n /** Milestone Price Increases: Number of milestones that have passed */\n milestonesPassed: number;\n /** Dynamic Price Multiplier: Current calculated multiplier in BPS (10000 = 1.0x)\n * Used when priceMultipliers[0] === 0 (dynamic mode) */\n currentPriceMultiplierBps: number;\n // === Linear Progressive Pricing Fields (add-linear-progressive-pricing) ===\n /** Base multiplier at start of current milestone period (BPS) */\n milestoneBaseMultiplierBps: number;\n /** Timestamp when current milestone period started (0 = legacy mode) */\n milestonePeriodStartedAt: BN;\n /** Expected deadline for current milestone period */\n expectedMilestoneDeadlineTs: BN;\n /** Time-based allocation for current period (50% of ZEMYTH increment) */\n timeAllocationBps: number;\n /** Milestone-based allocation (50% of ZEMYTH increment) */\n milestoneAllocationBps: number;\n bump: number;\n}\n\nexport interface MilestoneAccount {\n project: PublicKey;\n milestoneIndex: number;\n percentage: number;\n description: string;\n state: MilestoneState;\n yesVotes: BN;\n noVotes: BN;\n totalWeight: BN;\n voterCount: number;\n votingEndsAt: BN | null;\n bump: number;\n}\n\nexport interface InvestmentAccount {\n project: PublicKey;\n investor: PublicKey;\n nftMint: PublicKey;\n amount: BN;\n voteWeight: BN;\n tokenAllocation: BN;\n tier: number;\n investedAt: BN;\n tokensClaimed: boolean;\n withdrawnFromPivot: boolean;\n refundClaimed: boolean;\n /** Early Token Release: Whether 5% early tokens have been claimed */\n earlyTokensClaimed: boolean;\n /** Early Token Release: Amount of early tokens claimed (for burn-for-refund tracking) */\n earlyTokensAmount: BN;\n /** Preserve Pivot Vesting Rights: Milestone index at which vesting was frozen (on refund) */\n vestingFrozenAtMilestone: number | null;\n bump: number;\n}\n\nexport interface VoteAccount {\n milestone: PublicKey;\n voter: PublicKey;\n choice: VoteChoice;\n weight: BN;\n votedAt: BN;\n bump: number;\n}\n\nexport interface AdminConfigAccount {\n admin: PublicKey;\n pendingAdmin: PublicKey | null;\n /** Zemyth treasury wallet for platform token allocations */\n zemythTreasury: PublicKey | null;\n bump: number;\n}\n\nexport interface PivotProposalAccount {\n project: PublicKey;\n newMetadataUri: string;\n newMilestones: Array<{ percentage: number; description: string }>;\n state: PivotState;\n proposedAt: BN;\n approvedAt: BN | null;\n withdrawalWindowEndsAt: BN | null;\n withdrawnAmount: BN;\n withdrawnCount: number;\n bump: number;\n}\n\nexport interface TgeEscrowAccount {\n project: PublicKey;\n holdbackAmount: BN;\n scamReports: BN;\n scamWeight: BN;\n scamConfirmed: boolean;\n holdbackReleased: boolean;\n bump: number;\n}\n\n// =============================================================================\n// Instruction Arguments\n// =============================================================================\n\nexport interface InitializeProjectArgs {\n projectId: BN;\n fundingGoal: BN;\n metadataUri: string;\n /** Founder-configured tiers (1-10 tiers) */\n tiers: TierConfig[];\n}\n\nexport interface CreateMilestoneArgs {\n milestoneIndex: number;\n percentage: number;\n description: string;\n}\n\nexport interface InvestArgs {\n amount: BN;\n}\n\nexport interface VoteOnMilestoneArgs {\n choice: VoteChoice;\n}\n\nexport interface SetTgeDateArgs {\n tgeDate: BN;\n tokenMint: PublicKey;\n}\n\nexport interface DepositTokensArgs {\n amount: BN;\n}\n\nexport interface ProposePivotArgs {\n newMetadataUri: string;\n newMilestones: Array<{ percentage: number; description: string }>;\n}\n\n// =============================================================================\n// Event Types\n// =============================================================================\n\nexport interface ProjectCreatedEvent {\n projectId: BN;\n founder: PublicKey;\n fundingGoal: BN;\n metadataUri: string;\n}\n\nexport interface InvestmentMadeEvent {\n projectId: BN;\n investor: PublicKey;\n amount: BN;\n nftMint: PublicKey;\n tier: number;\n voteWeight: BN;\n /** Milestone Price Increases: Effective tier price with multiplier applied */\n effectivePrice: BN;\n /** Milestone Price Increases: Current price multiplier in BPS (10000 = 1.0x) */\n multiplierBps: number;\n}\n\nexport interface MilestoneVoteCastEvent {\n projectId: BN;\n milestoneIndex: number;\n voter: PublicKey;\n choice: VoteChoice;\n weight: BN;\n}\n\nexport interface MilestoneVoteFinalizedEvent {\n projectId: BN;\n milestoneIndex: number;\n passed: boolean;\n yesVotes: BN;\n noVotes: BN;\n}\n\n// =============================================================================\n// Utility Types\n// =============================================================================\n\nexport interface InvestmentWithKey {\n publicKey: PublicKey;\n account: InvestmentAccount;\n}\n\nexport interface MilestoneWithKey {\n publicKey: PublicKey;\n account: MilestoneAccount;\n}\n\nexport interface VoteWithKey {\n publicKey: PublicKey;\n account: VoteAccount;\n}\n\n// =============================================================================\n// Dynamic Tokenomics Types\n// =============================================================================\n\n/**\n * Sub-allocation within a project's founder allocation pool\n * Sub-allocations are founder team tokens (advisors, marketing, etc.)\n */\nexport interface SubAllocation {\n /** Unique ID within project (0-9, max 10 sub-allocations) */\n id: number;\n /** Name of the allocation (UTF-8, e.g., \"advisors\", \"marketing\") */\n name: string;\n /** Basis points from founder allocation pool (e.g., 500 = 5% of total supply) */\n bps: number;\n /** Token destination wallet */\n recipient: PublicKey;\n /** Vesting duration in months (0 = immediate, >0 = linear vesting) */\n vestingMonths: number;\n /** Cliff period in months before vesting starts */\n cliffMonths: number;\n /** Timestamp when sub-allocation was created */\n createdAt: BN;\n /** True if this was added via governance vote (post-Open state) */\n approvedViaGovernance: boolean;\n}\n\n/**\n * Allocation proposal for governance voting\n */\nexport interface AllocationProposalAccount {\n /** Parent project */\n project: PublicKey;\n /** Proposer (must be founder) */\n proposer: PublicKey;\n /** The proposed sub-allocation */\n subAllocation: SubAllocation;\n /** Total vote weight in favor */\n votesFor: BN;\n /** Total vote weight against */\n votesAgainst: BN;\n /** Timestamp when voting ends (7 days from creation) */\n votingEndsAt: BN;\n /** Whether the proposal has been executed */\n executed: boolean;\n /** Proposal index (for unique PDA derivation) */\n proposalIndex: number;\n /** PDA bump seed */\n bump: number;\n}\n\n/**\n * Vote record for an allocation proposal\n */\nexport interface AllocationVoteAccount {\n /** The proposal being voted on */\n proposal: PublicKey;\n /** The investment NFT used to vote */\n nftMint: PublicKey;\n /** Voter's wallet */\n voter: PublicKey;\n /** Vote weight */\n voteWeight: BN;\n /** True = for, False = against */\n voteFor: boolean;\n /** Timestamp of vote */\n votedAt: BN;\n /** PDA bump seed */\n bump: number;\n}\n\n/**\n * Extended Tokenomics account with dynamic allocation fields\n */\nexport interface TokenomicsAccount {\n /** Parent project */\n project: PublicKey;\n /** Token symbol (2-8 chars uppercase) */\n tokenSymbol: string;\n /** Token decimals (default: 9) */\n tokenDecimals: number;\n /** Total token supply */\n totalSupply: BN;\n /** Investor allocation in basis points */\n investorAllocationBps: number;\n /** LP token allocation in basis points */\n lpTokenAllocationBps: number;\n /** LP USDC allocation in basis points */\n lpUsdcAllocationBps: number;\n /** Founder allocation in basis points (includes sub-allocations for team) */\n founderAllocationBps: number;\n /** Zemyth platform allocation in basis points (minimum 1%, vests per milestone) */\n zemythAllocationBps: number;\n /** Number of active sub-allocations (draw from founder allocation) */\n subAllocationCount: number;\n /** Sub-allocations from founder pool (advisors, marketing, etc.) */\n subAllocations: SubAllocation[];\n /** Number of allocation proposals created */\n proposalCount: number;\n /** Founder wallet for vesting */\n founderWallet: PublicKey | null;\n /** Vesting duration in months */\n vestingDurationMonths: number;\n /** Cliff period in months */\n cliffMonths: number;\n /** Early Token Release: Founder milestone-based vesting BPS (e.g., 4750 = 47.5% of founder allocation) */\n founderMilestoneVestingBps: number;\n /** Early Token Release: Founder time-based vesting BPS (e.g., 4750 = 47.5% of founder allocation) */\n founderTimeVestingBps: number;\n /** Future round allocation in basis points (0 or >= 1000, reserved for second-round fundraising) */\n futureRoundAllocationBps: number;\n /** PDA bump seed */\n bump: number;\n}\n\n// =============================================================================\n// Dynamic Tokenomics Instruction Arguments\n// =============================================================================\n\nexport interface AddSubAllocationArgs {\n /** Name of the sub-allocation (e.g., \"advisors\", \"marketing\") */\n name: string;\n /** Basis points from founder allocation pool */\n bps: number;\n /** Token destination wallet */\n recipient: PublicKey;\n /** Vesting duration in months (0 = immediate) */\n vestingMonths: number;\n /** Cliff period in months */\n cliffMonths: number;\n}\n\nexport interface ProposeAllocationChangeArgs {\n /** Name of the sub-allocation */\n name: string;\n /** Basis points from founder allocation pool */\n bps: number;\n /** Token destination wallet */\n recipient: PublicKey;\n /** Vesting duration in months */\n vestingMonths: number;\n /** Cliff period in months */\n cliffMonths: number;\n}\n\nexport interface VoteAllocationChangeArgs {\n /** True = vote for, False = vote against */\n voteFor: boolean;\n}\n\n// =============================================================================\n// Dynamic Tokenomics Events\n// =============================================================================\n\nexport interface SubAllocationAddedEvent {\n projectId: BN;\n projectKey: PublicKey;\n subAllocationId: number;\n name: string;\n bps: number;\n recipient: PublicKey;\n vestingMonths: number;\n cliffMonths: number;\n timestamp: BN;\n}\n\nexport interface AllocationProposalCreatedEvent {\n projectId: BN;\n projectKey: PublicKey;\n proposalKey: PublicKey;\n name: string;\n bps: number;\n recipient: PublicKey;\n votingEndsAt: BN;\n timestamp: BN;\n}\n\nexport interface AllocationVoteCastEvent {\n projectId: BN;\n projectKey: PublicKey;\n proposalKey: PublicKey;\n voter: PublicKey;\n nftMint: PublicKey;\n voteWeight: BN;\n voteFor: boolean;\n timestamp: BN;\n}\n\nexport interface AllocationChangeExecutedEvent {\n projectId: BN;\n projectKey: PublicKey;\n proposalKey: PublicKey;\n subAllocationId: number;\n votesFor: BN;\n votesAgainst: BN;\n timestamp: BN;\n}\n\n// =============================================================================\n// Dynamic Tokenomics Utility Types\n// =============================================================================\n\nexport interface AllocationProposalWithKey {\n publicKey: PublicKey;\n account: AllocationProposalAccount;\n}\n\nexport interface AllocationVoteWithKey {\n publicKey: PublicKey;\n account: AllocationVoteAccount;\n}\n\n// =============================================================================\n// Sub-Allocation Vesting Types\n// =============================================================================\n\n/**\n * Sub-allocation vesting account for tracking vesting schedules\n * Similar to FounderVesting but for sub-allocations (treasury, advisors, etc.)\n */\nexport interface SubAllocationVestingAccount {\n /** Parent project */\n project: PublicKey;\n /** Sub-allocation ID (0-9) */\n subAllocationId: number;\n /** Recipient wallet (only this wallet can claim) */\n recipientWallet: PublicKey;\n /** Total token amount for this sub-allocation */\n totalAmount: BN;\n /** Timestamp when vesting starts (MAE completion) */\n startTimestamp: BN;\n /** Timestamp when cliff ends */\n cliffEnd: BN;\n /** Timestamp when vesting fully ends */\n vestingEnd: BN;\n /** Amount already claimed */\n claimedAmount: BN;\n /** Pending wallet update (for wallet migration) */\n pendingWallet: PublicKey | null;\n /** Timestamp when wallet update was initiated */\n walletUpdateTimestamp: BN | null;\n /** PDA bump seed */\n bump: number;\n}\n\nexport interface SubAllocationVestingWithKey {\n publicKey: PublicKey;\n account: SubAllocationVestingAccount;\n}\n\n/**\n * Arguments for claiming sub-allocation tokens\n */\nexport interface ClaimSubAllocationTokensArgs {\n /** Sub-allocation ID to claim from (0-9) */\n subAllocationId: number;\n}\n\n// =============================================================================\n// Sub-Allocation Vesting Events\n// =============================================================================\n\nexport interface SubAllocationVestingInitializedEvent {\n projectId: BN;\n projectKey: PublicKey;\n subAllocationId: number;\n recipientWallet: PublicKey;\n totalAmount: BN;\n cliffMonths: number;\n vestingMonths: number;\n startTimestamp: BN;\n timestamp: BN;\n}\n\nexport interface SubAllocationTokensClaimedEvent {\n projectId: BN;\n projectKey: PublicKey;\n subAllocationId: number;\n recipient: PublicKey;\n amountClaimed: BN;\n totalClaimed: BN;\n remaining: BN;\n vestingProgressPercent: number;\n timestamp: BN;\n}\n\n// =============================================================================\n// Early Token Release Types\n// =============================================================================\n\n/**\n * Arguments for claim_investor_tokens instruction\n */\nexport interface ClaimInvestorTokensArgs {\n /** Milestone index to claim tokens from */\n milestoneIndex: number;\n}\n\n/**\n * Arguments for claim_founder_milestone_tokens instruction\n */\nexport interface ClaimFounderMilestoneTokensArgs {\n /** Milestone index to claim tokens from */\n milestoneIndex: number;\n}\n\n// =============================================================================\n// Early Token Release Events\n// =============================================================================\n\nexport interface EarlyTokensClaimedEvent {\n projectId: BN;\n projectKey: PublicKey;\n investor: PublicKey;\n nftMint: PublicKey;\n amount: BN;\n timestamp: BN;\n}\n\nexport interface FounderEarlyTokensClaimedEvent {\n projectId: BN;\n projectKey: PublicKey;\n founder: PublicKey;\n amount: BN;\n timestamp: BN;\n}\n\nexport interface FounderMilestoneTokensClaimedEvent {\n projectId: BN;\n projectKey: PublicKey;\n founder: PublicKey;\n milestoneIndex: number;\n amount: BN;\n cumulativeClaimed: BN;\n timestamp: BN;\n}\n\nexport interface TokensBurnedForRefundEvent {\n projectId: BN;\n projectKey: PublicKey;\n investor: PublicKey;\n nftMint: PublicKey;\n amountBurned: BN;\n timestamp: BN;\n}\n\n// =============================================================================\n// Milestone Price Increase Events\n// =============================================================================\n\nexport interface PriceMultiplierActivatedEvent {\n projectId: BN;\n projectKey: PublicKey;\n milestoneIndex: number;\n newMultiplierBps: number;\n timestamp: BN;\n}\n\n// =============================================================================\n// Dynamic Price Multiplier Events (refactor-dynamic-price-multiplier)\n// =============================================================================\n\n/** Emitted when claim_milestone_funds dynamically calculates and updates the price multiplier */\nexport interface PriceMultiplierUpdatedEvent {\n projectId: BN;\n projectKey: PublicKey;\n /** Index of the milestone just claimed */\n milestoneIndex: number;\n /** Number of milestones remaining after this claim */\n remainingMilestones: number;\n /** Days until the next milestone deadline */\n daysToDeadline: BN;\n /** New price multiplier in BPS (10000 = 1.0x) */\n newMultiplierBps: number;\n /** Previous price multiplier in BPS */\n previousMultiplierBps: number;\n timestamp: BN;\n}\n\n// =============================================================================\n// Zemyth Platform Allocation Types (refactor-sub-allocation-to-founder)\n// =============================================================================\n\n/**\n * Zemyth vesting account for platform token allocation\n * Created at project approval, tokens vest proportionally per milestone\n */\nexport interface ZemythVestingAccount {\n /** Parent project */\n project: PublicKey;\n /** Total Zemyth token allocation */\n totalTokens: BN;\n /** Tokens already claimed by Zemyth treasury */\n claimedTokens: BN;\n /** Number of milestones claimed for (0 to milestone_count) */\n milestonesClaimed: number;\n /** Destination treasury wallet for claims */\n treasuryWallet: PublicKey;\n /** Account creation timestamp */\n createdAt: BN;\n /** PDA bump seed */\n bump: number;\n}\n\nexport interface ZemythVestingWithKey {\n publicKey: PublicKey;\n account: ZemythVestingAccount;\n}\n\n// =============================================================================\n// Zemyth Platform Allocation Events (refactor-sub-allocation-to-founder)\n// =============================================================================\n\n/** Emitted when ZemythVesting account is created at project approval */\nexport interface ZemythVestingCreatedEvent {\n projectId: BN;\n projectKey: PublicKey;\n totalTokens: BN;\n treasuryWallet: PublicKey;\n timestamp: BN;\n}\n\n/** Emitted when Zemyth claims tokens for passed milestones */\nexport interface ZemythTokensClaimedEvent {\n projectId: BN;\n projectKey: PublicKey;\n amountClaimed: BN;\n totalClaimed: BN;\n milestonesClaimed: number;\n treasuryWallet: PublicKey;\n timestamp: BN;\n}\n\n// =============================================================================\n// Future Round Allocation Types (add-future-round-allocation)\n// =============================================================================\n\n/**\n * FutureRoundVault account for tracking reserved tokens for future fundraising\n * Created at project approval if future_round_allocation_bps > 0\n */\nexport interface FutureRoundVaultAccount {\n /** Parent project */\n project: PublicKey;\n /** Token mint for the project */\n tokenMint: PublicKey;\n /** Total tokens reserved for future rounds */\n totalAmount: BN;\n /** Tokens already used in subsequent rounds */\n usedAmount: BN;\n /** PDA bump seed */\n bump: number;\n}\n\nexport interface FutureRoundVaultWithKey {\n publicKey: PublicKey;\n account: FutureRoundVaultAccount;\n}\n\n// =============================================================================\n// Multi-Round Fundraising Types (add-second-round-fundraising)\n// =============================================================================\n\n/**\n * FundingRound state enum\n */\nexport enum FundingRoundState {\n Open = 'open',\n Funded = 'funded',\n InProgress = 'inProgress',\n Completed = 'completed',\n Failed = 'failed',\n}\n\n/**\n * Milestone configuration input for funding rounds\n */\nexport interface RoundMilestoneConfig {\n /** Percentage of round funds for this milestone */\n percentage: number;\n /** Milestone description */\n description: string;\n /** Instant release percentage in basis points (e.g., 1000 = 10%) */\n instantReleaseBps: number;\n /** Cliff period in months */\n cliffMonths: number;\n /** Vesting duration in months */\n vestingDurationMonths: number;\n}\n\n/**\n * FundingRound account for R2, R3, R4... rounds\n */\nexport interface FundingRoundAccount {\n /** Parent project */\n project: PublicKey;\n /** Round number (2, 3, 4...) */\n roundNumber: number;\n /** Funding goal for this round */\n fundingGoal: BN;\n /** Amount raised in this round */\n amountRaised: BN;\n /** Round state */\n state: FundingRoundState;\n /** Number of investors in this round */\n investorCount: number;\n /** Allocation from future round pool (basis points) */\n roundAllocationBps: number;\n /** Number of tiers */\n tierCount: number;\n /** Tier configurations */\n tiers: Tier[];\n /** Number of milestones */\n milestoneCount: number;\n /** Current milestone index */\n currentMilestone: number;\n /** Consecutive milestone failures */\n consecutiveFailures: number;\n /** Round creation timestamp */\n createdAt: BN;\n /** PDA bump seed */\n bump: number;\n}\n\nexport interface FundingRoundWithKey {\n publicKey: PublicKey;\n account: FundingRoundAccount;\n}\n\n// =============================================================================\n// Multi-Round Fundraising Instruction Arguments\n// =============================================================================\n\n/**\n * Arguments for open_funding_round instruction\n */\nexport interface OpenFundingRoundArgs {\n /** Allocation from future round pool in basis points */\n roundAllocationBps: number;\n /** Funding goal for this round */\n fundingGoal: BN;\n /** Tier configurations (token_ratio must be <= R1's best tier) */\n tiers: TierConfig[];\n /** Milestone configurations for this round */\n milestones: RoundMilestoneConfig[];\n}\n\n/**\n * Arguments for invest_in_round instruction\n */\nexport interface InvestInRoundArgs {\n /** Round number to invest in */\n roundNumber: number;\n /** Investment amount in USDC lamports */\n amount: BN;\n}\n\n/**\n * Arguments for vote_on_round_milestone instruction\n */\nexport interface VoteOnRoundMilestoneArgs {\n /** Round number */\n roundNumber: number;\n /** Milestone index */\n milestoneIndex: number;\n /** Vote choice */\n choice: VoteChoice;\n}\n\n/**\n * Arguments for claim_round_instant_tokens instruction\n */\nexport interface ClaimRoundInstantTokensArgs {\n /** Milestone index */\n milestoneIndex: number;\n}\n\n/**\n * Arguments for claim_round_vested_tokens instruction\n */\nexport interface ClaimRoundVestedTokensArgs {\n /** Milestone index */\n milestoneIndex: number;\n}\n\n// =============================================================================\n// Multi-Round Fundraising Events\n// =============================================================================\n\nexport interface FundingRoundOpenedEvent {\n projectId: BN;\n projectKey: PublicKey;\n roundNumber: number;\n fundingGoal: BN;\n roundAllocationBps: number;\n tierCount: number;\n milestoneCount: number;\n timestamp: BN;\n}\n\nexport interface RoundInvestmentMadeEvent {\n projectId: BN;\n projectKey: PublicKey;\n roundNumber: number;\n investor: PublicKey;\n amount: BN;\n nftMint: PublicKey;\n tier: number;\n tokensAllocated: BN;\n timestamp: BN;\n}\n\nexport interface RoundMilestoneVoteEvent {\n projectId: BN;\n projectKey: PublicKey;\n roundNumber: number;\n milestoneIndex: number;\n voter: PublicKey;\n choice: VoteChoice;\n weight: BN;\n timestamp: BN;\n}\n\nexport interface RoundMilestoneFinalizedEvent {\n projectId: BN;\n projectKey: PublicKey;\n roundNumber: number;\n milestoneIndex: number;\n passed: boolean;\n yesVotes: BN;\n noVotes: BN;\n timestamp: BN;\n}\n\nexport interface RoundEarlyTokensClaimedEvent {\n projectId: BN;\n roundNumber: number;\n investmentKey: PublicKey;\n nftHolder: PublicKey;\n amount: BN;\n timestamp: BN;\n}\n\nexport interface RoundInstantTokensClaimedEvent {\n projectId: BN;\n roundNumber: number;\n milestoneIndex: number;\n investmentKey: PublicKey;\n nftHolder: PublicKey;\n tokensClaimed: BN;\n timestamp: BN;\n}\n\nexport interface RoundVestedTokensClaimedEvent {\n projectId: BN;\n roundNumber: number;\n milestoneIndex: number;\n investmentKey: PublicKey;\n nftHolder: PublicKey;\n tokensClaimed: BN;\n cumulativeClaimed: BN;\n vestingProgressPercent: number;\n timestamp: BN;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zemyth/raise-sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "TypeScript SDK for the Raise Solana program - decentralized venture funding platform",
5
5
  "author": "Raise Team",
6
6
  "license": "MIT",
@@ -102,6 +102,7 @@
102
102
  },
103
103
  "devDependencies": {
104
104
  "@coral-xyz/anchor": "^0.32.1",
105
+ "@solana/spl-token": "^0.4.14",
105
106
  "@solana/web3.js": "^1.98.4",
106
107
  "@types/node": "^20.0.0",
107
108
  "tsup": "^8.0.0",
@@ -119,11 +120,12 @@
119
120
  ],
120
121
  "repository": {
121
122
  "type": "git",
122
- "url": "https://github.com/zemyth/raise"
123
+ "url": "https://github.com/zemyth-app/raise"
123
124
  },
124
125
  "bugs": {
125
- "url": "https://github.com/zemyth/raise/issues"
126
+ "url": "https://github.com/zemyth-app/raise/issues"
126
127
  },
128
+ "homepage": "https://github.com/zemyth-app/raise#readme",
127
129
  "engines": {
128
130
  "node": ">=18"
129
131
  }
@@ -0,0 +1,358 @@
1
+ /**
2
+ * SDK Tests for Dynamic Tokenomics
3
+ *
4
+ * Tests per tasks.md Section 8.2:
5
+ * - 8.2.1: SubAllocation type serialization
6
+ * - 8.2.2: PDA derivation for proposals and vesting
7
+ * - 8.2.3: Client methods integration (unit tests)
8
+ */
9
+
10
+ import { describe, it, expect } from 'vitest';
11
+ import { PublicKey, Keypair } from '@solana/web3.js';
12
+ import { BN } from '@coral-xyz/anchor';
13
+ import {
14
+ getAllocationProposalPDA,
15
+ getAllocationVotePDA,
16
+ getSubAllocationVestingPDA,
17
+ getProjectPDA,
18
+ getTokenomicsPDA,
19
+ } from '../pdas/index.js';
20
+
21
+ // Mock program ID for testing
22
+ const MOCK_PROGRAM_ID = new PublicKey('Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS');
23
+
24
+ describe('8.2.1: SubAllocation Type Serialization', () => {
25
+ it('should correctly represent SubAllocation structure', () => {
26
+ // SubAllocation fields as defined in types
27
+ const subAllocation = {
28
+ id: 0,
29
+ name: Array(32).fill(0),
30
+ bps: 500,
31
+ recipient: Keypair.generate().publicKey,
32
+ vestingMonths: 12,
33
+ cliffMonths: 3,
34
+ createdAt: new BN(Date.now() / 1000),
35
+ approvedViaGovernance: false,
36
+ };
37
+
38
+ // Verify structure
39
+ expect(subAllocation.id).toBe(0);
40
+ expect(subAllocation.name).toHaveLength(32);
41
+ expect(subAllocation.bps).toBe(500);
42
+ expect(subAllocation.vestingMonths).toBe(12);
43
+ expect(subAllocation.cliffMonths).toBe(3);
44
+ expect(subAllocation.approvedViaGovernance).toBe(false);
45
+ });
46
+
47
+ it('should encode name to 32-byte array correctly', () => {
48
+ const name = 'Marketing';
49
+ const nameBytes = new Array(32).fill(0);
50
+ const nameLen = Math.min(name.length, 32);
51
+ for (let i = 0; i < nameLen; i++) {
52
+ nameBytes[i] = name.charCodeAt(i);
53
+ }
54
+
55
+ expect(nameBytes[0]).toBe('M'.charCodeAt(0)); // 77
56
+ expect(nameBytes[8]).toBe('g'.charCodeAt(0)); // 103
57
+ expect(nameBytes[9]).toBe(0); // null terminator padding
58
+ });
59
+
60
+ it('should validate bps is within valid range (0-10000)', () => {
61
+ const validBps = [0, 100, 500, 1000, 5000, 10000];
62
+ const invalidBps = [-1, 10001, 100000];
63
+
64
+ validBps.forEach((bps) => {
65
+ expect(bps >= 0 && bps <= 10000).toBe(true);
66
+ });
67
+
68
+ invalidBps.forEach((bps) => {
69
+ expect(bps >= 0 && bps <= 10000).toBe(false);
70
+ });
71
+ });
72
+
73
+ it('should validate cliff must be less than vesting', () => {
74
+ // Valid configurations
75
+ expect(3 < 12).toBe(true); // cliff 3, vesting 12
76
+ expect(0 < 24).toBe(true); // cliff 0, vesting 24
77
+
78
+ // Invalid configuration
79
+ expect(12 < 6).toBe(false); // cliff 12, vesting 6 - invalid
80
+
81
+ // Edge case: vesting_months = 0 means cliff must also be 0
82
+ expect(0 === 0).toBe(true);
83
+ });
84
+ });
85
+
86
+ describe('8.2.2: PDA Derivation for Proposals and Vesting', () => {
87
+ it('should derive consistent AllocationProposal PDA', () => {
88
+ const projectPda = Keypair.generate().publicKey;
89
+ const proposalIndex = 0;
90
+
91
+ const pda1 = getAllocationProposalPDA(projectPda, proposalIndex, MOCK_PROGRAM_ID);
92
+ const pda2 = getAllocationProposalPDA(projectPda, proposalIndex, MOCK_PROGRAM_ID);
93
+
94
+ // Same inputs should always produce same PDA
95
+ expect(pda1.toBase58()).toBe(pda2.toBase58());
96
+ });
97
+
98
+ it('should derive different PDAs for different proposal indices', () => {
99
+ const projectPda = Keypair.generate().publicKey;
100
+
101
+ const pda0 = getAllocationProposalPDA(projectPda, 0, MOCK_PROGRAM_ID);
102
+ const pda1 = getAllocationProposalPDA(projectPda, 1, MOCK_PROGRAM_ID);
103
+ const pda2 = getAllocationProposalPDA(projectPda, 2, MOCK_PROGRAM_ID);
104
+
105
+ // Different indices should produce different PDAs
106
+ expect(pda0.toBase58()).not.toBe(pda1.toBase58());
107
+ expect(pda1.toBase58()).not.toBe(pda2.toBase58());
108
+ expect(pda0.toBase58()).not.toBe(pda2.toBase58());
109
+ });
110
+
111
+ it('should derive consistent AllocationVote PDA', () => {
112
+ const proposalPda = Keypair.generate().publicKey;
113
+ const nftMint = Keypair.generate().publicKey;
114
+
115
+ const pda1 = getAllocationVotePDA(proposalPda, nftMint, MOCK_PROGRAM_ID);
116
+ const pda2 = getAllocationVotePDA(proposalPda, nftMint, MOCK_PROGRAM_ID);
117
+
118
+ expect(pda1.toBase58()).toBe(pda2.toBase58());
119
+ });
120
+
121
+ it('should derive different vote PDAs for different NFT mints', () => {
122
+ const proposalPda = Keypair.generate().publicKey;
123
+ const nftMint1 = Keypair.generate().publicKey;
124
+ const nftMint2 = Keypair.generate().publicKey;
125
+
126
+ const pda1 = getAllocationVotePDA(proposalPda, nftMint1, MOCK_PROGRAM_ID);
127
+ const pda2 = getAllocationVotePDA(proposalPda, nftMint2, MOCK_PROGRAM_ID);
128
+
129
+ expect(pda1.toBase58()).not.toBe(pda2.toBase58());
130
+ });
131
+
132
+ it('should derive consistent SubAllocationVesting PDA', () => {
133
+ const projectPda = Keypair.generate().publicKey;
134
+ const subAllocationId = 0;
135
+
136
+ const pda1 = getSubAllocationVestingPDA(projectPda, subAllocationId, MOCK_PROGRAM_ID);
137
+ const pda2 = getSubAllocationVestingPDA(projectPda, subAllocationId, MOCK_PROGRAM_ID);
138
+
139
+ expect(pda1.toBase58()).toBe(pda2.toBase58());
140
+ });
141
+
142
+ it('should derive different vesting PDAs for different sub-allocation IDs', () => {
143
+ const projectPda = Keypair.generate().publicKey;
144
+
145
+ const pda0 = getSubAllocationVestingPDA(projectPda, 0, MOCK_PROGRAM_ID);
146
+ const pda1 = getSubAllocationVestingPDA(projectPda, 1, MOCK_PROGRAM_ID);
147
+ const pda5 = getSubAllocationVestingPDA(projectPda, 5, MOCK_PROGRAM_ID);
148
+
149
+ expect(pda0.toBase58()).not.toBe(pda1.toBase58());
150
+ expect(pda1.toBase58()).not.toBe(pda5.toBase58());
151
+ });
152
+
153
+ it('should derive PDAs that are valid PublicKeys', () => {
154
+ const projectPda = Keypair.generate().publicKey;
155
+ const proposalPda = Keypair.generate().publicKey;
156
+ const nftMint = Keypair.generate().publicKey;
157
+
158
+ const allocationProposalPda = getAllocationProposalPDA(projectPda, 0, MOCK_PROGRAM_ID);
159
+ const allocationVotePda = getAllocationVotePDA(proposalPda, nftMint, MOCK_PROGRAM_ID);
160
+ const subAllocationVestingPda = getSubAllocationVestingPDA(projectPda, 0, MOCK_PROGRAM_ID);
161
+
162
+ // All PDAs should be valid PublicKeys (not on the Ed25519 curve)
163
+ expect(PublicKey.isOnCurve(allocationProposalPda.toBuffer())).toBe(false);
164
+ expect(PublicKey.isOnCurve(allocationVotePda.toBuffer())).toBe(false);
165
+ expect(PublicKey.isOnCurve(subAllocationVestingPda.toBuffer())).toBe(false);
166
+ });
167
+
168
+ it('should derive tokenomics PDA correctly', () => {
169
+ const projectId = new BN(1);
170
+ const projectPda = getProjectPDA(projectId, MOCK_PROGRAM_ID);
171
+ const tokenomicsPda = getTokenomicsPDA(projectPda, MOCK_PROGRAM_ID);
172
+
173
+ // Should be deterministic
174
+ const tokenomicsPda2 = getTokenomicsPDA(projectPda, MOCK_PROGRAM_ID);
175
+ expect(tokenomicsPda.toBase58()).toBe(tokenomicsPda2.toBase58());
176
+
177
+ // Should be valid PDA (off curve)
178
+ expect(PublicKey.isOnCurve(tokenomicsPda.toBuffer())).toBe(false);
179
+ });
180
+ });
181
+
182
+ describe('8.2.3: Client Methods Integration', () => {
183
+ describe('getRemainingFounderPool calculation', () => {
184
+ it('should calculate remaining founder pool correctly', () => {
185
+ // Simulate tokenomics state
186
+ // Sub-allocations (treasury, advisors, marketing) draw from founder's allocation pool
187
+ const tokenomics = {
188
+ founderAllocationBps: 2000, // 20%
189
+ subAllocations: [
190
+ { bps: 500 }, // 5%
191
+ { bps: 300 }, // 3%
192
+ ],
193
+ subAllocationCount: 2,
194
+ };
195
+
196
+ // Calculate remaining founder pool
197
+ let allocatedBps = 0;
198
+ for (let i = 0; i < tokenomics.subAllocationCount; i++) {
199
+ allocatedBps += tokenomics.subAllocations[i].bps;
200
+ }
201
+ const remainingFounderPool = tokenomics.founderAllocationBps - allocatedBps;
202
+
203
+ expect(remainingFounderPool).toBe(1200); // 20% - 5% - 3% = 12%
204
+ });
205
+
206
+ it('should return 0 when founder pool is fully allocated', () => {
207
+ const tokenomics = {
208
+ founderAllocationBps: 1000, // 10%
209
+ subAllocations: [
210
+ { bps: 500 }, // 5%
211
+ { bps: 500 }, // 5%
212
+ ],
213
+ subAllocationCount: 2,
214
+ };
215
+
216
+ let allocatedBps = 0;
217
+ for (let i = 0; i < tokenomics.subAllocationCount; i++) {
218
+ allocatedBps += tokenomics.subAllocations[i].bps;
219
+ }
220
+ const remainingFounderPool = tokenomics.founderAllocationBps - allocatedBps;
221
+
222
+ expect(remainingFounderPool).toBe(0);
223
+ });
224
+ });
225
+
226
+ describe('proposal status calculation', () => {
227
+ it('should identify active proposals', () => {
228
+ const now = Math.floor(Date.now() / 1000);
229
+ const proposal = {
230
+ votingEndsAt: new BN(now + 3600), // 1 hour from now
231
+ executed: false,
232
+ };
233
+
234
+ const isActive = !proposal.executed && proposal.votingEndsAt.toNumber() > now;
235
+ expect(isActive).toBe(true);
236
+ });
237
+
238
+ it('should identify expired proposals pending execution', () => {
239
+ const now = Math.floor(Date.now() / 1000);
240
+ const proposal = {
241
+ votingEndsAt: new BN(now - 3600), // 1 hour ago
242
+ executed: false,
243
+ votesFor: new BN(6000),
244
+ votesAgainst: new BN(4000),
245
+ };
246
+
247
+ const isExpired = proposal.votingEndsAt.toNumber() <= now;
248
+ const isPendingExecution = isExpired && !proposal.executed;
249
+
250
+ // Check if passed (>51%)
251
+ const totalVotes = proposal.votesFor.add(proposal.votesAgainst);
252
+ const hasPassed = totalVotes.gt(new BN(0)) &&
253
+ proposal.votesFor.mul(new BN(100)).div(totalVotes).gte(new BN(51));
254
+
255
+ expect(isPendingExecution).toBe(true);
256
+ expect(hasPassed).toBe(true);
257
+ });
258
+
259
+ it('should identify executed proposals', () => {
260
+ const proposal = {
261
+ executed: true,
262
+ };
263
+
264
+ expect(proposal.executed).toBe(true);
265
+ });
266
+ });
267
+
268
+ describe('vesting calculation', () => {
269
+ it('should calculate immediate release correctly (vesting_months=0)', () => {
270
+ const vestingMonths = 0;
271
+ const cliffMonths = 0;
272
+ const totalAmount = new BN(1_000_000);
273
+
274
+ // Immediate release = full amount
275
+ const claimable = vestingMonths === 0 ? totalAmount : new BN(0);
276
+
277
+ expect(claimable.eq(totalAmount)).toBe(true);
278
+ });
279
+
280
+ it('should calculate cliff period correctly', () => {
281
+ const vestingStartTime = 1700000000; // Some past timestamp
282
+ const cliffMonths = 6;
283
+ const vestingMonths = 24;
284
+ const now = vestingStartTime + (4 * 30 * 24 * 60 * 60); // 4 months later
285
+
286
+ const cliffEndTime = vestingStartTime + (cliffMonths * 30 * 24 * 60 * 60);
287
+ const isCliffPassed = now >= cliffEndTime;
288
+
289
+ expect(isCliffPassed).toBe(false); // 4 months < 6 months cliff
290
+ });
291
+
292
+ it('should calculate linear vesting correctly', () => {
293
+ const vestingStartTime = 1700000000;
294
+ const cliffMonths = 6;
295
+ const vestingMonths = 24;
296
+ const totalAmount = new BN(1_000_000);
297
+
298
+ // 12 months after vesting start (6 months into linear vesting)
299
+ const now = vestingStartTime + (12 * 30 * 24 * 60 * 60);
300
+
301
+ const cliffEndTime = vestingStartTime + (cliffMonths * 30 * 24 * 60 * 60);
302
+ const vestingEndTime = vestingStartTime + (vestingMonths * 30 * 24 * 60 * 60);
303
+ const vestingDuration = vestingEndTime - cliffEndTime;
304
+
305
+ // Time since cliff ended
306
+ const timeSinceCliff = Math.max(0, now - cliffEndTime);
307
+
308
+ // Linear unlock percentage (capped at 100%)
309
+ const unlockPercentage = Math.min(timeSinceCliff / vestingDuration, 1);
310
+
311
+ const claimable = totalAmount.muln(Math.floor(unlockPercentage * 10000)).divn(10000);
312
+
313
+ // 6 months into 18-month linear vesting = 33.3%
314
+ expect(unlockPercentage).toBeCloseTo(0.333, 1);
315
+ });
316
+ });
317
+
318
+ describe('vote weight validation', () => {
319
+ it('should accumulate vote weights correctly', () => {
320
+ let votesFor = new BN(0);
321
+ let votesAgainst = new BN(0);
322
+
323
+ // Simulate votes
324
+ const votes = [
325
+ { weight: new BN(100), voteFor: true },
326
+ { weight: new BN(200), voteFor: true },
327
+ { weight: new BN(150), voteFor: false },
328
+ ];
329
+
330
+ votes.forEach((vote) => {
331
+ if (vote.voteFor) {
332
+ votesFor = votesFor.add(vote.weight);
333
+ } else {
334
+ votesAgainst = votesAgainst.add(vote.weight);
335
+ }
336
+ });
337
+
338
+ expect(votesFor.toNumber()).toBe(300);
339
+ expect(votesAgainst.toNumber()).toBe(150);
340
+ });
341
+
342
+ it('should calculate pass threshold correctly (>51%)', () => {
343
+ const testCases = [
344
+ { votesFor: 51, votesAgainst: 49, expected: true },
345
+ { votesFor: 50, votesAgainst: 50, expected: false },
346
+ { votesFor: 100, votesAgainst: 0, expected: true },
347
+ { votesFor: 0, votesAgainst: 100, expected: false },
348
+ { votesFor: 52, votesAgainst: 48, expected: true },
349
+ ];
350
+
351
+ testCases.forEach(({ votesFor, votesAgainst, expected }) => {
352
+ const totalVotes = votesFor + votesAgainst;
353
+ const hasPassed = totalVotes > 0 && (votesFor * 100) / totalVotes > 51;
354
+ expect(hasPassed).toBe(expected);
355
+ });
356
+ });
357
+ });
358
+ });