@forgedevv/core 0.2.0 → 0.3.1

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/reads.js CHANGED
@@ -165,6 +165,19 @@ export async function readCoin(connection, pool, options = {}) {
165
165
  * @throws DbcReadError when the config does not exist.
166
166
  */
167
167
  export async function listCoinsOfConfig(connection, config, options = {}) {
168
+ return (await listPoolStatesOfConfig(connection, config, options)).map(summarizeCoin);
169
+ }
170
+ /**
171
+ * The decoded pools of a config with the config itself (what `listCoinsOfConfig` summarizes):
172
+ * one `getPoolsByConfig` scan (one `getProgramAccounts` per pool account variant, filtered on the
173
+ * config at offset 72) and one config read. A pool whose decoded `config` is not `config` is
174
+ * dropped (a wrong RPC answer is never shown). Sorted by pool address.
175
+ *
176
+ * The scan returns every pool of the config: callers should cache it (the FORGE RPC relay serves
177
+ * it to server-token callers only, with a response size cap).
178
+ * @throws DbcReadError when the config does not exist.
179
+ */
180
+ export async function listPoolStatesOfConfig(connection, config, options = {}) {
168
181
  const state = stateService(connection, options.commitment ?? READ_COMMITMENT);
169
182
  const configState = await retryRpc(() => state.getPoolConfig(config), options.retry);
170
183
  if (!configState) {
@@ -173,8 +186,32 @@ export async function listCoinsOfConfig(connection, config, options = {}) {
173
186
  const pools = await retryRpc(() => state.getPoolsByConfig(config), options.retry);
174
187
  return pools
175
188
  .filter((p) => p.account.poolState.config.equals(config))
176
- .map((p) => summarizeCoin({ address: p.publicKey, virtualPool: p.account, config: configState }))
177
- .sort((a, b) => compareStrings(a.pool.toBase58(), b.pool.toBase58()));
189
+ .map((p) => ({ address: p.publicKey, virtualPool: p.account, config: configState }))
190
+ .sort((a, b) => compareStrings(a.address.toBase58(), b.address.toBase58()));
191
+ }
192
+ /** 2^128: a pool's `sqrtPrice` is a Q64.64 number, so its square is a Q128.128 price. */
193
+ const Q128 = 1n << 128n;
194
+ /** Fixed-point scale used to turn the bigint price into a float without losing small prices. */
195
+ const SPOT_PRICE_SCALE = 10n ** 18n;
196
+ /**
197
+ * Pure: spot price of one whole base token (the coin) in whole quote tokens (SOL for FORGE pools),
198
+ * from the pool's `sqrtPrice`: `sqrtPrice^2 / 2^128` quote units per base unit (the price the
199
+ * curve is at, before fees), times `10^(baseDecimals - quoteDecimals)`. A float, for display only:
200
+ * quotes and transactions use the exact bigint math of `quoteSwap`.
201
+ */
202
+ export function poolSpotPrice(sqrtPrice, baseDecimals, quoteDecimals = 9) {
203
+ const sqrt = toBigint(sqrtPrice);
204
+ if (sqrt <= 0n)
205
+ return 0;
206
+ const shift = baseDecimals - quoteDecimals;
207
+ let numerator = sqrt * sqrt * SPOT_PRICE_SCALE;
208
+ let denominator = Q128;
209
+ if (shift >= 0)
210
+ numerator *= 10n ** BigInt(shift);
211
+ else
212
+ denominator *= 10n ** BigInt(-shift);
213
+ const scaled = numerator / denominator;
214
+ return Number(scaled) / Number(SPOT_PRICE_SCALE);
178
215
  }
179
216
  /**
180
217
  * Byte offset of `creator` in a DBC pool account (`VirtualPool` and `TransferHookPool`: 8-byte
package/dist/swap.d.ts CHANGED
@@ -5,7 +5,8 @@
5
5
  * One transaction contains, in this order:
6
6
  *
7
7
  * 1. compute budget: `setComputeUnitLimit`, then `setComputeUnitPrice` (only when a priority fee
8
- * is given);
8
+ * is given: a fixed `priorityFeeMicroLamports`, or a `priority` setting whose price is read
9
+ * from the recent fees of the swap's writable accounts and bounded, see priority-fee.ts);
9
10
  * 2. token accounts: the user's wrapped SOL ATA is always created (idempotent) and, on a buy, the
10
11
  * user's coin ATA when it does not exist yet;
11
12
  * 3. buy only: SOL transfer of `amountInLamports` into the wrapped SOL ATA + `SyncNative` (wrap);
@@ -31,20 +32,22 @@
31
32
  * site only trades its own coins;
32
33
  * - the platform fee wallet does not exist or is not rent-exempt
33
34
  * (`PLATFORM_FEE_WALLET_NOT_RENT_EXEMPT`): the whole swap would fail on-chain (devnet finding);
34
- * - the platform fee wallet is not exactly `FORGE_PLATFORM_FEE_WALLET`, or not a system account
35
- * (`PLATFORM_FEE_WALLET_MISMATCH`);
35
+ * - the platform fee wallet is not exactly `FORGE_PLATFORM_FEE_WALLET`, or not a system account,
36
+ * or `FORGE_PLATFORM_FEE_WALLET` is not the revenue wallet when the environment has
37
+ * `FORGE_REVENUE_WALLET` (`PLATFORM_FEE_WALLET_MISMATCH`);
36
38
  * - the referral account is not an initialized wrapped SOL token account, or is not held by
37
- * `FORGE_MULTISIG_VAULT` when the environment has it (`REFERRAL_ACCOUNT_INVALID`);
39
+ * `FORGE_REVENUE_WALLET` when the environment has it (`REFERRAL_ACCOUNT_INVALID`);
38
40
  * - the pool is not a plain SOL-quoted DBC pool (`UNSUPPORTED_POOL`; transfer-hook pools excluded);
39
41
  * - the serialized transaction exceeds 1,232 bytes (`TRANSACTION_TOO_LARGE`).
40
42
  *
41
43
  * Transaction size: measured on devnet (scripts/devnet-tests/reports/core-swap.md) at 819 bytes for a
42
44
  * buy and 797 bytes for a sell with this exact layout, far below the 1,232-byte limit (a buy that
43
- * also creates the coin ATA adds 10 bytes). If a future layout does not fit, the builders throw
44
- * `TRANSACTION_TOO_LARGE` unless the caller passes `addressLookupTables` (already fetched
45
- * `AddressLookupTableAccount`s, e.g. one holding the DBC program, the pool authority, the event
46
- * authority, the token programs, the wrapped SOL mint and the two FORGE fee accounts); the message
47
- * is then compiled against those tables and checked again.
45
+ * also creates the coin ATA adds 10 bytes, a priority fee 12 bytes for `setComputeUnitPrice`). If
46
+ * a future layout does not fit, the builders throw `TRANSACTION_TOO_LARGE` unless the caller
47
+ * passes `addressLookupTables` (already fetched `AddressLookupTableAccount`s, e.g. one holding the
48
+ * DBC program, the pool authority, the event authority, the token programs, the wrapped SOL mint
49
+ * and the two FORGE fee accounts); the message is then compiled against those tables and checked
50
+ * again.
48
51
  *
49
52
  * Layering: `fetchSwapContext` does all the network reads (with retries); everything else is pure
50
53
  * and unit-tested without network (`buildSwapTransaction`, `computeSwapQuote`, fee math).
@@ -53,6 +56,7 @@ import { PublicKey, TransactionInstruction, VersionedTransaction, type AddressLo
53
56
  import BN from 'bn.js';
54
57
  import { type PoolConfig, type VirtualPool } from '@meteora-ag/dynamic-bonding-curve-sdk';
55
58
  import { type AddressEnv } from './addresses.js';
59
+ import { type PriorityFeeQuote, type PriorityFeeSetting } from './priority-fee.js';
56
60
  /** Platform fee on swaps built by @forge/core, in bps (`FEES.platformFeeBps` from @forge/shared). */
57
61
  export declare const PLATFORM_FEE_BPS: number;
58
62
  /** Highest slippage tolerance accepted by the builders (25%), whether given in bps or as a minimum. */
@@ -284,7 +288,7 @@ export interface SwapContext extends SwapPoolState {
284
288
  export declare function assertPlatformFeeWalletReady(wallet: PublicKey, account: SwapAccountSnapshot | null, rentExemptMinimum: bigint, expectedWallet?: PublicKey | null): void;
285
289
  /**
286
290
  * Throws unless the referral account is an initialized wrapped SOL token account (SPL Token).
287
- * With `expectedOwner` (FORGE_MULTISIG_VAULT, when the environment has it), the token account must
291
+ * With `expectedOwner` (FORGE_REVENUE_WALLET, when the environment has it), the token account must
288
292
  * be held by that key: otherwise the referral share would be paid to someone else.
289
293
  */
290
294
  export declare function assertReferralAccountReady(address: PublicKey, account: SwapAccountSnapshot | null, expectedOwner?: PublicKey | null): void;
@@ -292,7 +296,7 @@ export declare function assertReferralAccountReady(address: PublicKey, account:
292
296
  export interface SwapFeeAccountExpectations {
293
297
  /** The platform fee transfer must go to exactly this key (FORGE_PLATFORM_FEE_WALLET). */
294
298
  platformFeeWallet?: PublicKey | null;
295
- /** The Meteora referral token account must be held by this key (FORGE_MULTISIG_VAULT). */
299
+ /** The Meteora referral token account must be held by this key (FORGE_REVENUE_WALLET). */
296
300
  referralOwner?: PublicKey | null;
297
301
  }
298
302
  /**
@@ -305,6 +309,13 @@ export interface SwapBuildOptions {
305
309
  computeUnitLimit?: number;
306
310
  /** Priority fee in micro-lamports per CU (default 0: no `setComputeUnitPrice` instruction). */
307
311
  priorityFeeMicroLamports?: number;
312
+ /**
313
+ * Priority fee read from the cluster (priority-fee.ts): the recent fees of the swap's writable
314
+ * accounts, bounded by this setting (`{}` = `DEFAULT_PRIORITY_FEE_POLICY`: at most 0.0005 SOL
315
+ * per swap). Only `buildBuyTx` / `buildSellTx` read it (they hold the connection); not with
316
+ * `priorityFeeMicroLamports`.
317
+ */
318
+ priority?: PriorityFeeSetting;
308
319
  /**
309
320
  * Address lookup tables to compile the v0 message with, if the transaction would exceed 1,232
310
321
  * bytes without them (it does not with the current layout). Fetch them with
@@ -324,6 +335,11 @@ export interface BuiltSwapTransaction {
324
335
  sizeBytes: number;
325
336
  computeUnitLimit: number;
326
337
  priorityFeeMicroLamports: number;
338
+ /**
339
+ * The priority fee: price, fee in lamports and its cap (`maxLamports`, the cost guard to show
340
+ * before signing), and where the price comes from (`fixed` without a `priority` setting).
341
+ */
342
+ priorityFee: PriorityFeeQuote;
327
343
  /** Base fee + priority fee, in lamports. */
328
344
  estimatedNetworkFeeLamports: bigint;
329
345
  /** Rent locked in a coin ATA created by a buy (0 when it already existed or on a sell). */
@@ -376,13 +392,15 @@ export interface FetchSwapContextParams {
376
392
  export declare function fetchSwapContext(p: FetchSwapContextParams): Promise<SwapContext>;
377
393
  /**
378
394
  * FORGE fee accounts used by the swap, resolved through addresses.ts (never hard-coded), and the
379
- * multisig vault that must hold the referral account (`null` when the environment does not have
380
- * `FORGE_MULTISIG_VAULT`: the owner check is then skipped).
395
+ * revenue wallet that must hold the referral account (`null` when the environment does not have
396
+ * `FORGE_REVENUE_WALLET`: the owner check is then skipped). When the environment has it, the
397
+ * platform fee wallet must be that same wallet (D34: the platform fee is revenue too), otherwise
398
+ * `PLATFORM_FEE_WALLET_MISMATCH`.
381
399
  */
382
400
  export declare function swapFeeAddresses(env?: AddressEnv): {
383
401
  platformFeeWallet: PublicKey;
384
402
  referralTokenAccount: PublicKey;
385
- multisigVault: PublicKey | null;
403
+ revenueWallet: PublicKey | null;
386
404
  };
387
405
  interface SwapCommonParams extends SwapBuildOptions {
388
406
  connection: Connection;
@@ -392,7 +410,8 @@ interface SwapCommonParams extends SwapBuildOptions {
392
410
  /**
393
411
  * Environment to read the FORGE addresses from (defaults to `process.env`):
394
412
  * `FORGE_PLATFORM_FEE_WALLET` and `FORGE_METEORA_REFERRAL_ACCOUNT` (required), and
395
- * `FORGE_MULTISIG_VAULT` (optional: when set, the referral account must be held by it).
413
+ * `FORGE_REVENUE_WALLET` (optional: when set, the referral account must be held by it and the
414
+ * platform fee wallet must be it).
396
415
  */
397
416
  env?: AddressEnv;
398
417
  commitment?: Commitment;
@@ -436,7 +455,7 @@ export declare function quoteSwap(params: QuoteSwapParams): Promise<SwapQuote>;
436
455
  /**
437
456
  * Startup / pre-build check of the FORGE fee accounts: the platform fee wallet must be a
438
457
  * rent-exempt system account and the referral account must be a wrapped SOL token account, held by
439
- * `FORGE_MULTISIG_VAULT` when the environment has it. Throws `SwapError`.
458
+ * `FORGE_REVENUE_WALLET` when the environment has it. Throws `SwapError`.
440
459
  */
441
460
  export declare function assertSwapFeeAccountsReady(params: {
442
461
  connection: Connection;
package/dist/swap.js CHANGED
@@ -5,7 +5,8 @@
5
5
  * One transaction contains, in this order:
6
6
  *
7
7
  * 1. compute budget: `setComputeUnitLimit`, then `setComputeUnitPrice` (only when a priority fee
8
- * is given);
8
+ * is given: a fixed `priorityFeeMicroLamports`, or a `priority` setting whose price is read
9
+ * from the recent fees of the swap's writable accounts and bounded, see priority-fee.ts);
9
10
  * 2. token accounts: the user's wrapped SOL ATA is always created (idempotent) and, on a buy, the
10
11
  * user's coin ATA when it does not exist yet;
11
12
  * 3. buy only: SOL transfer of `amountInLamports` into the wrapped SOL ATA + `SyncNative` (wrap);
@@ -31,20 +32,22 @@
31
32
  * site only trades its own coins;
32
33
  * - the platform fee wallet does not exist or is not rent-exempt
33
34
  * (`PLATFORM_FEE_WALLET_NOT_RENT_EXEMPT`): the whole swap would fail on-chain (devnet finding);
34
- * - the platform fee wallet is not exactly `FORGE_PLATFORM_FEE_WALLET`, or not a system account
35
- * (`PLATFORM_FEE_WALLET_MISMATCH`);
35
+ * - the platform fee wallet is not exactly `FORGE_PLATFORM_FEE_WALLET`, or not a system account,
36
+ * or `FORGE_PLATFORM_FEE_WALLET` is not the revenue wallet when the environment has
37
+ * `FORGE_REVENUE_WALLET` (`PLATFORM_FEE_WALLET_MISMATCH`);
36
38
  * - the referral account is not an initialized wrapped SOL token account, or is not held by
37
- * `FORGE_MULTISIG_VAULT` when the environment has it (`REFERRAL_ACCOUNT_INVALID`);
39
+ * `FORGE_REVENUE_WALLET` when the environment has it (`REFERRAL_ACCOUNT_INVALID`);
38
40
  * - the pool is not a plain SOL-quoted DBC pool (`UNSUPPORTED_POOL`; transfer-hook pools excluded);
39
41
  * - the serialized transaction exceeds 1,232 bytes (`TRANSACTION_TOO_LARGE`).
40
42
  *
41
43
  * Transaction size: measured on devnet (scripts/devnet-tests/reports/core-swap.md) at 819 bytes for a
42
44
  * buy and 797 bytes for a sell with this exact layout, far below the 1,232-byte limit (a buy that
43
- * also creates the coin ATA adds 10 bytes). If a future layout does not fit, the builders throw
44
- * `TRANSACTION_TOO_LARGE` unless the caller passes `addressLookupTables` (already fetched
45
- * `AddressLookupTableAccount`s, e.g. one holding the DBC program, the pool authority, the event
46
- * authority, the token programs, the wrapped SOL mint and the two FORGE fee accounts); the message
47
- * is then compiled against those tables and checked again.
45
+ * also creates the coin ATA adds 10 bytes, a priority fee 12 bytes for `setComputeUnitPrice`). If
46
+ * a future layout does not fit, the builders throw `TRANSACTION_TOO_LARGE` unless the caller
47
+ * passes `addressLookupTables` (already fetched `AddressLookupTableAccount`s, e.g. one holding the
48
+ * DBC program, the pool authority, the event authority, the token programs, the wrapped SOL mint
49
+ * and the two FORGE fee accounts); the message is then compiled against those tables and checked
50
+ * again.
48
51
  *
49
52
  * Layering: `fetchSwapContext` does all the network reads (with retries); everything else is pure
50
53
  * and unit-tested without network (`buildSwapTransaction`, `computeSwapQuote`, fee math).
@@ -55,6 +58,7 @@ import BN from 'bn.js';
55
58
  import { ActivationType, BaseFeeMode, DYNAMIC_BONDING_CURVE_PROGRAM_ID, DynamicBondingCurveIdl, TradeDirection, createDbcProgram, deriveDbcEventAuthority, deriveDbcPoolAuthority, getFeeMode, getTokenProgram, isRateLimiterApplied, swapQuote, } from '@meteora-ag/dynamic-bonding-curve-sdk';
56
59
  import { readOptionalAddress, requireAddresses } from './addresses.js';
57
60
  import { FEES } from './constants.js';
61
+ import { estimatePriorityFee, fixedPriorityFee, resolvePriorityFeePolicy, } from './priority-fee.js';
58
62
  // ---------------------------------------------------------------------------------------------
59
63
  // Constants
60
64
  // ---------------------------------------------------------------------------------------------
@@ -454,7 +458,7 @@ export function assertPlatformFeeWalletReady(wallet, account, rentExemptMinimum,
454
458
  }
455
459
  /**
456
460
  * Throws unless the referral account is an initialized wrapped SOL token account (SPL Token).
457
- * With `expectedOwner` (FORGE_MULTISIG_VAULT, when the environment has it), the token account must
461
+ * With `expectedOwner` (FORGE_REVENUE_WALLET, when the environment has it), the token account must
458
462
  * be held by that key: otherwise the referral share would be paid to someone else.
459
463
  */
460
464
  export function assertReferralAccountReady(address, account, expectedOwner) {
@@ -478,7 +482,7 @@ export function assertReferralAccountReady(address, account, expectedOwner) {
478
482
  if (!token.isInitialized || token.isFrozen)
479
483
  throw fail('is not usable (uninitialized or frozen)');
480
484
  if (expectedOwner && !token.owner.equals(expectedOwner)) {
481
- throw fail('is not held by FORGE_MULTISIG_VAULT');
485
+ throw fail('is not held by FORGE_REVENUE_WALLET');
482
486
  }
483
487
  }
484
488
  function toConfigKeys(allowedConfigs) {
@@ -529,6 +533,21 @@ function readBuildOptions(o) {
529
533
  throw invalid('addressLookupTables must be an array');
530
534
  return { computeUnitLimit, priorityFeeMicroLamports, addressLookupTables };
531
535
  }
536
+ /** Checks a `priority` setting (INVALID_PARAMS), and that no fixed price is given with it. */
537
+ function readPrioritySetting(o) {
538
+ if (o.priority === undefined)
539
+ return undefined;
540
+ if (o.priorityFeeMicroLamports !== undefined) {
541
+ throw invalid('Pass either priority or priorityFeeMicroLamports, not both');
542
+ }
543
+ try {
544
+ resolvePriorityFeePolicy(o.priority);
545
+ }
546
+ catch (error) {
547
+ throw invalid(`priority: ${error instanceof Error ? error.message : String(error)}`);
548
+ }
549
+ return o.priority;
550
+ }
532
551
  /**
533
552
  * Compiles the instructions into an unsigned v0 transaction paid by `payer` and checks its
534
553
  * serialized size (signature placeholders included) against 1,232 bytes.
@@ -573,6 +592,10 @@ export function compileSwapTransaction(args) {
573
592
  * the top of this file.
574
593
  */
575
594
  export function buildSwapTransaction(ctx, request, expected = {}) {
595
+ if (request.priority !== undefined) {
596
+ // The price of a `priority` setting is read from the cluster: buildBuyTx / buildSellTx.
597
+ throw invalid('buildSwapTransaction takes a fixed priorityFeeMicroLamports, not priority');
598
+ }
576
599
  const options = readBuildOptions(request);
577
600
  assertPlatformFeeWalletReady(ctx.platformFeeWallet, ctx.platformFeeWalletAccount, ctx.platformFeeWalletRentExemptMinimum, expected.platformFeeWallet);
578
601
  assertReferralAccountReady(ctx.referralTokenAccount, ctx.referralTokenAccountInfo, expected.referralOwner);
@@ -669,6 +692,7 @@ export function buildSwapTransaction(ctx, request, expected = {}) {
669
692
  sizeBytes,
670
693
  computeUnitLimit: options.computeUnitLimit,
671
694
  priorityFeeMicroLamports: options.priorityFeeMicroLamports,
695
+ priorityFee: fixedPriorityFee(options.computeUnitLimit, options.priorityFeeMicroLamports),
672
696
  estimatedNetworkFeeLamports,
673
697
  newCoinAccountRentLamports,
674
698
  requiredLamports,
@@ -833,15 +857,21 @@ export async function fetchSwapContext(p) {
833
857
  }
834
858
  /**
835
859
  * FORGE fee accounts used by the swap, resolved through addresses.ts (never hard-coded), and the
836
- * multisig vault that must hold the referral account (`null` when the environment does not have
837
- * `FORGE_MULTISIG_VAULT`: the owner check is then skipped).
860
+ * revenue wallet that must hold the referral account (`null` when the environment does not have
861
+ * `FORGE_REVENUE_WALLET`: the owner check is then skipped). When the environment has it, the
862
+ * platform fee wallet must be that same wallet (D34: the platform fee is revenue too), otherwise
863
+ * `PLATFORM_FEE_WALLET_MISMATCH`.
838
864
  */
839
865
  export function swapFeeAddresses(env) {
840
866
  const a = requireAddresses(['FORGE_PLATFORM_FEE_WALLET', 'FORGE_METEORA_REFERRAL_ACCOUNT'], env);
867
+ const revenueWallet = readOptionalAddress('FORGE_REVENUE_WALLET', env);
868
+ if (revenueWallet && !a.FORGE_PLATFORM_FEE_WALLET.equals(revenueWallet)) {
869
+ throw new SwapError('PLATFORM_FEE_WALLET_MISMATCH', 'FORGE_PLATFORM_FEE_WALLET must be the revenue wallet (FORGE_REVENUE_WALLET)', { details: { platformFeeWallet: a.FORGE_PLATFORM_FEE_WALLET.toBase58() } });
870
+ }
841
871
  return {
842
872
  platformFeeWallet: a.FORGE_PLATFORM_FEE_WALLET,
843
873
  referralTokenAccount: a.FORGE_METEORA_REFERRAL_ACCOUNT,
844
- multisigVault: readOptionalAddress('FORGE_MULTISIG_VAULT', env),
874
+ revenueWallet,
845
875
  };
846
876
  }
847
877
  function slippageOf(p) {
@@ -852,7 +882,8 @@ async function buildSide(side, amountIn, p) {
852
882
  assertU64(side === 'buy' ? 'amountInLamports' : 'amountInTokens', amountIn, 1n);
853
883
  const slippage = readSlippage(slippageOf(p));
854
884
  const options = readBuildOptions(p);
855
- const { platformFeeWallet, referralTokenAccount, multisigVault } = swapFeeAddresses(p.env);
885
+ const priority = readPrioritySetting(p);
886
+ const { platformFeeWallet, referralTokenAccount, revenueWallet } = swapFeeAddresses(p.env);
856
887
  if (p.allowedConfigs !== undefined)
857
888
  toConfigKeys(p.allowedConfigs);
858
889
  const ctx = await fetchSwapContext({
@@ -864,7 +895,25 @@ async function buildSide(side, amountIn, p) {
864
895
  ...(p.commitment ? { commitment: p.commitment } : {}),
865
896
  ...(p.allowedConfigs !== undefined ? { allowedConfigs: p.allowedConfigs } : {}),
866
897
  });
867
- return buildSwapTransaction(ctx, { side, amountIn, slippage, ...options }, { platformFeeWallet, referralOwner: multisigVault });
898
+ const expected = { platformFeeWallet, referralOwner: revenueWallet };
899
+ const built = buildSwapTransaction(ctx, { side, amountIn, slippage, ...options }, expected);
900
+ if (priority === undefined)
901
+ return built;
902
+ // Same swap with its price: read for the accounts it write-locks (the pool, its vaults and the
903
+ // FORGE fee accounts are shared by every trade), bounded by the setting.
904
+ const quote = await estimatePriorityFee({
905
+ connection: p.connection,
906
+ writableAccounts: writableStaticKeys(built.transaction),
907
+ computeUnitLimit: options.computeUnitLimit,
908
+ setting: priority,
909
+ });
910
+ const priced = buildSwapTransaction(ctx, { side, amountIn, slippage, ...options, priorityFeeMicroLamports: quote.microLamports }, expected);
911
+ return { ...priced, priorityFee: quote };
912
+ }
913
+ /** Writable static account keys of a compiled v0 transaction, in message order. */
914
+ function writableStaticKeys(transaction) {
915
+ const message = transaction.message;
916
+ return message.staticAccountKeys.filter((_, index) => message.isAccountWritable(index));
868
917
  }
869
918
  /** Unsigned buy transaction (SOL -> coin) with the FORGE referral and the platform fee. */
870
919
  export async function buildBuyTx(params) {
@@ -889,13 +938,13 @@ export async function quoteSwap(params) {
889
938
  /**
890
939
  * Startup / pre-build check of the FORGE fee accounts: the platform fee wallet must be a
891
940
  * rent-exempt system account and the referral account must be a wrapped SOL token account, held by
892
- * `FORGE_MULTISIG_VAULT` when the environment has it. Throws `SwapError`.
941
+ * `FORGE_REVENUE_WALLET` when the environment has it. Throws `SwapError`.
893
942
  */
894
943
  export async function assertSwapFeeAccountsReady(params) {
895
944
  const commitment = params.commitment ?? DEFAULT_COMMITMENT;
896
- const { platformFeeWallet, referralTokenAccount, multisigVault } = swapFeeAddresses(params.env);
945
+ const { platformFeeWallet, referralTokenAccount, revenueWallet } = swapFeeAddresses(params.env);
897
946
  const [platformInfo, referralInfo] = (await rpc('getMultipleAccountsInfo', () => params.connection.getMultipleAccountsInfo([platformFeeWallet, referralTokenAccount], commitment))).map(snapshot);
898
947
  const rentMinimum = BigInt(await rpc('getMinimumBalanceForRentExemption', () => params.connection.getMinimumBalanceForRentExemption(platformInfo?.data.length ?? 0, commitment)));
899
948
  assertPlatformFeeWalletReady(platformFeeWallet, platformInfo ?? null, rentMinimum, platformFeeWallet);
900
- assertReferralAccountReady(referralTokenAccount, referralInfo ?? null, multisigVault);
949
+ assertReferralAccountReady(referralTokenAccount, referralInfo ?? null, revenueWallet);
901
950
  }
package/dist/tx.d.ts CHANGED
@@ -182,6 +182,8 @@ export declare function readNonce(connection: Pick<Connection, 'getAccountInfo'>
182
182
  expectedAuthority?: PublicKey;
183
183
  commitment?: Commitment;
184
184
  }): Promise<NonceInfo>;
185
+ /** Compute unit limit of the nonce account creations when a priority fee is set. */
186
+ export declare const CREATE_NONCE_ACCOUNT_COMPUTE_UNITS = 10000;
185
187
  export interface BuildCreateNonceAccountTxParams {
186
188
  connection: Pick<Connection, 'getMinimumBalanceForRentExemption' | 'getLatestBlockhash'>;
187
189
  /** Pays the rent and the fee; signs. */
package/dist/tx.js CHANGED
@@ -390,6 +390,8 @@ export async function readNonce(connection, nonceAccount, options = {}) {
390
390
  lamportsPerSignature: BigInt(decoded.feeCalculator.lamportsPerSignature),
391
391
  };
392
392
  }
393
+ /** Compute unit limit of the nonce account creations when a priority fee is set. */
394
+ export const CREATE_NONCE_ACCOUNT_COMPUTE_UNITS = 10_000;
393
395
  /**
394
396
  * Unsigned transaction creating and initializing a durable nonce account
395
397
  * (`SystemProgram.createNonceAccount`). The rent is read from the cluster.
@@ -404,7 +406,7 @@ export async function buildCreateNonceAccountTx(params) {
404
406
  const transaction = new Transaction({ feePayer: params.payer, blockhash, lastValidBlockHeight });
405
407
  if (params.computeUnitPriceMicroLamports !== undefined) {
406
408
  transaction.add(...computeBudgetInstructions({
407
- units: 10_000,
409
+ units: CREATE_NONCE_ACCOUNT_COMPUTE_UNITS,
408
410
  microLamports: params.computeUnitPriceMicroLamports,
409
411
  }));
410
412
  }
@@ -475,7 +477,7 @@ export async function buildCreateNonceAccountWithSeedTx(params) {
475
477
  const nonceAccount = await deriveSeededNonceAccount(base, seed);
476
478
  const budget = params.computeUnitPriceMicroLamports !== undefined
477
479
  ? computeBudgetInstructions({
478
- units: 10_000,
480
+ units: CREATE_NONCE_ACCOUNT_COMPUTE_UNITS,
479
481
  microLamports: params.computeUnitPriceMicroLamports,
480
482
  })
481
483
  : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgedevv/core",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "FORGE transaction core: Meteora Dynamic Bonding Curve configs, pools, swaps, claims and on-chain checks. Published on npm as @forgedevv/core.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -47,9 +47,10 @@
47
47
  "registry": "https://registry.npmjs.org/"
48
48
  },
49
49
  "dependencies": {
50
- "@forge/shared": "npm:@forgedevv/shared@0.2.0",
50
+ "@forge/shared": "npm:@forgedevv/shared@0.3.1",
51
51
  "@meteora-ag/cp-amm-sdk": "1.5.1",
52
52
  "@meteora-ag/dynamic-bonding-curve-sdk": "1.5.13",
53
+ "@noble/hashes": "1.8.0",
53
54
  "@solana/spl-token": "0.4.15",
54
55
  "@solana/web3.js": "1.99.0",
55
56
  "bn.js": "5.2.5"