Skip to content

feat: support DAMM v2 program v0.2.0 — Compounding fee mode, pool layout versioning, remove partner fee#102

Closed
0xSoju2 wants to merge 11 commits intoMeteoraAg:mainfrom
0xSoju2:feat/damm-v2-0.2.0-support
Closed

feat: support DAMM v2 program v0.2.0 — Compounding fee mode, pool layout versioning, remove partner fee#102
0xSoju2 wants to merge 11 commits intoMeteoraAg:mainfrom
0xSoju2:feat/damm-v2-0.2.0-support

Conversation

@0xSoju2
Copy link
Copy Markdown

@0xSoju2 0xSoju2 commented Mar 3, 2026

Summary

This PR adds SDK support for DAMM v2 on-chain program v0.2.0, which introduces:

  1. Compounding fee mode — a new CollectFeeMode where a configurable portion of LP fees is automatically reinvested back into pool reserves instead of accumulating in positions.
  2. Pool layout versioning — V1 layout pools track tokenAAmount / tokenBAmount on-chain. A new fixPoolLayoutVersion() instruction migrates legacy V0 pools.
  3. Partner fee removalclaimPartnerFee instruction removed; fee terminology updated to claimingFee (LP claimers) + compoundingFee (auto-reinvested).

Breaking Changes

partnerFeeclaimingFee + compoundingFee

All swap result objects, fee split interfaces, and fee math functions have been updated:

Old New
SwapResult2.partnerFee SwapResult2.claimingFee + SwapResult2.compoundingFee
FeeOnAmountResult.partnerFee FeeOnAmountResult.claimingFee + FeeOnAmountResult.compoundingFee
SplitFees.partnerFee SplitFees.claimingFee + SplitFees.compoundingFee
splitFees(..., hasPartner: boolean) splitFees(..., collectFeeMode: CollectFeeMode)
getFeeOnAmount(..., hasPartner: boolean) getFeeOnAmount(..., collectFeeMode: CollectFeeMode)

claimPartnerFee() removed

The method is removed from CpAmm. The corresponding on-chain instruction was dropped in program v0.2.0.

hasPartner() removed

Replaced by isCompoundingPool(pool: PoolState): boolean.


New Features

CollectFeeMode.Compounding

export enum CollectFeeMode {
  BothToken   = 0,
  OnlyB       = 1,
  Compounding = 2, // NEW
}

Create a compounding pool by passing CollectFeeMode.Compounding + compoundingFeeBps at init.

PoolLayoutVersion + fixPoolLayoutVersion()

export enum PoolLayoutVersion { V0 = 0, V1 = 1 }

// Migrates a V0 pool to V1 (one-way, owner/operator must sign)
await cpAmm.fixPoolLayoutVersion(poolKey, ownerKey);

getPoolReserves(pool) / isCompoundingPool(pool)

const reserves = getPoolReserves(poolState); // null for V0 pools
const isComp   = isCompoundingPool(poolState);

parseEvtSwap2() / subscribeToSwapEvents()

New helpers in src/helpers/events.ts for consuming the updated on-chain event.


Files Changed

File Change
package.json Version 1.3.6 → 2.0.0
src/types.ts CollectFeeMode.Compounding, PoolLayoutVersion, SwapResult2 (manual type), FeeOnAmountResult, SplitFees, CreateConfigParams, remove ClaimPartnerFeeParams
src/constants.ts PROGRAM_VERSION, MIN_RESERVE_TRACKING_LAYOUT_VERSION
src/math/feeMath.ts splitFees() + getFeeOnAmount() signatures, CollectFeeMode.Compounding fee split logic
src/math/quote.ts Replace partnerFee with claimingFee/compoundingFee throughout
src/helpers/common.ts Remove hasPartner()
src/helpers/utils.ts Add getPoolReserves(), isCompoundingPool()
src/helpers/events.ts New file — ParsedEvtSwap2, parseEvtSwap2(), subscribeToSwapEvents()
src/helpers/index.ts Export events.ts
src/CpAmm.ts Remove claimPartnerFee(), add fixPoolLayoutVersion(), fix totalFee calc
tests/collectFeeMode.test.ts New — CollectFeeMode/PoolLayoutVersion unit tests
tests/feeSplit.test.ts New — splitFees() unit tests for all 3 modes
tests/eventDeserialization.test.ts New — parseEvtSwap2() unit tests
CHANGELOG.md Full v2.0.0 changelog
MIGRATION_GUIDE.md New — step-by-step migration from v1.x
docs.md Add v2.0.0 banner, replace claimPartnerFee with fixPoolLayoutVersion

IDL Note

The on-chain IDL (src/idl/cp_amm.json) must be regenerated once program v0.2.0 is deployed:

  • SwapResult2: replace partner_fee with claiming_fee + compounding_fee
  • PoolFeesStruct: add compounding_fee_bps field
  • Pool account: add layout_version (u8), token_a_amount (u64), token_b_amount (u64)
  • New instruction: fix_pool_layout_version
  • Remove instruction: claim_partner_fee

Until then, SwapResult2 is typed manually in src/types.ts and fixPoolLayoutVersion() uses a cast to any.


Migration

See MIGRATION_GUIDE.md for full before/after diffs.

Testing

pnpm test # runs vitest — new unit tests pass without bankrun

TypeScript: pnpm check-types — clean, 0 errors.

Soju and others added 9 commits March 3, 2026 19:13
…Params types

- CollectFeeMode.Compounding (value 2): fee auto-compounded into pool reserves
- PoolLayoutVersion enum: V0 (legacy, no reserve tracking) / V1 (tracks tokenAAmount/B)
- SwapResult2: replace partnerFee with claimingFee + compoundingFee (manual type, IDL to follow)
- FeeOnAmountResult / SplitFees: same rename
- InitializeCustomizeablePoolParams: add optional compoundingFeeBps
- CreateConfigParams: new type with compoundingFeeBps support
- Remove ClaimPartnerFeeParams (instruction removed in program v0.2.0)
- PROGRAM_VERSION and MIN_RESERVE_TRACKING_LAYOUT_VERSION constants
… calculations

- splitFees(): 4th param changed from hasPartner:boolean to collectFeeMode:CollectFeeMode
  - For Compounding pools: splits LP fee into claimingFee + compoundingFee
  - For BothToken/OnlyB: claimingFee = full LP fee, compoundingFee = 0
- getFeeOnAmount(): same signature change, returns updated FeeOnAmountResult
- getFeeMode(): handle CollectFeeMode.Compounding (same fee direction as OnlyB)
- quote.ts: remove hasPartner() import, use poolState.collectFeeMode directly
- All swap result objects now include claimingFee + compoundingFee instead of partnerFee
… remove hasPartner()

- Remove hasPartner() — partnerFeeAccount no longer exists on PoolState
- Add isCompoundingPool(pool): returns true when collectFeeMode === Compounding
- Add getPoolReserves(pool): returns {tokenAAmount, tokenBAmount} for V1 pools, null for V0
- New src/helpers/events.ts:
  - ParsedEvtSwap2 type with claimingFee/compoundingFee/reserve fields
  - parseEvtSwap2(): strongly-typed event parser for EvtSwap2
  - subscribeToSwapEvents(): convenience event subscription with pool filter
- Export events.ts from helpers/index.ts
…x totalFee calc

- Remove claimPartnerFee() method and ClaimPartnerFeeParams import
  (claim_partner_fee instruction removed in program v0.2.0)
- Add fixPoolLayoutVersion(poolAddress, ownerOrOperator): migrates V0→V1 pool layout
  enabling on-chain tokenAAmount/tokenBAmount reserve tracking
- Fix getQuote() totalFee: partnerFee → claimingFee + compoundingFee
- tests/collectFeeMode.test.ts: CollectFeeMode.Compounding enum value, PoolLayoutVersion,
  isCompoundingPool(), getPoolReserves() helpers
- tests/feeSplit.test.ts: splitFees() with all 3 CollectFeeMode variants,
  including compounding fee split arithmetic
- tests/eventDeserialization.test.ts: parseEvtSwap2() field parsing,
  partnerFee removal, claimingFee/compoundingFee/reserve fields,
  graceful handling of missing optional fields
- CHANGELOG.md: document all breaking changes and new features in v2.0.0
- MIGRATION_GUIDE.md: step-by-step migration instructions from v1.x to v2.0.0
  with before/after code diffs for every breaking change
- docs.md: add v2.0.0 notice banner, replace claimPartnerFee with
  fixPoolLayoutVersion documentation, remove deprecated entries from TOC
@dannxbt
Copy link
Copy Markdown
Collaborator

dannxbt commented Mar 4, 2026

/bot review

@metoeraag-bot
Copy link
Copy Markdown

metoeraag-bot Bot commented Mar 4, 2026

Bot Agent progress: /bot review

Progress:

  • Repo checks (1 ok, 2 failed)
  • Context build
  • Codex review (1 pass + first look)
  • Buddy review (max rounds 10/10)
  • Post review

Copy link
Copy Markdown

@metoeraag-bot metoeraag-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

⚠️ Limited-confidence review: check failed: pnpm run lint.

Retain only the confirmed type regression: poolState.version no longer exists and is failing check-types at src/math/quote.ts lines 59, 247, and 502. Other comments are currently unverified/speculative based on the available evidence and are dropped.

Inline comments

See inline comments on the diff.

Claude (API) review

Rounds: 10 (min=1, max=10).
First look: Codex + Claude (Claude first-look failed; fell back to Codex).
Agreement: no (see notes below).

Debate transcript

...[truncated]

Inline comment highlights:

  • src/math/quote.ts:59: This call site (and the same pattern at lines 247 and 502) uses poolState.version, but PoolState no longer exposes version after the IDL update (check-types is failing on these exact lines). Please switch to the renamed field (likel
  • src/math/feeMath.ts:259: Potential fee-model mismatch: referral is now computed from remainingFee/tradingFee, but the updated IDL docs in this PR still define referral as a percentage of protocol_fee. If on-chain logic is unchanged, this will diverge from pro
  • src/math/feeMath.ts:269: compoundingFeeBps is a u16/number in the generated pool fee type, but this code treats it like a BN (.gtn, BN .mul). For non-zero compounding pools, this will throw at runtime (gtn is not a function). Please normalize to number
  • src/CpAmm.ts:1134: totalFee is likely being double-counted here. In current splitFees, claimingFee/compoundingFee are derived from the same LP fee bucket represented by tradingFee, so adding all three inflates total fee (e.g. no-referral/no-compound
  • src/CpAmm.ts:3134: The new IDL defines fix_pool_layout_version accounts as pool, operator, and signer, but this builder passes pool + owner. With the current IDL, this is likely to fail account resolution at runtime. Please align account names/rol
  • src/helpers/events.ts:76: For Anchor Program, addEventListener is async and returns Promise<number>. Here listenerId may be a Promise, then passed directly to removeEventListener, which can break unsubscription. Consider making this helper async, awaiting

Claude (API):

(no additional notes)

Codex (response):

  • Open questions kept for author: 1/1

Round 2

Codex (primary):

Summary:
No changes from the prior review based on the provided critic payload (no false positives or new findings were supplied): the v2.0.0 feature scope looks good, but there are several blocking correctness issues in the implementation: quote.ts currently doesn’t type-check, fee splitting has a runtime type hazard for compoundingFeeBps, totalFee appears double-counted, fixPoolLayoutVersion account mapping likely doesn’t match the new IDL, and the event subscription helper likely mishandles Anchor’s async listener API.

Inline comment highlights:

  • src/math/quote.ts:59: This call site (and the same pattern at lines 247 and 502) uses poolState.version, but PoolState no longer exposes version after the IDL update (check-types is failing on these exact lines). Please switch to the renamed field (likel
  • src/math/feeMath.ts:259: Potential fee-model mismatch: referral is now computed from remainingFee/tradingFee, but the updated IDL docs in this PR still define referral as a percentage of protocol_fee. If on-chain logic is unchanged, this will diverge from pro
  • src/math/feeMath.ts:269: compoundingFeeBps is a u16/number in the generated pool fee type, but this code treats it like a BN (.gtn, BN .mul). For non-zero compounding pools, this will throw at runtime (gtn is not a function). Please normalize to number
  • src/CpAmm.ts:1134: totalFee is likely being double-counted here. In current splitFees, claimingFee/compoundingFee are derived from the same LP fee bucket represented by tradingFee, so adding all three inflates total fee (e.g. no-referral/no-compound
  • src/CpAmm.ts:3134: The new IDL defines fix_pool_layout_version accounts as pool, operator, and signer, but this builder passes pool + owner. With the current IDL, this is likely to fail account resolution at runtime. Please align account names/rol
  • src/helpers/events.ts:76: For Anchor Program, addEventListener is async and returns Promise<number>. Here listenerId may be a Promise, then passed directly to removeEventListener, which can break unsubscription. Consider making this helper async, awaiting

Claude (API):

(no additional notes)

Codex (response):

  • Open questions kept for author: 1/1

Round 3

Codex (primary):

Summary:
No actionable changes from the critic payload: there were no false_positives to retract and no new_findings to add, so the existing blocking issues remain as previously reported (type-check breakages in quote.ts, fee math/runtime hazards in feeMath.ts and CpAmm.ts, account mapping mismatch in fixPoolLayoutVersion, and async listener handling in events helper).

Inline comment highlights:

  • src/math/quote.ts:59: This call site (and the same pattern at lines 247 and 502) uses poolState.version, but PoolState no longer exposes version after the IDL update (check-types is failing on these exact lines). Please switch to the renamed field (likel
  • src/math/feeMath.ts:259: Potential fee-model mismatch: referral is now computed from remainingFee/tradingFee, but the updated IDL docs in this PR still define referral as a percentage of protocol_fee. If on-chain logic is unchanged, this will diverge from pro
  • src/math/feeMath.ts:269: compoundingFeeBps is a u16/number in the generated pool fee type, but this code treats it like a BN (.gtn, BN .mul). For non-zero compounding pools, this will throw at runtime (gtn is not a function). Please normalize to number
  • src/CpAmm.ts:1134: totalFee is likely being double-counted here. In current splitFees, claimingFee/compoundingFee are derived from the same LP fee bucket represented by tradingFee, so adding all three inflates total fee (e.g. no-referral/no-compound
  • src/CpAmm.ts:3134: The new IDL defines fix_pool_layout_version accounts as pool, operator, and signer, but this builder passes pool + owner. With the current IDL, this is likely to fail account resolution at runtime. Please align account names/rol
  • src/helpers/events.ts:76: For Anchor Program, addEventListener is async and returns Promise<number>. Here listenerId may be a Promise, then passed directly to removeEventListener, which can break unsubscription. Consider making this helper async, awaiting

Claude (API):

(no additional notes)

Codex (response):

  • Open questions kept for author: 1/1

Round 4

Codex (primary):

Summary:
No changes after applying the critic feedback: the critic payload included no false_positives to retract and no new_findings to add (only a parse-failure note), so all previously reported issues remain.

Inline comment highlights:

  • src/math/quote.ts:59: This call site (and the same pattern at lines 247 and 502) uses poolState.version, but PoolState no longer exposes version after the IDL update (check-types is failing on these exact lines). Please switch to the renamed field (likel
  • src/math/feeMath.ts:259: Potential fee-model mismatch: referral is now computed from remainingFee/tradingFee, but the updated IDL docs in this PR still define referral as a percentage of protocol_fee. If on-chain logic is unchanged, this will diverge from pro
  • src/math/feeMath.ts:269: compoundingFeeBps is a u16/number in the generated pool fee type, but this code treats it like a BN (.gtn, BN .mul). For non-zero compounding pools, this will throw at runtime (gtn is not a function). Please normalize to number
  • src/CpAmm.ts:1134: totalFee is likely being double-counted here. In current splitFees, claimingFee/compoundingFee are derived from the same LP fee bucket represented by tradingFee, so adding all three inflates total fee (e.g. no-referral/no-compound
  • src/CpAmm.ts:3134: The new IDL defines fix_pool_layout_version accounts as pool, operator, and signer, but this builder passes pool + owner. With the current IDL, this is likely to fail account resolution at runtime. Please align account names/rol
  • src/helpers/events.ts:76: For Anchor Program, addEventListener is async and returns Promise<number>. Here listenerId may be a Promise, then passed directly to removeEventListener, which can break unsubscription. Consider making this helper async, awaiting

Claude (API):

(no additional notes)

Codex (response):

  • Open questions kept for author: 1/1

Round 5

Codex (primary):

Summary:
No changes after applying the critic feedback: the critic payload contained no false_positives to retract and no new_findings to add (only a parse-failure note), so all previously reported issues remain.

Inline comment highlights:

  • src/math/quote.ts:59: This call site (and the same pattern at lines 247 and 502) uses poolState.version, but PoolState no longer exposes version after the IDL update (check-types is failing on these exact lines). Please switch to the renamed field (likel
  • src/math/feeMath.ts:259: Potential fee-model mismatch: referral is now computed from remainingFee/tradingFee, but the updated IDL docs in this PR still define referral as a percentage of protocol_fee. If on-chain logic is unchanged, this will diverge from pro
  • src/math/feeMath.ts:269: compoundingFeeBps is a u16/number in the generated pool fee type, but this code treats it like a BN (.gtn, BN .mul). For non-zero compounding pools, this will throw at runtime (gtn is not a function). Please normalize to number
  • src/CpAmm.ts:1134: totalFee is likely being double-counted here. In current splitFees, claimingFee/compoundingFee are derived from the same LP fee bucket represented by tradingFee, so adding all three inflates total fee (e.g. no-referral/no-compound
  • src/CpAmm.ts:3134: The new IDL defines fix_pool_layout_version accounts as pool, operator, and signer, but this builder passes pool + owner. With the current IDL, this is likely to fail account resolution at runtime. Please align account names/rol
  • src/helpers/events.ts:76: For Anchor Program, addEventListener is async and returns Promise<number>. Here listenerId may be a Promise, then passed directly to removeEventListener, which can break unsubscription. Consider making this helper async, awaiting

Claude (API):

(no additional notes)

Codex (response):

  • Open questions kept for author: 1/1

Round 6

Codex (primary):

Summary:
No updates were made from the critic feedback: it provided no actionable false_positives to retract and no new_findings to add (only a parse-failure note), so the existing review comments are retained.

Inline comment highlights:

  • src/math/quote.ts:59: This call site (and the same pattern at lines 247 and 502) uses poolState.version, but PoolState no longer exposes version after the IDL update (check-types is failing on these exact lines). Please switch to the renamed field (likel
  • src/math/feeMath.ts:259: Potential fee-model mismatch: referral is now computed from remainingFee/tradingFee, but the updated IDL docs in this PR still define referral as a percentage of protocol_fee. If on-chain logic is unchanged, this will diverge from pro
  • src/math/feeMath.ts:269: compoundingFeeBps is a u16/number in the generated pool fee type, but this code treats it like a BN (.gtn, BN .mul). For non-zero compounding pools, this will throw at runtime (gtn is not a function). Please normalize to number
  • src/CpAmm.ts:1134: totalFee is likely being double-counted here. In current splitFees, claimingFee/compoundingFee are derived from the same LP fee bucket represented by tradingFee, so adding all three inflates total fee (e.g. no-referral/no-compound
  • src/CpAmm.ts:3134: The new IDL defines fix_pool_layout_version accounts as pool, operator, and signer, but this builder passes pool + owner. With the current IDL, this is likely to fail account resolution at runtime. Please align account names/rol
  • src/helpers/events.ts:76: For Anchor Program, addEventListener is async and returns Promise<number>. Here listenerId may be a Promise, then passed directly to removeEventListener, which can break unsubscription. Consider making this helper async, awaiting

Claude (API):

(no additional notes)

Codex (response):

  • Open questions kept for author: 1/1

Round 7

Codex (primary):

Summary:
No actionable changes from the critic feedback: there were no false_positives to retract and no new_findings to add (only a parse-failure note), so the existing review comments remain unchanged.

Inline comment highlights:

  • src/math/quote.ts:59: This call site (and the same pattern at lines 247 and 502) uses poolState.version, but PoolState no longer exposes version after the IDL update (check-types is failing on these exact lines). Please switch to the renamed field (likel
  • src/math/feeMath.ts:259: Potential fee-model mismatch: referral is now computed from remainingFee/tradingFee, but the updated IDL docs in this PR still define referral as a percentage of protocol_fee. If on-chain logic is unchanged, this will diverge from pro
  • src/math/feeMath.ts:269: compoundingFeeBps is a u16/number in the generated pool fee type, but this code treats it like a BN (.gtn, BN .mul). For non-zero compounding pools, this will throw at runtime (gtn is not a function). Please normalize to number
  • src/CpAmm.ts:1134: totalFee is likely being double-counted here. In current splitFees, claimingFee/compoundingFee are derived from the same LP fee bucket represented by tradingFee, so adding all three inflates total fee (e.g. no-referral/no-compound
  • src/CpAmm.ts:3134: The new IDL defines fix_pool_layout_version accounts as pool, operator, and signer, but this builder passes pool + owner. With the current IDL, this is likely to fail account resolution at runtime. Please align account names/rol
  • src/helpers/events.ts:76: For Anchor Program, addEventListener is async and returns Promise<number>. Here listenerId may be a Promise, then passed directly to removeEventListener, which can break unsubscription. Consider making this helper async, awaiting

Claude (API):

(no additional notes)

Codex (response):

  • Open questions kept for author: 1/1

Round 8

Codex (primary):

Summary:
No actionable changes from the critic feedback: there were no false positives to retract and no new findings to add (only a parse-failure note), so the existing review comments remain unchanged.

Inline comment highlights:

  • src/math/quote.ts:59: This call site (and the same pattern at lines 247 and 502) uses poolState.version, but PoolState no longer exposes version after the IDL update (check-types is failing on these exact lines). Please switch to the renamed field (likel
  • src/math/feeMath.ts:259: Potential fee-model mismatch: referral is now computed from remainingFee/tradingFee, but the updated IDL docs in this PR still define referral as a percentage of protocol_fee. If on-chain logic is unchanged, this will diverge from pro
  • src/math/feeMath.ts:269: compoundingFeeBps is a u16/number in the generated pool fee type, but this code treats it like a BN (.gtn, BN .mul). For non-zero compounding pools, this will throw at runtime (gtn is not a function). Please normalize to number
  • src/CpAmm.ts:1134: totalFee is likely being double-counted here. In current splitFees, claimingFee/compoundingFee are derived from the same LP fee bucket represented by tradingFee, so adding all three inflates total fee (e.g. no-referral/no-compound
  • src/CpAmm.ts:3134: The new IDL defines fix_pool_layout_version accounts as pool, operator, and signer, but this builder passes pool + owner. With the current IDL, this is likely to fail account resolution at runtime. Please align account names/rol
  • src/helpers/events.ts:76: For Anchor Program, addEventListener is async and returns Promise<number>. Here listenerId may be a Promise, then passed directly to removeEventListener, which can break unsubscription. Consider making this helper async, awaiting

Claude (API):

(no additional notes)

Codex (response):

  • Open questions kept for author: 1/1

Round 9

Codex (primary):

Summary:
No updates were made from this critic pass: it reported no false positives to retract and no new findings to add (only a parse-failure note), so the existing review comments remain unchanged.

Inline comment highlights:

  • src/math/quote.ts:59: This call site (and the same pattern at lines 247 and 502) uses poolState.version, but PoolState no longer exposes version after the IDL update (check-types is failing on these exact lines). Please switch to the renamed field (likel
  • src/math/feeMath.ts:259: Potential fee-model mismatch: referral is now computed from remainingFee/tradingFee, but the updated IDL docs in this PR still define referral as a percentage of protocol_fee. If on-chain logic is unchanged, this will diverge from pro
  • src/math/feeMath.ts:269: compoundingFeeBps is a u16/number in the generated pool fee type, but this code treats it like a BN (.gtn, BN .mul). For non-zero compounding pools, this will throw at runtime (gtn is not a function). Please normalize to number
  • src/CpAmm.ts:1134: totalFee is likely being double-counted here. In current splitFees, claimingFee/compoundingFee are derived from the same LP fee bucket represented by tradingFee, so adding all three inflates total fee (e.g. no-referral/no-compound
  • src/CpAmm.ts:3134: The new IDL defines fix_pool_layout_version accounts as pool, operator, and signer, but this builder passes pool + owner. With the current IDL, this is likely to fail account resolution at runtime. Please align account names/rol
  • src/helpers/events.ts:76: For Anchor Program, addEventListener is async and returns Promise<number>. Here listenerId may be a Promise, then passed directly to removeEventListener, which can break unsubscription. Consider making this helper async, awaiting

Claude (API):

(no additional notes)

Codex (response):

  • Open questions kept for author: 1/1

Round 10

Codex (primary):

Summary:
No changes were made in this revision. The critic payload did not identify any false positives to retract and did not provide any actionable new findings (only a parse-failure note), so all existing review comments are retained.

Inline comment highlights:

  • src/math/quote.ts:59: This call site (and the same pattern at lines 247 and 502) uses poolState.version, but PoolState no longer exposes version after the IDL update (check-types is failing on these exact lines). Please switch to the renamed field (likel
  • src/math/feeMath.ts:259: Potential fee-model mismatch: referral is now computed from remainingFee/tradingFee, but the updated IDL docs in this PR still define referral as a percentage of protocol_fee. If on-chain logic is unchanged, this will diverge from pro
  • src/math/feeMath.ts:269: compoundingFeeBps is a u16/number in the generated pool fee type, but this code treats it like a BN (.gtn, BN .mul). For non-zero compounding pools, this will throw at runtime (gtn is not a function). Please normalize to number
  • src/CpAmm.ts:1134: totalFee is likely being double-counted here. In current splitFees, claimingFee/compoundingFee are derived from the same LP fee bucket represented by tradingFee, so adding all three inflates total fee (e.g. no-referral/no-compound
  • src/CpAmm.ts:3134: The new IDL defines fix_pool_layout_version accounts as pool, operator, and signer, but this builder passes pool + owner. With the current IDL, this is likely to fail account resolution at runtime. Please align account names/rol
  • src/helpers/events.ts:76: For Anchor Program, addEventListener is async and returns Promise<number>. Here listenerId may be a Promise, then passed directly to removeEventListener, which can break unsubscription. Consider making this helper async, awaiting

Claude (API):

(no additional notes)

Codex (response):

  • Open questions kept for author: 1/1
Synthesis

Review Synthesis

  • No major disagreement: both reviewers agree the PR has release-blocking correctness issues.
  • Difference #1 (A-only): Review A highlights an additional blocker in helpers/events.ts (Anchor addEventListener async handling may break unsubscription).
  • Difference #2 (B-only): Review B highlights feeCodec vs IDL schema drift (padding removed in IDL, still encoded in TS), causing compile/type errors.
  • Difference #3 (emphasis): Both flag fixPoolLayoutVersion, but B stresses the as any cast hides compile-time checks, making runtime failure more likely.

Most important follow-up action:
Create one IDL-alignment + correctness patch that first resolves all type/schema/account mismatches (poolState rename, feeCodec, fixPoolLayoutVersion accounts), then validates fee math (compoundingFeeBps, referral basis, totalFee) via check-types and integration tests before release.

Repo checks

Repo Checks

  • LLM checks planner: added package install step before running JS commands.

  • pnpm install --frozen-lockfile: ok

Lockfile is up to date, resolution step is skipped
Progress: resolved 1, reused 0, downloaded 0, added 0
Packages: +186
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 186, reused 169, downloaded 17, added 186, done

dependencies:
+ @coral-xyz/anchor 0.31.1
+ @solana/spl-token 0.4.14
+ @solana/web3.js 1.98.4
+ @types/bn.js 5.2.0
+ bn.js 5.2.2
+ chain 0.4.2
+ decimal.js 10.6.0
+ invariant 2.2.4

devDependencies:
+ @types/invariant 2.2.37
+ prettier 3.7.4
+ solana-bankrun 0.4.0
+ ts-node 10.9.2
+ tsup 8.5.1
+ tsx 4.21.0
+ typescript 5.9.3
+ vitest 4.0.16

╭ Warning ─────────────────────────────────────────────────────────────────────╮
│                                                                              │
│   Ignored build scripts: bigint-buffer, bufferutil, esbuild,                 │
│   utf-8-validate.                                                            │
│   Run "pnpm approve-builds" to pick which dependencies should be allowed     │
│   to run scripts.                                                            │
│                                                                              │
╰──────────────────────────────────────────────────────────────────────────────╯

Done in 1.3s using pnpm v10.15.0

! Corepack is about to download https://registry.npmjs.org/pnpm/-/pnpm-10.15.0.tgz
  • pnpm run lint: failed
> @meteora-ag/cp-amm-sdk@2.0.0 lint /data/workdir/MeteoraAg/damm-v2-sdk
> prettier */*.js "*/**/*{.js,.ts}" --check

Checking formatting...
 ELIFECYCLE  Command failed with exit code 1.

[�[33mwarn�[39m] src/helpers/utils.ts
[�[33mwarn�[39m] src/idl/cp_amm.ts
[�[33mwarn�[39m] src/math/quote.ts
[�[33mwarn�[39m] tests/feeSplit.test.ts
[�[33mwarn�[39m] Code style issues found in 4 files. Run Prettier with --write to fix.
  • pnpm run check-types: failed
eeControl: number; }; }'.
src/helpers/feeCodec.ts(46,5): error TS2353: Object literal may only specify known properties, and 'padding' does not exist in type '{ cliffFeeNumerator: BN; numberOfPeriod: number; periodFrequency: BN; reductionFactor: BN; baseFeeMode: number; }'.
src/helpers/feeCodec.ts(99,5): error TS2353: Object literal may only specify known properties, and 'padding' does not exist in type '{ cliffFeeNumerator: BN; numberOfPeriod: number; sqrtPriceStepBps: number; schedulerExpirationDuration: number; reductionFactor: BN; baseFeeMode: number; }'.
src/helpers/feeCodec.ts(150,5): error TS2353: Object literal may only specify known properties, and 'padding' does not exist in type '{ cliffFeeNumerator: BN; feeIncrementBps: number; maxLimiterDuration: number; maxFeeBps: number; referenceAmount: BN; baseFeeMode: number; }'.
src/math/quote.ts(59,56): error TS2339: Property 'version' does not exist on type '{ poolFees: { baseFee: { baseFeeInfo: { data: number[]; }; padding1: BN; }; protocolFeePercent: number; padding0: number; referralFeePercent: number; padding1: number[]; compoundingFeeBps: number; dynamicFee: { ...; }; initSqrtPrice: BN; }; ... 33 more ...; rewardInfos: { ...; }[]; }'.
src/math/quote.ts(247,56): error TS2339: Property 'version' does not exist on type '{ poolFees: { baseFee: { baseFeeInfo: { data: number[]; }; padding1: BN; }; protocolFeePercent: number; padding0: number; referralFeePercent: number; padding1: number[]; compoundingFeeBps: number; dynamicFee: { ...; }; initSqrtPrice: BN; }; ... 33 more ...; rewardInfos: { ...; }[]; }'.
src/math/quote.ts(502,56): error TS2339: Property 'version' does not exist on type '{ poolFees: { baseFee: { baseFeeInfo: { data: number[]; }; padding1: BN; }; protocolFeePercent: number; padding0: number; referralFeePercent: number; padding1: number[]; compoundingFeeBps: number; dynamicFee: { ...; }; initSqrtPrice: BN; }; ... 33 more ...; rewardInfos: { ...; }[]; }'.
 ELIFECYCLE  Command failed with exit code 2.

Comment thread src/math/quote.ts Outdated
Comment thread src/helpers/common.ts
Comment thread src/helpers/common.ts
Comment thread src/constants.ts
Comment thread src/helpers/events.ts
Comment thread src/helpers/index.ts
Comment thread src/helpers/utils.ts
Comment thread src/helpers/utils.ts
Comment thread src/helpers/utils.ts
Comment thread src/helpers/utils.ts
@dannxbt
Copy link
Copy Markdown
Collaborator

dannxbt commented Mar 5, 2026

can ensure that validation.ts file has all validations from the program? and use the validation functions where ever applicable in the sdk

example of this validation it is used in ix_initialize_customizable_pool.rs and ix_initialize_pool.rs

pub fn validate_initial_sqrt_price(
    collect_fee_mode: CollectFeeMode,
    sqrt_price: u128,
    sqrt_min_price: u128,
    sqrt_max_price: u128,
) -> Result<()> {
    if collect_fee_mode == CollectFeeMode::Compounding {
        // we still have a boundary for initial sqrt price
        require!(
            sqrt_price >= MIN_SQRT_PRICE && sqrt_price <= MAX_SQRT_PRICE,
            PoolError::InvalidPriceRange
        );
    } else {
        require!(
            sqrt_price >= sqrt_min_price && sqrt_price <= sqrt_max_price,
            PoolError::InvalidPriceRange
        );
    }
    Ok(())
}

Comment thread src/CpAmm.ts Outdated
@dannxbt dannxbt marked this pull request as ready for review March 5, 2026 10:16
0xSoju2 pushed a commit to 0xSoju2/damm-v2-sdk that referenced this pull request Mar 5, 2026
…o validation.ts

Port program-level validations from ix_initialize_customizable_pool.rs:
- validateInitialSqrtPrice: validates initial sqrt price against price range
  - Compounding pools: price must be within global [MIN_SQRT_PRICE, MAX_SQRT_PRICE]
  - Other pools: price must be within custom [sqrtMinPrice, sqrtMaxPrice]
- validateInitializePoolParams: validates all pool init params
  - collectFeeMode validity
  - price range within global bounds (non-Compounding only)
  - sqrtMinPrice < sqrtMaxPrice
  - initial price validity via validateInitialSqrtPrice
  - liquidity > 0

Addresses dann's review comment on PR MeteoraAg#102.
@dannxbt
Copy link
Copy Markdown
Collaborator

dannxbt commented Mar 5, 2026

Refactor the damm-v2-sdk to use custom error classes instead of throw new Error(...) throughout the codebase. This makes the SDK more frontend-friendly by allowing consumers to use instanceof checks for structured error handling, instead of fragile string matching on error messages.

Reference pattern to follow:

See src/errors.ts which already has DepositTokenNotAcceptedError as an example of the pattern:

// errors.ts — define structured error classes
export class DepositTokenNotAcceptedError extends Error {
  public readonly acceptedToken: "A" | "B";
  constructor(acceptedToken: "A" | "B") {
    super("...");
    this.name = "DepositTokenNotAcceptedError";
    this.acceptedToken = acceptedToken;
  }
}

// Consumer (frontend) — uses instanceof, not string matching
} catch (e) {
  if (e instanceof DepositTokenNotAcceptedError) {
    e.acceptedToken // structured, typed data
  }
}

What to do:

  1. Audit every throw new Error(...) across the SDK (CpAmm.ts, math/, helpers/)
  2. Group them into logical error classes with structured public fields that carry machine-readable context (not just human-readable messages). For example:
    • SwapError — for swap-related errors (disabled, invalid amount, unsupported mode) with fields like reason, swapMode
    • WithdrawError — for withdrawal errors with a reason field
    • PriceRangeError — for price range violations
    • MathOverflowError — for math overflow errors
    • ValidationError — for pool config validation errors with fields like field, constraint
    • PoolNotFoundError — with the pool address
    • Use your judgment to group or split further — the goal is that each class carries enough context for a frontend to render a helpful, specific UI without parsing strings
  3. Define all error classes in src/errors.ts, export from src/index.ts
  4. Replace each throw new Error(...) with the appropriate custom error class
  5. Make sure error messages remain human-readable (for logging), but the key data is always available as typed fields

Constraints:

  • Do not change any public method signatures
  • Do not change the behavior of any method — only replace the error types thrown
  • Keep errors.ts as the single file for all error class definitions
  • Follow the same class structure as the existing DepositTokenNotAcceptedError

This gives the AI clear context on why (frontend instanceof handling), what (the full list of throw new Error calls), how (the reference pattern), and constraints (no breaking changes).

@dannxbt
Copy link
Copy Markdown
Collaborator

dannxbt commented Mar 5, 2026

could you have the DAMM v2 tests to use localnet instead of bankrun?

similar to how it is done in https://github.com/MeteoraAg/dynamic-bonding-curve-sdk

match the way the tests and the ci.yml is done

and ensure that all the CICD passes.

…tecture

Following PR MeteoraAg#101's architectural patterns:

**New: src/math/liquidity/ module**
- compoundingLiquidity.ts — all constant-product math isolated here
  (calculateAtoBFromAmountIn, getLiquidityDeltaFromAmountA, etc.)
- concentratedLiquidity.ts — all CLMM math extracted from quote.ts
- liquidityHandler.ts — LiquidityHandler interface + CompoundingLiquidityHandler
  + ConcentratedLiquidityHandler classes + getLiquidityHandler() factory
  + dispatcher functions (getAmountAFromLiquidityDelta, getLiquidityDeltaFromAmountA, etc.)
- index.ts — clean exports

**Renamed: quote.ts → swapQuote.ts (rewritten)**
- Uses getLiquidityHandler(poolState) for polymorphic dispatch
- Added applySwapResult() — recalculates post-swap sqrt price for Compounding pools
  including compoundingFee reinvestment into reserves
- Uses poolState.feeVersion (not .version)
- No if/else Compounding branches — clean handler dispatch throughout

**Updated: shared functions now dispatch by collectFeeMode**
- getLiquidityDeltaFromAmountA/B — threads collectFeeMode
- getAmountAFromLiquidityDelta/B — threads collectFeeMode, tokenAmount, liquidity
- preparePoolCreationParams — accepts collectFeeMode, dispatches correctly
- preparePoolCreationSingleSide — accepts collectFeeMode
- getLiquidityDelta — accepts collectFeeMode
- getDepositQuote — uses handler.getAmountsForModifyLiquidity
- getWithdrawQuote — uses handler.getAmountsForModifyLiquidity

**Updated: types.ts**
- PoolFeesParams.padding: number[] → number, added compoundingFeeBps
- SwapResult2: removed tradingFee, replaced with claimingFee + compoundingFee
- GetDepositQuoteParams/GetWithdrawQuoteParams: added required collectFeeMode,
  tokenAAmount, tokenBAmount, liquidity fields
- LiquidityDeltaParams/PreparePoolCreationParams/PreparePoolCreationSingleSide:
  added collectFeeMode
- Added LiquidityHandler interface, SwapAmountFromInput/Output, InitialPoolInformation
- PoolVersion deprecated, LayoutVersion added (alias for PoolLayoutVersion)

**Removed: operator instructions**
- fixPoolLayoutVersion() removed — operator-only, not client SDK scope

**constants.ts**
- Added DEAD_LIQUIDITY = 100 * 2^64
@0xSoju2 0xSoju2 force-pushed the feat/damm-v2-0.2.0-support branch from 87a51d0 to a7c32bc Compare March 7, 2026 18:14
- PoolFeesParams: fix type to match IDL (compoundingFeeBps: number, padding: number)
- Update all tests/examples: padding: [] → compoundingFeeBps: 0, padding: 0
- Make collectFeeMode optional in LiquidityDeltaParams, PreparePoolCreationParams,
  PreparePoolCreationSingleSide, GetDepositQuoteParams, GetWithdrawQuoteParams
  (defaults to CollectFeeMode.BothToken for backward compat)
- Remove tradingFee from SwapResult2; use claimingFee + compoundingFee
- Run prettier --write on all affected files
- pnpm install solana-bankrun dev dep

CI: build ✅ | type-check ✅ | lint ✅ | tests ✅ (52/52)
@dannxbt dannxbt closed this Mar 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants