@forgedevv/core 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/pool.js CHANGED
@@ -33,14 +33,20 @@
33
33
  *
34
34
  * `buildUserCoinLaunchTx` is the same launch for a coin created by a user on the client's
35
35
  * launchpad config: the user is pool creator, rent payer, fee payer and buyer, the mint is the
36
- * only other signer.
36
+ * only other signer. Its first buy pays the FORGE platform fee (0.3%, rounded up) like any swap
37
+ * built by core: one SOL transfer to `FORGE_PLATFORM_FEE_WALLET`, the last instruction.
38
+ *
39
+ * Both builders compare the on-chain config with what core builds for it, field by field
40
+ * (`onchainConfigMismatches` in config.ts), before building anything.
37
41
  */
38
42
  import { DYNAMIC_BONDING_CURVE_PROGRAM_ID, DynamicBondingCurveClient, MigrationOption, TokenType, deriveDbcPoolAddress, } from '@meteora-ag/dynamic-bonding-curve-sdk';
39
43
  import { ACCOUNT_SIZE, ASSOCIATED_TOKEN_PROGRAM_ID, MINT_SIZE, NATIVE_MINT, TOKEN_PROGRAM_ID, createAssociatedTokenAccountIdempotentInstruction, getAssociatedTokenAddressSync, } from '@solana/spl-token';
40
44
  import { PublicKey, SystemProgram, Transaction, } from '@solana/web3.js';
41
45
  import BN from 'bn.js';
42
- import { quoteFirstBuyOnConfig } from './config.js';
46
+ import { getPlatformFeeWallet } from './addresses.js';
47
+ import { onchainConfigMismatches, quoteFirstBuyOnConfig, } from './config.js';
43
48
  import { FEES, MIGRATION_THRESHOLD_LAMPORTS, SOL_QUOTE_MINT } from './constants.js';
49
+ import { PLATFORM_FEE_BPS, platformFeeLamports } from './swap.js';
44
50
  import { LaunchCoinError, assertFirstBuyLamports, assertLaunchMetadata, } from './launch-metadata.js';
45
51
  import { MAX_TRANSACTION_BYTES, computeBudgetInstructions, priorityFeeLamports, readNonce, requiredSigners, transactionSize, } from './tx.js';
46
52
  // The metadata and first buy checks live in launch-metadata.ts (shared with config.ts); they
@@ -84,14 +90,12 @@ function assertDistinctKeys(entries) {
84
90
  }
85
91
  }
86
92
  /**
87
- * Checks that an on-chain config is the launchpad coin config of `owner` and that it is safe to
88
- * launch the coin on it: SOL quote, SPL token, fee claimer = owner, FORGE creator share, creator
89
- * locked liquidity, first swap at the minimum fee (otherwise the client's first buy would pay
90
- * the anti-sniper fee), fee scheduler base fee, DAMM v2 migration at exactly 10 SOL.
91
- *
92
- * @throws LaunchCoinError `CONFIG_MISMATCH` listing every mismatch.
93
+ * The essential checks of a config a coin is launched on, with readable messages: SOL quote (SPL),
94
+ * SPL base token, first swap at the minimum fee (otherwise the first buy pays the anti-sniper
95
+ * fee), a fee scheduler base fee, DAMM v2 migration at exactly 10 SOL. The full field by field
96
+ * comparison (`onchainConfigMismatches`) follows them.
93
97
  */
94
- export function assertLaunchCoinConfig(config, owner) {
98
+ function essentialConfigProblems(config) {
95
99
  const problems = [];
96
100
  if (!config.quoteMint.equals(SOL_QUOTE_MINT))
97
101
  problems.push('quote mint is not SOL');
@@ -99,16 +103,6 @@ export function assertLaunchCoinConfig(config, owner) {
99
103
  problems.push('quote token is not an SPL token');
100
104
  if (config.tokenType !== TokenType.SPLToken)
101
105
  problems.push('base token type is not SPL token');
102
- if (!config.feeClaimer.equals(owner))
103
- problems.push('fee claimer is not the owner wallet');
104
- if (!config.leftoverReceiver.equals(owner))
105
- problems.push('leftover receiver is not the owner');
106
- if (config.creatorTradingFeePercentage !== FEES.forgeCreatorSharePct) {
107
- problems.push(`creator trading fee is not ${FEES.forgeCreatorSharePct}%`);
108
- }
109
- if (!(config.creatorPermanentLockedLiquidityPercentage > 0)) {
110
- problems.push('creator has no permanently locked liquidity');
111
- }
112
106
  if (config.enableFirstSwapWithMinFee !== 1) {
113
107
  problems.push('first swap with minimum fee is disabled (the first buy would pay the anti-sniper fee)');
114
108
  }
@@ -121,6 +115,40 @@ export function assertLaunchCoinConfig(config, owner) {
121
115
  if (BigInt(config.migrationQuoteThreshold.toString()) !== MIGRATION_THRESHOLD_LAMPORTS) {
122
116
  problems.push(`migration threshold is not ${MIGRATION_THRESHOLD_LAMPORTS} lamports`);
123
117
  }
118
+ return problems;
119
+ }
120
+ /**
121
+ * Checks that an on-chain config is the launchpad coin config of `owner` and that it is safe to
122
+ * launch the coin on it. Besides the essential checks (SOL quote, SPL token, first swap at the
123
+ * minimum fee, fee scheduler, DAMM v2 at exactly 10 SOL), EVERY field is compared with what core
124
+ * builds for a launchpad coin config (`onchainConfigMismatches`): fee claimer and leftover
125
+ * receiver = owner, FORGE creator share, the 75 / 25 permanently locked liquidity, no pool
126
+ * creation fee, immutable metadata, the exact fee schedule, no migration fee, no locked vesting,
127
+ * FORGE's supply and curve, timestamp activation.
128
+ *
129
+ * With `spec` (the spec the config was built from), the reference is exactly
130
+ * `buildLaunchpadCoinConfig(spec)`; without it, the trading fee and the anti-sniper flag are read
131
+ * from the config and only have to be values a FORGE spec can hold (linear schedule, whole fee of
132
+ * 50..200 bps). Pass the spec whenever it is known.
133
+ *
134
+ * @throws LaunchCoinError `CONFIG_MISMATCH` listing every mismatch.
135
+ */
136
+ export function assertLaunchCoinConfig(config, owner, spec) {
137
+ const problems = essentialConfigProblems(config);
138
+ if (!config.feeClaimer.equals(owner))
139
+ problems.push('fee claimer is not the owner wallet');
140
+ if (!config.leftoverReceiver.equals(owner))
141
+ problems.push('leftover receiver is not the owner');
142
+ if (config.creatorTradingFeePercentage !== FEES.forgeCreatorSharePct) {
143
+ problems.push(`creator trading fee is not ${FEES.forgeCreatorSharePct}%`);
144
+ }
145
+ if (!(config.creatorPermanentLockedLiquidityPercentage > 0)) {
146
+ problems.push('creator has no permanently locked liquidity');
147
+ }
148
+ problems.push(...onchainConfigMismatches(config, 'launchpadCoin', {
149
+ owner,
150
+ ...(spec !== undefined ? { spec } : {}),
151
+ }));
124
152
  if (problems.length > 0) {
125
153
  fail('CONFIG_MISMATCH', `config is not a valid launchpad coin config: ${problems.join('; ')}`);
126
154
  }
@@ -231,7 +259,8 @@ async function loadLaunchState(connection, accounts, checkConfig) {
231
259
  * Quotes the first buy on the fresh pool at the minimum fee (exact: nothing else can trade before
232
260
  * it, the pool is created in the same transaction) and assembles the transaction: nonce advance,
233
261
  * compute budget, the buyer's WSOL account (idempotent), then the SDK instructions of
234
- * `createPoolWithFirstBuy` (proven call pattern of devnet test 3), without referral.
262
+ * `createPoolWithFirstBuy` (proven call pattern of devnet test 3), without referral, then the
263
+ * trailing instructions.
235
264
  */
236
265
  async function assembleLaunch(input) {
237
266
  const { buyer, nonce } = input;
@@ -273,7 +302,7 @@ async function assembleLaunch(input) {
273
302
  if (quote && !createsAssociatedAccount(sdkTx.instructions, buyerWsol)) {
274
303
  instructions.push(createAssociatedTokenAccountIdempotentInstruction(buyer, buyerWsol, buyer, NATIVE_MINT, TOKEN_PROGRAM_ID));
275
304
  }
276
- instructions.push(...sdkTx.instructions);
305
+ instructions.push(...sdkTx.instructions, ...(input.trailingInstructions ?? []));
277
306
  const transaction = new Transaction({ feePayer: buyer, recentBlockhash: input.recentBlockhash });
278
307
  if (input.lastValidBlockHeight !== null) {
279
308
  transaction.lastValidBlockHeight = input.lastValidBlockHeight;
@@ -332,9 +361,11 @@ async function computeLaunchCosts(input) {
332
361
  const rentPayerCosts = poolAccountsRentLamports +
333
362
  METAPLEX_CREATE_METADATA_FEE_LAMPORTS +
334
363
  input.poolCreationFeeLamports;
364
+ const platformFee = input.platformFeeLamports ?? 0n;
335
365
  const ownerTotalLamports = firstBuy +
336
366
  networkFeeLamports +
337
367
  ownerTokenAccountRentLamports +
368
+ platformFee +
338
369
  (input.rentPayerIsOwner ? rentPayerCosts : 0n);
339
370
  return {
340
371
  firstBuyLamports: firstBuy,
@@ -346,6 +377,7 @@ async function computeLaunchCosts(input) {
346
377
  ownerTemporaryLamports,
347
378
  networkFeeLamports,
348
379
  networkFeeSource,
380
+ ...(input.platformFeeLamports !== undefined ? { platformFeeLamports: platformFee } : {}),
349
381
  ownerTotalLamports,
350
382
  ownerRequiredBalanceLamports: ownerTotalLamports + ownerTemporaryLamports,
351
383
  creatorTotalLamports: input.rentPayerIsOwner ? 0n : rentPayerCosts,
@@ -406,7 +438,7 @@ export async function buildLaunchCoinTx(params) {
406
438
  const pool = deriveDbcPoolAddress(SOL_QUOTE_MINT, mint, config);
407
439
  const rentPayer = rentPayerKind === 'owner' ? owner : creator;
408
440
  // 2. On-chain state: config (this owner's launchpad coin config), pool and mint not existing.
409
- const { poolConfig, client, program } = await loadLaunchState(connection, { config, pool, mint }, (decoded) => assertLaunchCoinConfig(decoded, owner));
441
+ const { poolConfig, client, program } = await loadLaunchState(connection, { config, pool, mint }, (decoded) => assertLaunchCoinConfig(decoded, owner, params.spec));
410
442
  const poolCreationFeeLamports = toBigInt(poolConfig.poolCreationFee);
411
443
  if (rentPayerKind === 'creator' && poolCreationFeeLamports !== 0n) {
412
444
  fail('RENT_PAYER_REFUSED', `the creator wallet cannot pay the rent on a config with a pool creation fee (${poolCreationFeeLamports} lamports): it would pay the client's own fee`);
@@ -511,34 +543,49 @@ export async function buildLaunchCoinTx(params) {
511
543
  };
512
544
  }
513
545
  /**
514
- * Checks that an on-chain config can launch a user coin with a first buy at the minimum fee: SOL
515
- * quote, SPL token program for both tokens, `enableFirstSwapWithMinFee`. Nothing about who owns
516
- * the config or the creator share: the template checks the config address itself.
546
+ * Checks that an on-chain config is a FORGE launchpad config a user coin can be launched on:
547
+ * the essential checks (SOL quote, SPL token program for both tokens, `enableFirstSwapWithMinFee`,
548
+ * fee scheduler, DAMM v2 at exactly 10 SOL), then EVERY field compared with what core builds for
549
+ * a launchpad config (`onchainConfigMismatches`): liquidity split following the creator share,
550
+ * immutable metadata, the exact fee schedule, no migration fee, no locked vesting, FORGE's supply
551
+ * and curve, timestamp activation. `expected` pins the owner wallet (fee claimer and leftover
552
+ * receiver), the pool creation fee, or the whole spec; without it the spec fields only have to be
553
+ * within the client bounds and the leftover receiver must be the fee claimer.
517
554
  *
518
555
  * @throws LaunchCoinError `CONFIG_MISMATCH` listing every mismatch.
519
556
  */
520
- export function assertUserCoinConfig(config) {
521
- const problems = [];
522
- if (!config.quoteMint.equals(SOL_QUOTE_MINT))
523
- problems.push('quote mint is not SOL');
524
- if (config.quoteTokenFlag !== 0)
525
- problems.push('quote token is not an SPL token');
526
- if (config.tokenType !== TokenType.SPLToken)
527
- problems.push('base token type is not SPL token');
528
- if (config.enableFirstSwapWithMinFee !== 1) {
529
- problems.push('first swap with minimum fee is disabled (the first buy would pay the anti-sniper fee)');
530
- }
557
+ export function assertUserCoinConfig(config, expected = {}) {
558
+ const problems = [
559
+ ...essentialConfigProblems(config),
560
+ ...onchainConfigMismatches(config, 'launchpad', expected),
561
+ ];
531
562
  if (problems.length > 0) {
532
563
  fail('CONFIG_MISMATCH', `config cannot launch a user coin: ${problems.join('; ')}`);
533
564
  }
534
565
  }
566
+ /**
567
+ * The platform fee wallet must exist and be rent-exempt: a SOL transfer to an account below
568
+ * its rent-exempt minimum fails, and the whole launch with it.
569
+ *
570
+ * @throws LaunchCoinError `PLATFORM_FEE_WALLET_NOT_READY`.
571
+ */
572
+ async function assertPlatformFeeWalletLive(connection, wallet) {
573
+ const info = await connection.getAccountInfo(wallet, 'confirmed');
574
+ const minimum = BigInt(await connection.getMinimumBalanceForRentExemption(info?.data.length ?? 0));
575
+ if (!info || BigInt(info.lamports) < minimum) {
576
+ fail('PLATFORM_FEE_WALLET_NOT_READY', 'FORGE_PLATFORM_FEE_WALLET must exist and be rent-exempt, otherwise the launch fails on-chain');
577
+ }
578
+ }
535
579
  /**
536
580
  * Builds the launch transaction of a coin created by a user on the client's launchpad config:
537
581
  * pool creation + the user's first buy at the minimum fee (no referral), with a compute unit limit
538
582
  * (and a priority fee when given) and a recent blockhash. The user is pool creator, rent payer,
539
- * fee payer and buyer; the mint is the only other signer. Rent is read from the cluster.
583
+ * fee payer and buyer; the mint is the only other signer. Rent is read from the cluster. With a
584
+ * first buy, the last instruction transfers the platform fee (`platformFeeLamports(firstBuy)`) to
585
+ * `FORGE_PLATFORM_FEE_WALLET` (see `chargePlatformFee`).
540
586
  *
541
- * @throws LaunchCoinError (typed `code`).
587
+ * @throws LaunchCoinError (typed `code`; `PLATFORM_FEE_WALLET_NOT_READY` when the fee wallet is
588
+ * not rent-exempt), or ForgeAddressError when `FORGE_PLATFORM_FEE_WALLET` is missing.
542
589
  */
543
590
  export async function buildUserCoinLaunchTx(params) {
544
591
  const { connection, config, wallet } = params;
@@ -568,9 +615,22 @@ export async function buildUserCoinLaunchTx(params) {
568
615
  ]);
569
616
  const baseMint = mint;
570
617
  const pool = deriveDbcPoolAddress(SOL_QUOTE_MINT, baseMint, config);
618
+ // FORGE platform fee on the first buy (rounded up), to FORGE_PLATFORM_FEE_WALLET only.
619
+ const platformFee = params.chargePlatformFee === false || params.firstBuyLamports <= 0n
620
+ ? 0n
621
+ : platformFeeLamports(params.firstBuyLamports, PLATFORM_FEE_BPS);
622
+ const platformFeeWallet = platformFee > 0n ? getPlatformFeeWallet(params.env) : null;
623
+ if (platformFeeWallet &&
624
+ (platformFeeWallet.equals(baseMint) ||
625
+ platformFeeWallet.equals(config) ||
626
+ platformFeeWallet.equals(pool))) {
627
+ fail('INVALID_INPUT', 'FORGE_PLATFORM_FEE_WALLET must not be the mint, the config or the pool');
628
+ }
571
629
  // 2. On-chain state.
572
- const { poolConfig, client, program } = await loadLaunchState(connection, { config, pool, mint: baseMint }, assertUserCoinConfig);
630
+ const { poolConfig, client, program } = await loadLaunchState(connection, { config, pool, mint: baseMint }, (decoded) => assertUserCoinConfig(decoded, params.expectedConfig));
573
631
  const poolCreationFeeLamports = toBigInt(poolConfig.poolCreationFee);
632
+ if (platformFeeWallet)
633
+ await assertPlatformFeeWalletLive(connection, platformFeeWallet);
574
634
  // 3. Recent blockhash: the user signs right away, no durable nonce.
575
635
  const latest = await connection.getLatestBlockhash('confirmed');
576
636
  // 4. Quote and instructions.
@@ -591,6 +651,16 @@ export async function buildUserCoinLaunchTx(params) {
591
651
  nonce: null,
592
652
  recentBlockhash: latest.blockhash,
593
653
  lastValidBlockHeight: latest.lastValidBlockHeight,
654
+ // Last, like on a swap (docs/METEORA.md, "The platform fee").
655
+ trailingInstructions: platformFeeWallet
656
+ ? [
657
+ SystemProgram.transfer({
658
+ fromPubkey: wallet,
659
+ toPubkey: platformFeeWallet,
660
+ lamports: platformFee,
661
+ }),
662
+ ]
663
+ : [],
594
664
  });
595
665
  // 5. Signers: exactly the wallet (fee payer, first) and the mint.
596
666
  const signers = checkSigners(transaction, [wallet, baseMint], wallet);
@@ -611,6 +681,7 @@ export async function buildUserCoinLaunchTx(params) {
611
681
  firstBuyLamports: params.firstBuyLamports,
612
682
  poolCreationFeeLamports,
613
683
  rentPayerIsOwner: true,
684
+ platformFeeLamports: platformFee,
614
685
  });
615
686
  return {
616
687
  transaction,
@@ -626,6 +697,8 @@ export async function buildUserCoinLaunchTx(params) {
626
697
  expectedTokensOut: quote ? quote.tokensOut : 0n,
627
698
  minimumTokensOut: quote ? quote.minimumTokensOut : 0n,
628
699
  slippageBps,
700
+ platformFeeLamports: platformFee,
701
+ platformFeeWallet,
629
702
  costs,
630
703
  recentBlockhash: latest.blockhash,
631
704
  lastValidBlockHeight: latest.lastValidBlockHeight,
@@ -0,0 +1,207 @@
1
+ import { PublicKey, Transaction, type Connection } from '@solana/web3.js';
2
+ import { type PoolState as DammV2PoolState, type PositionState as DammV2PositionState } from '@meteora-ag/cp-amm-sdk';
3
+ import { type AddressEnv } from './addresses.js';
4
+ import { type ClaimOptions } from './claims.js';
5
+ import { type DbcPoolState, type ReadOptions, type TokenAmounts } from './reads.js';
6
+ export type { DammV2PoolState, DammV2PositionState };
7
+ /** DAMM v2 (cp-amm) program id, from the pinned SDK. */
8
+ export declare const DAMM_V2_PROGRAM_ID: PublicKey;
9
+ /** Anchor discriminator of `claim_position_fee` (sha256("global:claim_position_fee")[0..8]). */
10
+ export declare const CLAIM_POSITION_FEE_DISCRIMINATOR: Buffer<ArrayBuffer>;
11
+ /** Account indexes of `claim_position_fee` (cp-amm SDK 1.5.1 IDL). */
12
+ export declare const CLAIM_POSITION_FEE_ACCOUNTS: {
13
+ readonly poolAuthority: 0;
14
+ readonly pool: 1;
15
+ readonly position: 2;
16
+ readonly tokenAAccount: 3;
17
+ readonly tokenBAccount: 4;
18
+ readonly tokenAVault: 5;
19
+ readonly tokenBVault: 6;
20
+ readonly tokenAMint: 7;
21
+ readonly tokenBMint: 8;
22
+ readonly positionNftAccount: 9;
23
+ readonly owner: 10;
24
+ readonly tokenAProgram: 11;
25
+ readonly tokenBProgram: 12;
26
+ readonly eventAuthority: 13;
27
+ readonly program: 14;
28
+ };
29
+ /** `tokenAFlag` / `tokenBFlag` of a DAMM v2 pool: 0 = SPL Token, 1 = Token-2022. */
30
+ export declare function dammV2TokenProgram(flag: number): PublicKey;
31
+ export interface MigratedDammV2Pool {
32
+ /** The DAMM v2 pool the DBC pool migrated to. */
33
+ dammPool: PublicKey;
34
+ /** The DAMM v2 config of the migration fee option (`DAMM_V2_MIGRATION_FEE_ADDRESS`). */
35
+ dammConfig: PublicKey;
36
+ }
37
+ /**
38
+ * Pure: the DAMM v2 pool of a migrated DBC pool, derived like the SDK's `migrateToDammV2`
39
+ * (`deriveDammV2PoolAddress(dammConfig, baseMint, quoteMint)` with the DAMM v2 config the SDK
40
+ * pins for the config's `migrationFeeOption`, `DAMM_V2_MIGRATION_FEE_ADDRESS`). Null when the
41
+ * pool has not migrated yet, migrates to DAMM v1, or has a migration fee option the pinned SDK
42
+ * does not know (FORGE configs use `FixedBps25`, D27).
43
+ */
44
+ export declare function migratedDammV2Pool(state: Pick<DbcPoolState, 'virtualPool' | 'config'>): MigratedDammV2Pool | null;
45
+ /** A DAMM v2 position: the account, the NFT that owns it and the NFT's token account. */
46
+ export interface DammV2PositionRef {
47
+ position: PublicKey;
48
+ nftMint: PublicKey;
49
+ positionNftAccount: PublicKey;
50
+ }
51
+ export interface DammV2PositionSummary extends DammV2PositionRef {
52
+ /** DAMM v2 pool of the position. */
53
+ dammPool: PublicKey;
54
+ liquidity: {
55
+ unlocked: bigint;
56
+ vested: bigint;
57
+ permanentLocked: bigint;
58
+ };
59
+ /** Fees claimable now: `base` = token A (the coin), `quote` = token B (SOL), raw units. */
60
+ pendingFees: TokenAmounts;
61
+ }
62
+ /** Pure: unclaimed fees of a position (SDK `getUnClaimLpFee`), as bigint. */
63
+ export declare function positionPendingFees(poolState: DammV2PoolState, positionState: DammV2PositionState): TokenAmounts;
64
+ /** Pure: summary of one position of `poolState`. */
65
+ export declare function summarizePosition(poolState: DammV2PoolState, ref: DammV2PositionRef, positionState: DammV2PositionState): DammV2PositionSummary;
66
+ /** Pure: sum of the pending fees of `positions`. */
67
+ export declare function sumPendingFees(positions: readonly DammV2PositionSummary[]): TokenAmounts;
68
+ /** Fetches a DAMM v2 pool. @throws ClaimError `damm_pool_not_found` when the account is missing. */
69
+ export declare function getDammV2PoolState(connection: Connection, dammPool: PublicKey, options?: ReadOptions): Promise<DammV2PoolState>;
70
+ /**
71
+ * The migrated DAMM v2 pool of a DBC pool and the creator's positions on it, with their fees.
72
+ * Null when the pool has not migrated (see `migratedDammV2Pool`).
73
+ */
74
+ export interface CreatorDammV2Positions {
75
+ /** The DBC pool. */
76
+ pool: PublicKey;
77
+ dammPool: PublicKey;
78
+ dammConfig: PublicKey;
79
+ creator: PublicKey;
80
+ /** Positions whose NFT the creator holds, sorted by address. */
81
+ positions: DammV2PositionSummary[];
82
+ /** Sum of the positions' pending fees. */
83
+ claimable: TokenAmounts;
84
+ }
85
+ export interface FindCreatorPositionsParams {
86
+ /** The DBC pool, or its already-read state. */
87
+ pool: PublicKey | DbcPoolState;
88
+ /** The pool creator (must match the pool state). */
89
+ creator: PublicKey;
90
+ }
91
+ /**
92
+ * Finds the creator's positions on the migrated DAMM v2 pool of a DBC pool (SDK
93
+ * `getUserPositionByPool`: the Token-2022 accounts of the creator holding one NFT, then the
94
+ * position of each), checked again (pool, NFT held by the creator) and sorted by address.
95
+ * Returns null when the pool has not migrated to DAMM v2.
96
+ * @throws ClaimError `not_pool_creator`, `damm_pool_not_found`, `damm_pool_mismatch`.
97
+ * @throws DbcReadError when the DBC pool or its config does not exist.
98
+ */
99
+ export declare function findCreatorDammV2Positions(connection: Connection, params: FindCreatorPositionsParams, options?: ReadOptions): Promise<CreatorDammV2Positions | null>;
100
+ /** Lamport costs of a position fee claim, as bigint. */
101
+ export interface PositionClaimCosts {
102
+ /** Signature fees (signers x 5000 lamports; no priority fee), paid by the payer. */
103
+ networkFeeLamports: bigint;
104
+ /**
105
+ * Rent of the receiver's coin account, created on the first claim of a pool (paid by the
106
+ * payer, kept in that account, which the receiver owns). 0 when it already exists.
107
+ */
108
+ receiverTokenAccountRentLamports: bigint;
109
+ /**
110
+ * Rent of the temporary WSOL account the payer funds: closed to the receiver with the fees, it
111
+ * reaches the receiver as lamports (not refunded to the payer). 0 when the account exists.
112
+ */
113
+ unwrapAccountRentLamports: bigint;
114
+ /** Everything the payer's balance decreases by: the three amounts above. */
115
+ payerTotalLamports: bigint;
116
+ /** Balance the payer needs before signing (same as the total: nothing is refunded). */
117
+ payerRequiredBalanceLamports: bigint;
118
+ }
119
+ export interface PositionClaimSummary {
120
+ kind: 'position';
121
+ /** The DBC pool. */
122
+ pool: PublicKey;
123
+ dammPool: PublicKey;
124
+ dammConfig: PublicKey;
125
+ baseMint: PublicKey;
126
+ /** The creator: owner of the positions, signs the claim. */
127
+ claimer: PublicKey;
128
+ receiver: PublicKey;
129
+ /** Fee payer of the transaction. */
130
+ payer: PublicKey;
131
+ /** The positions claimed (sorted by address), with their fees when the claim was built. */
132
+ positions: DammV2PositionSummary[];
133
+ /** Sum of the positions' pending fees when the claim was built (a lower bound at execution). */
134
+ claimable: TokenAmounts;
135
+ /**
136
+ * SOL the receiver gets from the transaction, lamports: the claimable SOL fees plus the
137
+ * lamports of the temporary WSOL account closed to it (its rent, and the WSOL it already held).
138
+ * A lower bound: the claim is uncapped and fees accrue until it executes. Never exact
139
+ * (`quoteIsExact` is always false).
140
+ */
141
+ receiverQuoteLamports: bigint;
142
+ quoteIsExact: false;
143
+ /**
144
+ * WSOL the creator's temporary account already held when the transaction was built (anyone
145
+ * can send some to it): it reaches the receiver with the close, counted in
146
+ * `receiverQuoteLamports`. Zero when the account is empty or new.
147
+ */
148
+ existingWsolLamports: bigint;
149
+ /** Zero costs when there is no transaction. */
150
+ costs: PositionClaimCosts;
151
+ /** `receiverQuoteLamports - costs.payerTotalLamports`. Coin fees are not valued. */
152
+ netLamports: bigint;
153
+ }
154
+ export type PositionClaimSkipReason = 'no_position' | 'nothing_to_claim' | 'not_worthwhile';
155
+ export interface PositionClaimTxResult {
156
+ /** Unsigned claim transaction, or `null` when skipped (see `skipReason`). */
157
+ transaction: Transaction | null;
158
+ lastValidBlockHeight: number | null;
159
+ skipReason: PositionClaimSkipReason | null;
160
+ summary: PositionClaimSummary;
161
+ }
162
+ export interface CreatorPositionClaimParams {
163
+ /** The DBC pool (the DAMM v2 pool is derived from it). */
164
+ pool: PublicKey;
165
+ /** Pool creator and owner of the positions; signs the claim. */
166
+ creator: PublicKey;
167
+ /** Where the fees go. */
168
+ receiver: PublicKey;
169
+ /** When set, `receiver` must equal it (`receiver_mismatch` otherwise). */
170
+ expectedReceiver?: PublicKey;
171
+ /** Fee payer; defaults to the creator. */
172
+ payer?: PublicKey;
173
+ /**
174
+ * Positions to claim (addresses). Each must belong to the migrated pool and be held by the
175
+ * creator. Default: every position of the creator on that pool.
176
+ */
177
+ positions?: readonly PublicKey[];
178
+ }
179
+ /** True when the claim collects more SOL than it costs, by more than `minNetLamports`. */
180
+ export declare function isPositionClaimWorthwhile(summary: PositionClaimSummary, minNetLamports?: bigint): boolean;
181
+ /**
182
+ * Builds the claim of the creator's DAMM v2 position fees of a migrated DBC pool to `receiver`
183
+ * (see the module comment for the transaction).
184
+ * @throws ClaimError when `creator` is not the pool creator, the pool is not SOL-quoted or not
185
+ * migrated to DAMM v2 (`not_migrated`), the receiver is invalid or differs from
186
+ * `expectedReceiver`, a position is missing, on another pool or not held by the creator, or
187
+ * the creator's WSOL account holds WSOL while the receiver is someone else.
188
+ * @throws DbcReadError when the DBC pool or its config does not exist.
189
+ */
190
+ export declare function buildCreatorPositionFeeClaimTx(connection: Connection, params: CreatorPositionClaimParams, options?: ClaimOptions): Promise<PositionClaimTxResult>;
191
+ export interface ForgeCreatorPositionClaimParams {
192
+ pool: PublicKey;
193
+ /** FORGE creator wallet of the launchpad (the pool creator); signs the claim. */
194
+ creator: PublicKey;
195
+ /** Fee payer; defaults to the creator. */
196
+ payer?: PublicKey;
197
+ positions?: readonly PublicKey[];
198
+ }
199
+ /**
200
+ * The position fee claim of a FORGE creator wallet: the receiver is always the FORGE multisig
201
+ * vault (`FORGE_MULTISIG_VAULT`, SECURITY.md §5), read from `env` (defaults to `process.env`).
202
+ * @throws ForgeAddressError when `FORGE_MULTISIG_VAULT` is missing or invalid; as
203
+ * `buildCreatorPositionFeeClaimTx` otherwise.
204
+ */
205
+ export declare function buildForgeCreatorPositionFeeClaimTx(connection: Connection, params: ForgeCreatorPositionClaimParams, options?: ClaimOptions & {
206
+ env?: AddressEnv;
207
+ }): Promise<PositionClaimTxResult>;