@vultisig/core-chain 2.18.0 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/chains/ton/address.d.ts +9 -0
  3. package/dist/chains/ton/address.d.ts.map +1 -1
  4. package/dist/chains/ton/address.js +13 -0
  5. package/dist/chains/ton/address.js.map +1 -1
  6. package/dist/chains/ton/staking.d.ts +104 -0
  7. package/dist/chains/ton/staking.d.ts.map +1 -0
  8. package/dist/chains/ton/staking.js +137 -0
  9. package/dist/chains/ton/staking.js.map +1 -0
  10. package/dist/coin/balance/getEvmChainBalances.d.ts +11 -0
  11. package/dist/coin/balance/getEvmChainBalances.d.ts.map +1 -0
  12. package/dist/coin/balance/getEvmChainBalances.js +57 -0
  13. package/dist/coin/balance/getEvmChainBalances.js.map +1 -0
  14. package/dist/swap/general/GeneralSwapProvider.d.ts +1 -1
  15. package/dist/swap/general/GeneralSwapProvider.d.ts.map +1 -1
  16. package/dist/swap/general/GeneralSwapProvider.js +2 -1
  17. package/dist/swap/general/GeneralSwapProvider.js.map +1 -1
  18. package/dist/swap/general/jupiter/JupiterSwapEnabledChains.d.ts +9 -0
  19. package/dist/swap/general/jupiter/JupiterSwapEnabledChains.d.ts.map +1 -0
  20. package/dist/swap/general/jupiter/JupiterSwapEnabledChains.js +9 -0
  21. package/dist/swap/general/jupiter/JupiterSwapEnabledChains.js.map +1 -0
  22. package/dist/swap/general/jupiter/api/getJupiterSwapQuote.d.ts +25 -0
  23. package/dist/swap/general/jupiter/api/getJupiterSwapQuote.d.ts.map +1 -0
  24. package/dist/swap/general/jupiter/api/getJupiterSwapQuote.js +112 -0
  25. package/dist/swap/general/jupiter/api/getJupiterSwapQuote.js.map +1 -0
  26. package/dist/swap/general/jupiter/api/jupiterFeeAta.d.ts +48 -0
  27. package/dist/swap/general/jupiter/api/jupiterFeeAta.d.ts.map +1 -0
  28. package/dist/swap/general/jupiter/api/jupiterFeeAta.js +115 -0
  29. package/dist/swap/general/jupiter/api/jupiterFeeAta.js.map +1 -0
  30. package/dist/swap/general/jupiter/config.d.ts +13 -0
  31. package/dist/swap/general/jupiter/config.d.ts.map +1 -0
  32. package/dist/swap/general/jupiter/config.js +52 -0
  33. package/dist/swap/general/jupiter/config.js.map +1 -0
  34. package/dist/swap/quote/findSwapQuote.d.ts +1 -1
  35. package/dist/swap/quote/findSwapQuote.d.ts.map +1 -1
  36. package/dist/swap/quote/findSwapQuote.js +33 -1
  37. package/dist/swap/quote/findSwapQuote.js.map +1 -1
  38. package/dist/swap/utils/getSwapExplorerUrl/index.d.ts +2 -2
  39. package/dist/swap/utils/getSwapExplorerUrl/index.d.ts.map +1 -1
  40. package/dist/swap/utils/getSwapExplorerUrl/index.js +11 -2
  41. package/dist/swap/utils/getSwapExplorerUrl/index.js.map +1 -1
  42. package/package.json +31 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @vultisig/core-chain
2
2
 
3
+ ## 2.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#894](https://github.com/vultisig/vultisig-sdk/pull/894) [`605fbba`](https://github.com/vultisig/vultisig-sdk/commit/605fbbaf107c553898f11f4f7eb6b56a59c01b9e) Thanks [@Ehsan-saradar](https://github.com/Ehsan-saradar)! - feat(swap): add Jupiter as a Solana same-chain swap provider with a VULT-scaled affiliate fee
8
+
9
+ `findSwapQuote` now offers Jupiter for on-Solana token pairs (SOL↔SPL, SPL↔SPL),
10
+ preferred over SwapKit/LiFi on a near-tie. Jupiter is Solana-only and same-chain
11
+ — it is never offered for any cross-chain route, and native SOL cross-chain swaps
12
+ stay on THORChain.
13
+
14
+ The Jupiter quote sends `platformFeeBps` = `max(0, 50 − vultTierDiscountBps)`
15
+ (the existing `getSwapAffiliateBps` value, shared with every other provider), and
16
+ the swap request sends `feeAccount` = the Associated Token Account of
17
+ `(owner = Vultisig fee wallet, mint = output mint)`. An idempotent
18
+ `createAssociatedTokenAccount` instruction for that fee ATA is prepended to the
19
+ returned transaction (Jupiter does not auto-create it). When the affiliate bps
20
+ floors to 0 (Ultimate-tier VULT holder), no platform fee or fee account is used.
21
+
22
+ New public surface: `swap/general/jupiter/*` (`getJupiterSwapQuote`,
23
+ `configureJupiter`, `jupiterSwapEnabledChains`) and `jupiter` added to
24
+ `generalSwapProviders` and the swap explorer providers.
25
+
26
+ - [#893](https://github.com/vultisig/vultisig-sdk/pull/893) [`552064c`](https://github.com/vultisig/vultisig-sdk/commit/552064cbfb7307867f9897734c010e856f8a08f9) Thanks [@Ehsan-saradar](https://github.com/Ehsan-saradar)! - Add TON nominator-pool staking support. `@vultisig/core-chain` gains a tonapi staking client (`chains/ton/staking`) — pool list, computed pool info, and account nominator positions — plus per-implementation deposit/withdraw comment resolution (`whales` → `Deposit`/`Withdraw`, `tf` → `d`/`w`), pool eligibility/capacity filters, and a `tonAddressToBounceable` helper that normalizes raw `0:` pool addresses to the bounceable `EQ…` form. `@vultisig/core-mpc` now forces TON transfers bounceable for any staking comment (via `isTonStakingComment`), so a rejected pool deposit/withdraw bounces back instead of being absorbed.
27
+
28
+ ### Patch Changes
29
+
30
+ - [#886](https://github.com/vultisig/vultisig-sdk/pull/886) [`baedd96`](https://github.com/vultisig/vultisig-sdk/commit/baedd96e9d75a9d73880a59503f95b527d692428) Thanks [@rcoderdev](https://github.com/rcoderdev)! - Add an EVM chain balance batching helper backed by Multicall3.
31
+
3
32
  ## 2.18.0
4
33
 
5
34
  ### Minor Changes
@@ -3,4 +3,13 @@
3
3
  * The toncenter v3 API requires raw addresses.
4
4
  */
5
5
  export declare const tonAddressToRaw: (userFriendlyAddress: string) => string;
6
+ /**
7
+ * Converts a raw TON address (`workchain:hex`) to the bounceable user-friendly
8
+ * form (`EQ…`). Staking-API pool addresses arrive in raw `0:` form, which the
9
+ * signer treats as non-bounceable — sending a deposit non-bounceable risks the
10
+ * pool absorbing (losing) a rejected transfer instead of bouncing it back, so
11
+ * pool destinations MUST be normalized to the bounceable form first. Inputs
12
+ * already in user-friendly form are returned re-encoded as bounceable.
13
+ */
14
+ export declare const tonAddressToBounceable: (address: string) => string;
6
15
  //# sourceMappingURL=address.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/ton/address.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,qBAAqB,MAAM,KAAG,MAM7D,CAAA"}
1
+ {"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/ton/address.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,qBAAqB,MAAM,KAAG,MAM7D,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,KAAG,MAIxD,CAAA"}
@@ -1,3 +1,4 @@
1
+ import { Address } from '@ton/core';
1
2
  import { fromBase64 } from '@vultisig/lib-utils/fromBase64';
2
3
  /** Converts base64url encoding to standard base64 so `Buffer.from` can decode it. */
3
4
  const fromBase64Url = (value) => {
@@ -14,4 +15,16 @@ export const tonAddressToRaw = (userFriendlyAddress) => {
14
15
  const hash = decoded.subarray(2, 34).toString('hex');
15
16
  return `${workchain}:${hash}`;
16
17
  };
18
+ /**
19
+ * Converts a raw TON address (`workchain:hex`) to the bounceable user-friendly
20
+ * form (`EQ…`). Staking-API pool addresses arrive in raw `0:` form, which the
21
+ * signer treats as non-bounceable — sending a deposit non-bounceable risks the
22
+ * pool absorbing (losing) a rejected transfer instead of bouncing it back, so
23
+ * pool destinations MUST be normalized to the bounceable form first. Inputs
24
+ * already in user-friendly form are returned re-encoded as bounceable.
25
+ */
26
+ export const tonAddressToBounceable = (address) => {
27
+ const parsed = address.includes(':') ? Address.parseRaw(address) : Address.parse(address);
28
+ return parsed.toString({ bounceable: true, testOnly: false, urlSafe: true });
29
+ };
17
30
  //# sourceMappingURL=address.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"address.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/ton/address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAA;AAE3D,qFAAqF;AACrF,MAAM,aAAa,GAAG,CAAC,KAAa,EAAU,EAAE;IAC9C,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAClE,OAAO,UAAU,CAAC,cAAc,CAAC,CAAA;AACnC,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,mBAA2B,EAAU,EAAE;IACrE,MAAM,OAAO,GAAG,aAAa,CAAC,mBAAmB,CAAC,CAAA;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IACnE,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAEpD,OAAO,GAAG,SAAS,IAAI,IAAI,EAAE,CAAA;AAC/B,CAAC,CAAA"}
1
+ {"version":3,"file":"address.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/ton/address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAA;AAE3D,qFAAqF;AACrF,MAAM,aAAa,GAAG,CAAC,KAAa,EAAU,EAAE;IAC9C,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAClE,OAAO,UAAU,CAAC,cAAc,CAAC,CAAA;AACnC,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,mBAA2B,EAAU,EAAE;IACrE,MAAM,OAAO,GAAG,aAAa,CAAC,mBAAmB,CAAC,CAAA;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IACnE,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAEpD,OAAO,GAAG,SAAS,IAAI,IAAI,EAAE,CAAA;AAC/B,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,OAAe,EAAU,EAAE;IAChE,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAEzF,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;AAC9E,CAAC,CAAA"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * tonapi `implementation` values that are genuine **nominator pools** — the
3
+ * only ones the text-comment deposit/withdraw mechanism can stake into.
4
+ * `liquidTF` (Tonstakers) mints a jetton instead and is excluded; unknown
5
+ * implementations are treated as non-nominator (excluded).
6
+ */
7
+ export declare const tonNominatorImplementations: readonly ["whales", "tf"];
8
+ export type TonNominatorImplementation = (typeof tonNominatorImplementations)[number];
9
+ export declare const isTonNominatorImplementation: (implementation: string | undefined) => implementation is TonNominatorImplementation;
10
+ /** Deposit comment for a pool implementation, or `undefined` if unsupported (block the action). */
11
+ export declare const tonStakingDepositComment: (implementation: string | undefined) => string | undefined;
12
+ /** Withdraw comment for a pool implementation, or `undefined` if unsupported (block the action). */
13
+ export declare const tonStakingWithdrawComment: (implementation: string | undefined) => string | undefined;
14
+ /**
15
+ * Whether a memo is a nominator-pool deposit/withdraw comment (`d`/`w`/`Deposit`
16
+ * /`Withdraw`). The signer uses this to force the transfer bounceable: a pool
17
+ * deposit/withdraw sent non-bounceable can be absorbed (lost) by the pool
18
+ * instead of bounced back if rejected.
19
+ */
20
+ export declare const isTonStakingComment: (memo: string | undefined) => boolean;
21
+ /**
22
+ * Processing-commission buffer (nanotons) added on top of a pool's `min_stake`
23
+ * for the minimum acceptable deposit. Depositing exactly the minimum is
24
+ * rejected; the pool needs ~1 TON headroom for its processing commission.
25
+ */
26
+ export declare const tonStakingDepositBuffer = 1000000000n;
27
+ /**
28
+ * Amount (nanotons) accompanying a withdraw message — the pool's 0.2 TON
29
+ * withdraw fee. The pool returns the full staked balance separately; sending
30
+ * more (e.g. 1 TON) fails.
31
+ */
32
+ export declare const tonStakingWithdrawFee = 200000000n;
33
+ /**
34
+ * Conservative network-fee reserve (nanotons ≈ 0.05 TON) held back from the
35
+ * spendable balance when sizing a stake deposit or checking the unstake fee is
36
+ * affordable. Larger than the bare base fee because the text-comment cell adds
37
+ * compute; matches the iOS reserve.
38
+ */
39
+ export declare const tonStakingFeeReserve = 50000000n;
40
+ /**
41
+ * A staking-pool list entry. `address` is the bounceable user-friendly (`EQ…`)
42
+ * pool contract address (normalized from the raw `0:` API form), `apy` is a
43
+ * percentage (e.g. `13.27` = 13.27%), and `minStake` is in nanotons.
44
+ */
45
+ export type TonStakingPool = {
46
+ address: string;
47
+ name: string;
48
+ apy: number;
49
+ minStake: bigint;
50
+ verified: boolean;
51
+ currentNominators?: number;
52
+ maxNominators?: number;
53
+ implementation?: string;
54
+ cycleEnd?: number;
55
+ };
56
+ /** Whether a pool has room for another nominator (pools at capacity reject new stakes). */
57
+ export declare const tonPoolHasCapacity: (pool: TonStakingPool) => boolean;
58
+ /**
59
+ * Whether a pool is a verified nominator pool with capacity — i.e. one the
60
+ * picker should surface and a stake can succeed into.
61
+ */
62
+ export declare const isStakeableTonPool: (pool: TonStakingPool) => boolean;
63
+ /**
64
+ * Fetches all verified TON staking pools from tonapi (`/v2/staking/pools`).
65
+ * Includes every implementation; callers filter to stakeable nominator pools
66
+ * via `isStakeableTonPool`.
67
+ */
68
+ export declare const getTonStakingPools: () => Promise<TonStakingPool[]>;
69
+ /**
70
+ * Computed info for a single pool (`/v2/staking/pool/{address}`). All fields
71
+ * are optional so a partial/changed response degrades gracefully. `apy` is a
72
+ * percentage; `cycleEnd` is the Unix second the current validation cycle ends.
73
+ */
74
+ export type TonStakingPoolInfo = {
75
+ address?: string;
76
+ name?: string;
77
+ apy?: number;
78
+ minStake?: bigint;
79
+ implementation?: string;
80
+ cycleEnd?: number;
81
+ };
82
+ /** Fetches computed pool metadata (name, APY, implementation, cycle end). */
83
+ export declare const getTonStakingPoolInfo: (poolAddress: string) => Promise<TonStakingPoolInfo | undefined>;
84
+ /**
85
+ * An account's position in a nominator pool. All amounts are nanotons. `amount`
86
+ * is the active stake; `pendingDeposit` is a just-placed stake awaiting the
87
+ * next validation cycle; `pendingWithdraw`/`readyWithdraw` indicate an
88
+ * in-progress withdrawal (funds locked until the cycle ends). `pool` is the
89
+ * bounceable user-friendly (`EQ…`) pool address (normalized from raw `0:`).
90
+ */
91
+ export type TonNominatorPosition = {
92
+ pool: string;
93
+ amount: bigint;
94
+ pendingDeposit: bigint;
95
+ pendingWithdraw: bigint;
96
+ readyWithdraw: bigint;
97
+ };
98
+ /**
99
+ * Fetches an account's nominator-pool positions
100
+ * (`/v2/staking/nominator/{accountId}/pools`) — the authoritative source for
101
+ * staked positions. `accountId` accepts the user-friendly wallet address.
102
+ */
103
+ export declare const getTonNominatorPools: (accountId: string) => Promise<TonNominatorPosition[]>;
104
+ //# sourceMappingURL=staking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staking.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/ton/staking.ts"],"names":[],"mappings":"AAYA;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,2BAA4B,CAAA;AAEpE,MAAM,MAAM,0BAA0B,GAAG,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAA;AAErF,eAAO,MAAM,4BAA4B,GACvC,gBAAgB,MAAM,GAAG,SAAS,KACjC,cAAc,IAAI,0BAC4E,CAAA;AAqBjG,mGAAmG;AACnG,eAAO,MAAM,wBAAwB,GAAI,gBAAgB,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SACe,CAAA;AAEtG,oGAAoG;AACpG,eAAO,MAAM,yBAAyB,GAAI,gBAAgB,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SACe,CAAA;AAOvG;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,GAAG,SAAS,KAAG,OAAwD,CAAA;AAEvH;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,cAAiB,CAAA;AAErD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,aAAe,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,YAAc,CAAA;AAc/C;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAiBD,2FAA2F;AAC3F,eAAO,MAAM,kBAAkB,GAAI,MAAM,cAAc,KAAG,OAMzD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,cAAc,KAAG,OACsC,CAAA;AAEhG;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC,cAAc,EAAE,CAKnE,CAAA;AAWD;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,qBAAqB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAavG,CAAA;AAUD;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAU,WAAW,MAAM,KAAG,OAAO,CAAC,oBAAoB,EAAE,CAW5F,CAAA"}
@@ -0,0 +1,137 @@
1
+ import { queryUrl } from '@vultisig/lib-utils/query/queryUrl';
2
+ import { tonAddressToBounceable } from './address.js';
3
+ /**
4
+ * Public TonAPI host. The staking endpoints (`/v2/staking/*`) are served
5
+ * exclusively by `tonapi.io` — the Vultisig `/ton` proxy fronts toncenter v3,
6
+ * which has no staking routes — so we hit tonapi.io directly. The extension's
7
+ * wildcard https host permission covers it.
8
+ */
9
+ const tonApiPublicUrl = 'https://tonapi.io';
10
+ /**
11
+ * tonapi `implementation` values that are genuine **nominator pools** — the
12
+ * only ones the text-comment deposit/withdraw mechanism can stake into.
13
+ * `liquidTF` (Tonstakers) mints a jetton instead and is excluded; unknown
14
+ * implementations are treated as non-nominator (excluded).
15
+ */
16
+ export const tonNominatorImplementations = ['whales', 'tf'];
17
+ export const isTonNominatorImplementation = (implementation) => !!implementation && tonNominatorImplementations.includes(implementation);
18
+ /**
19
+ * The deposit/withdraw text comments a TON nominator-pool contract expects.
20
+ * Each pool *implementation* uses a DIFFERENT word — sending the wrong one is
21
+ * rejected on-chain (exit code 72). These are contract protocol tokens, NOT
22
+ * user-facing UI: never translate them.
23
+ *
24
+ * The Whales pool repo README's "Stake" is stale and rejected by the live
25
+ * pools; the verified on-chain word is "Deposit".
26
+ */
27
+ const tonStakingDepositComments = {
28
+ tf: 'd',
29
+ whales: 'Deposit',
30
+ };
31
+ const tonStakingWithdrawComments = {
32
+ tf: 'w',
33
+ whales: 'Withdraw',
34
+ };
35
+ /** Deposit comment for a pool implementation, or `undefined` if unsupported (block the action). */
36
+ export const tonStakingDepositComment = (implementation) => isTonNominatorImplementation(implementation) ? tonStakingDepositComments[implementation] : undefined;
37
+ /** Withdraw comment for a pool implementation, or `undefined` if unsupported (block the action). */
38
+ export const tonStakingWithdrawComment = (implementation) => isTonNominatorImplementation(implementation) ? tonStakingWithdrawComments[implementation] : undefined;
39
+ const tonStakingComments = new Set([
40
+ ...Object.values(tonStakingDepositComments),
41
+ ...Object.values(tonStakingWithdrawComments),
42
+ ]);
43
+ /**
44
+ * Whether a memo is a nominator-pool deposit/withdraw comment (`d`/`w`/`Deposit`
45
+ * /`Withdraw`). The signer uses this to force the transfer bounceable: a pool
46
+ * deposit/withdraw sent non-bounceable can be absorbed (lost) by the pool
47
+ * instead of bounced back if rejected.
48
+ */
49
+ export const isTonStakingComment = (memo) => !!memo && tonStakingComments.has(memo.trim());
50
+ /**
51
+ * Processing-commission buffer (nanotons) added on top of a pool's `min_stake`
52
+ * for the minimum acceptable deposit. Depositing exactly the minimum is
53
+ * rejected; the pool needs ~1 TON headroom for its processing commission.
54
+ */
55
+ export const tonStakingDepositBuffer = 1000000000n;
56
+ /**
57
+ * Amount (nanotons) accompanying a withdraw message — the pool's 0.2 TON
58
+ * withdraw fee. The pool returns the full staked balance separately; sending
59
+ * more (e.g. 1 TON) fails.
60
+ */
61
+ export const tonStakingWithdrawFee = 200000000n;
62
+ /**
63
+ * Conservative network-fee reserve (nanotons ≈ 0.05 TON) held back from the
64
+ * spendable balance when sizing a stake deposit or checking the unstake fee is
65
+ * affordable. Larger than the bare base fee because the text-comment cell adds
66
+ * compute; matches the iOS reserve.
67
+ */
68
+ export const tonStakingFeeReserve = 50000000n;
69
+ const mapPoolEntry = (entry) => ({
70
+ // Normalize to the bounceable `EQ…` form at the boundary so no caller can
71
+ // build a staking transfer to a raw/non-bounceable destination (which the
72
+ // pool would absorb instead of bouncing back on rejection).
73
+ address: tonAddressToBounceable(entry.address),
74
+ name: entry.name,
75
+ apy: entry.apy,
76
+ minStake: BigInt(Math.trunc(entry.min_stake)),
77
+ verified: entry.verified,
78
+ currentNominators: entry.current_nominators,
79
+ maxNominators: entry.max_nominators,
80
+ implementation: entry.implementation,
81
+ cycleEnd: entry.cycle_end,
82
+ });
83
+ /** Whether a pool has room for another nominator (pools at capacity reject new stakes). */
84
+ export const tonPoolHasCapacity = (pool) => {
85
+ const { currentNominators, maxNominators } = pool;
86
+ if (currentNominators === undefined || maxNominators === undefined || maxNominators <= 0) {
87
+ return true;
88
+ }
89
+ return currentNominators < maxNominators;
90
+ };
91
+ /**
92
+ * Whether a pool is a verified nominator pool with capacity — i.e. one the
93
+ * picker should surface and a stake can succeed into.
94
+ */
95
+ export const isStakeableTonPool = (pool) => pool.verified && isTonNominatorImplementation(pool.implementation) && tonPoolHasCapacity(pool);
96
+ /**
97
+ * Fetches all verified TON staking pools from tonapi (`/v2/staking/pools`).
98
+ * Includes every implementation; callers filter to stakeable nominator pools
99
+ * via `isStakeableTonPool`.
100
+ */
101
+ export const getTonStakingPools = async () => {
102
+ const url = `${tonApiPublicUrl}/v2/staking/pools?include_unverified=false`;
103
+ const { pools } = await queryUrl(url);
104
+ return pools.map(mapPoolEntry);
105
+ };
106
+ /** Fetches computed pool metadata (name, APY, implementation, cycle end). */
107
+ export const getTonStakingPoolInfo = async (poolAddress) => {
108
+ const url = `${tonApiPublicUrl}/v2/staking/pool/${encodeURIComponent(poolAddress)}`;
109
+ const { pool } = await queryUrl(url);
110
+ if (!pool)
111
+ return undefined;
112
+ return {
113
+ address: pool.address ? tonAddressToBounceable(pool.address) : undefined,
114
+ name: pool.name,
115
+ apy: pool.apy,
116
+ minStake: pool.min_stake !== undefined ? BigInt(Math.trunc(pool.min_stake)) : undefined,
117
+ implementation: pool.implementation,
118
+ cycleEnd: pool.cycle_end,
119
+ };
120
+ };
121
+ /**
122
+ * Fetches an account's nominator-pool positions
123
+ * (`/v2/staking/nominator/{accountId}/pools`) — the authoritative source for
124
+ * staked positions. `accountId` accepts the user-friendly wallet address.
125
+ */
126
+ export const getTonNominatorPools = async (accountId) => {
127
+ const url = `${tonApiPublicUrl}/v2/staking/nominator/${encodeURIComponent(accountId)}/pools`;
128
+ const { pools } = await queryUrl(url);
129
+ return pools.map(position => ({
130
+ pool: tonAddressToBounceable(position.pool),
131
+ amount: BigInt(Math.trunc(position.amount)),
132
+ pendingDeposit: BigInt(Math.trunc(position.pending_deposit)),
133
+ pendingWithdraw: BigInt(Math.trunc(position.pending_withdraw)),
134
+ readyWithdraw: BigInt(Math.trunc(position.ready_withdraw)),
135
+ }));
136
+ };
137
+ //# sourceMappingURL=staking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staking.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/ton/staking.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { EvmChain } from '@vultisig/core-chain/Chain';
2
+ import { AccountCoinKey } from '@vultisig/core-chain/coin/AccountCoin';
3
+ import { Address } from 'viem';
4
+ export type GetEvmChainBalancesInput = {
5
+ chain: EvmChain;
6
+ address: Address;
7
+ coins: AccountCoinKey<EvmChain>[];
8
+ };
9
+ export type EvmChainBalances = Record<string, bigint>;
10
+ export declare const getEvmChainBalances: (input: GetEvmChainBalancesInput) => Promise<EvmChainBalances>;
11
+ //# sourceMappingURL=getEvmChainBalances.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getEvmChainBalances.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/coin/balance/getEvmChainBalances.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAErD,OAAO,EAAE,cAAc,EAA0B,MAAM,uCAAuC,CAAA;AAE9F,OAAO,EAAE,OAAO,EAAsB,MAAM,MAAM,CAAA;AAMlD,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,QAAQ,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAkBrD,eAAO,MAAM,mBAAmB,GAAU,OAAO,wBAAwB,KAAG,OAAO,CAAC,gBAAgB,CAiDnG,CAAA"}
@@ -0,0 +1,57 @@
1
+ import { getEvmClient } from '@vultisig/core-chain/chains/evm/client';
2
+ import { accountCoinKeyToString } from '@vultisig/core-chain/coin/AccountCoin';
3
+ import { isFeeCoin } from '@vultisig/core-chain/coin/utils/isFeeCoin';
4
+ import { erc20Abi, parseAbi } from 'viem';
5
+ import { getEvmCoinBalance } from './resolvers/evm.js';
6
+ const multicall3Abi = parseAbi(['function getEthBalance(address addr) view returns (uint256)']);
7
+ const getFallbackBalances = async ({ chain, address, coins }) => {
8
+ const entries = await Promise.all(coins.map(async (coin) => {
9
+ const input = { ...coin, chain, address };
10
+ try {
11
+ return [accountCoinKeyToString(input), await getEvmCoinBalance(input)];
12
+ }
13
+ catch {
14
+ return [accountCoinKeyToString(input), 0n];
15
+ }
16
+ }));
17
+ return Object.fromEntries(entries);
18
+ };
19
+ export const getEvmChainBalances = async (input) => {
20
+ const { chain, address, coins } = input;
21
+ if (coins.length === 0) {
22
+ return {};
23
+ }
24
+ const publicClient = getEvmClient(chain);
25
+ const multicall3Address = publicClient.chain?.contracts?.multicall3?.address;
26
+ if (!multicall3Address) {
27
+ return getFallbackBalances(input);
28
+ }
29
+ const contracts = coins.map(coin => isFeeCoin(coin)
30
+ ? {
31
+ address: multicall3Address,
32
+ abi: multicall3Abi,
33
+ functionName: 'getEthBalance',
34
+ args: [address],
35
+ }
36
+ : {
37
+ address: coin.id,
38
+ abi: erc20Abi,
39
+ functionName: 'balanceOf',
40
+ args: [address],
41
+ });
42
+ const results = await publicClient
43
+ .multicall({
44
+ allowFailure: true,
45
+ contracts,
46
+ })
47
+ .catch(() => undefined);
48
+ if (!results) {
49
+ return getFallbackBalances(input);
50
+ }
51
+ return Object.fromEntries(coins.map((coin, index) => {
52
+ const result = results[index];
53
+ const amount = result?.status === 'success' ? BigInt(result.result) : 0n;
54
+ return [accountCoinKeyToString({ ...coin, chain, address }), amount];
55
+ }));
56
+ };
57
+ //# sourceMappingURL=getEvmChainBalances.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getEvmChainBalances.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/coin/balance/getEvmChainBalances.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export declare const generalSwapProviders: readonly ["1inch", "li.fi", "kyber", "swapkit", "cowswap"];
1
+ export declare const generalSwapProviders: readonly ["1inch", "li.fi", "kyber", "swapkit", "cowswap", "jupiter"];
2
2
  export type GeneralSwapProvider = (typeof generalSwapProviders)[number];
3
3
  export declare const generalSwapProviderName: Record<GeneralSwapProvider, string>;
4
4
  //# sourceMappingURL=GeneralSwapProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GeneralSwapProvider.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/swap/general/GeneralSwapProvider.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,4DAA6D,CAAA;AAE9F,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAMvE,CAAA"}
1
+ {"version":3,"file":"GeneralSwapProvider.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/swap/general/GeneralSwapProvider.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,uEAAwE,CAAA;AAEzG,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAOvE,CAAA"}
@@ -1,9 +1,10 @@
1
- export const generalSwapProviders = ['1inch', 'li.fi', 'kyber', 'swapkit', 'cowswap'];
1
+ export const generalSwapProviders = ['1inch', 'li.fi', 'kyber', 'swapkit', 'cowswap', 'jupiter'];
2
2
  export const generalSwapProviderName = {
3
3
  '1inch': '1Inch',
4
4
  'li.fi': 'LI.FI',
5
5
  kyber: 'KyberSwap',
6
6
  swapkit: 'SwapKit',
7
7
  cowswap: 'CowSwap',
8
+ jupiter: 'Jupiter',
8
9
  };
9
10
  //# sourceMappingURL=GeneralSwapProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GeneralSwapProvider.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/swap/general/GeneralSwapProvider.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAU,CAAA;AAI9F,MAAM,CAAC,MAAM,uBAAuB,GAAwC;IAC1E,OAAO,EAAE,OAAO;IAChB,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,WAAW;IAClB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;CACnB,CAAA"}
1
+ {"version":3,"file":"GeneralSwapProvider.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/swap/general/GeneralSwapProvider.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAU,CAAA;AAIzG,MAAM,CAAC,MAAM,uBAAuB,GAAwC;IAC1E,OAAO,EAAE,OAAO;IAChB,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,WAAW;IAClB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;CACnB,CAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Jupiter is a Solana-only, same-chain aggregator. It can route SOL↔SPL and
3
+ * SPL↔SPL pairs that stay on Solana, and nothing else — there is no cross-chain
4
+ * Jupiter route. `findSwapQuote` therefore only offers Jupiter when both legs
5
+ * are on Solana.
6
+ */
7
+ export declare const jupiterSwapEnabledChains: readonly [import("@vultisig/core-chain/Chain").OtherChain.Solana];
8
+ export type JupiterSwapEnabledChain = (typeof jupiterSwapEnabledChains)[number];
9
+ //# sourceMappingURL=JupiterSwapEnabledChains.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JupiterSwapEnabledChains.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/swap/general/jupiter/JupiterSwapEnabledChains.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,mEAA0B,CAAA;AAE/D,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAA"}
@@ -0,0 +1,9 @@
1
+ import { Chain } from '@vultisig/core-chain/Chain';
2
+ /**
3
+ * Jupiter is a Solana-only, same-chain aggregator. It can route SOL↔SPL and
4
+ * SPL↔SPL pairs that stay on Solana, and nothing else — there is no cross-chain
5
+ * Jupiter route. `findSwapQuote` therefore only offers Jupiter when both legs
6
+ * are on Solana.
7
+ */
8
+ export const jupiterSwapEnabledChains = [Chain.Solana];
9
+ //# sourceMappingURL=JupiterSwapEnabledChains.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JupiterSwapEnabledChains.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/swap/general/jupiter/JupiterSwapEnabledChains.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAElD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,KAAK,CAAC,MAAM,CAAU,CAAA"}
@@ -0,0 +1,25 @@
1
+ import { AccountCoin } from '@vultisig/core-chain/coin/AccountCoin';
2
+ import { GeneralSwapQuote } from '@vultisig/core-chain/swap/general/GeneralSwapQuote';
3
+ import { JupiterSwapEnabledChain } from '@vultisig/core-chain/swap/general/jupiter/JupiterSwapEnabledChains';
4
+ import { TransferDirection } from '@vultisig/lib-utils/TransferDirection';
5
+ type Input = Record<TransferDirection, AccountCoin<JupiterSwapEnabledChain>> & {
6
+ amount: bigint;
7
+ /** VULT-scaled affiliate fee in bps. `0` disables the platform fee entirely. */
8
+ affiliateBps?: number;
9
+ /** Slippage tolerance in bps (e.g. 50 = 0.5%). Defaults to 50. */
10
+ slippageBps?: number;
11
+ };
12
+ /**
13
+ * Fetch a Solana same-chain swap quote from Jupiter and build the signed-tx
14
+ * payload the keysign pipeline expects.
15
+ *
16
+ * Affiliate fee: when `affiliateBps > 0`, `platformFeeBps` is sent on the quote.
17
+ * The fee ATA (output mint, owner = `jupiterFeeOwnerAddress`) is derived, passed
18
+ * to `/swap` as `feeAccount`, and an idempotent create instruction is prepended
19
+ * to the returned transaction only when the quote actually returns a non-zero
20
+ * `platformFee.amount`. When `affiliateBps` is 0 (e.g. an Ultimate-tier VULT
21
+ * holder) or Jupiter floors the fee to zero, no fee account is touched.
22
+ */
23
+ export declare const getJupiterSwapQuote: ({ from, to, amount, affiliateBps, slippageBps, }: Input) => Promise<GeneralSwapQuote>;
24
+ export {};
25
+ //# sourceMappingURL=getJupiterSwapQuote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getJupiterSwapQuote.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/swap/general/jupiter/api/getJupiterSwapQuote.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAA;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oDAAoD,CAAA;AAErF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oEAAoE,CAAA;AAC5G,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAUzE,KAAK,KAAK,GAAG,MAAM,CAAC,iBAAiB,EAAE,WAAW,CAAC,uBAAuB,CAAC,CAAC,GAAG;IAC7E,MAAM,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AA2CD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAAU,kDAMvC,KAAK,KAAG,OAAO,CAAC,gBAAgB,CAuFlC,CAAA"}
@@ -0,0 +1,112 @@
1
+ import { Chain } from '@vultisig/core-chain/Chain';
2
+ import { solanaConfig } from '@vultisig/core-chain/chains/solana/solanaConfig';
3
+ import { getJupiterConfig, jupiterFeeOwnerAddress } from '@vultisig/core-chain/swap/general/jupiter/config';
4
+ import { deriveJupiterFeeAccount, prependJupiterFeeAta } from './jupiterFeeAta.js';
5
+ /** Canonical wrapped-SOL mint, used as the mint address for native SOL legs. */
6
+ const solanaNativeMint = 'So11111111111111111111111111111111111111112';
7
+ /** Jupiter default slippage when the caller does not override (bps). */
8
+ const DEFAULT_JUPITER_SLIPPAGE_BPS = 50;
9
+ const mintFor = ({ id }) => id ?? solanaNativeMint;
10
+ const requestJson = async (url, init) => {
11
+ const response = await fetch(url, init);
12
+ const rawText = await response.text().catch(() => '');
13
+ if (!response.ok) {
14
+ const hint = rawText ? ` ${rawText.slice(0, 200)}` : '';
15
+ throw new Error(`Jupiter request failed (${response.status}): ${response.statusText}${hint}`);
16
+ }
17
+ try {
18
+ return JSON.parse(rawText);
19
+ }
20
+ catch {
21
+ throw new Error('Jupiter returned a non-JSON response.');
22
+ }
23
+ };
24
+ /**
25
+ * Fetch a Solana same-chain swap quote from Jupiter and build the signed-tx
26
+ * payload the keysign pipeline expects.
27
+ *
28
+ * Affiliate fee: when `affiliateBps > 0`, `platformFeeBps` is sent on the quote.
29
+ * The fee ATA (output mint, owner = `jupiterFeeOwnerAddress`) is derived, passed
30
+ * to `/swap` as `feeAccount`, and an idempotent create instruction is prepended
31
+ * to the returned transaction only when the quote actually returns a non-zero
32
+ * `platformFee.amount`. When `affiliateBps` is 0 (e.g. an Ultimate-tier VULT
33
+ * holder) or Jupiter floors the fee to zero, no fee account is touched.
34
+ */
35
+ export const getJupiterSwapQuote = async ({ from, to, amount, affiliateBps = 0, slippageBps = DEFAULT_JUPITER_SLIPPAGE_BPS, }) => {
36
+ const { baseUrl } = getJupiterConfig();
37
+ const inputMint = mintFor(from);
38
+ const outputMint = mintFor(to);
39
+ const requestsPlatformFee = affiliateBps > 0;
40
+ const quoteParams = new URLSearchParams({
41
+ swapMode: 'ExactIn',
42
+ inputMint,
43
+ outputMint,
44
+ amount: amount.toString(),
45
+ slippageBps: slippageBps.toString(),
46
+ });
47
+ if (requestsPlatformFee) {
48
+ quoteParams.set('platformFeeBps', affiliateBps.toString());
49
+ }
50
+ const quoteResponse = await requestJson(`${baseUrl}/swap/v1/quote?${quoteParams.toString()}`, {
51
+ headers: { Accept: 'application/json' },
52
+ });
53
+ // Gate the fee-account flow on the actually quoted fee, not just the requested
54
+ // bps: Jupiter can floor `platformFee.amount` to 0 (tiny amounts, route with no
55
+ // fee-eligible mint) even when we asked for a fee. Deriving a fee account and
56
+ // paying ATA rent for a zero fee would break the "no fee account when the fee
57
+ // floors to zero" contract. Require both a requested fee and a non-zero quoted
58
+ // amount before touching the fee account.
59
+ const swapFeeAmount = BigInt(quoteResponse.platformFee?.amount ?? '0');
60
+ const chargesFee = requestsPlatformFee && swapFeeAmount > 0n;
61
+ // The fee mint is the OUTPUT mint (ExactIn). Derive the fee ATA up front so
62
+ // it can be sent to /swap and so we know which program owns it for the
63
+ // prepended create instruction.
64
+ const feeAccountInfo = chargesFee
65
+ ? await deriveJupiterFeeAccount({ outputMint, feeOwner: jupiterFeeOwnerAddress })
66
+ : undefined;
67
+ const swapResponse = await requestJson(`${baseUrl}/swap/v1/swap`, {
68
+ method: 'POST',
69
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
70
+ body: JSON.stringify({
71
+ quoteResponse,
72
+ userPublicKey: from.address,
73
+ wrapAndUnwrapSol: true,
74
+ dynamicComputeUnitLimit: true,
75
+ ...(feeAccountInfo ? { feeAccount: feeAccountInfo.feeAccount } : {}),
76
+ }),
77
+ });
78
+ if (!swapResponse.swapTransaction) {
79
+ throw new Error('Jupiter swap response did not include a serialized transaction.');
80
+ }
81
+ const data = feeAccountInfo
82
+ ? await prependJupiterFeeAta({
83
+ txData: swapResponse.swapTransaction,
84
+ feeAccount: feeAccountInfo.feeAccount,
85
+ mintPubkey: feeAccountInfo.mintPubkey,
86
+ ownerPubkey: feeAccountInfo.ownerPubkey,
87
+ tokenProgramId: feeAccountInfo.tokenProgramId,
88
+ })
89
+ : swapResponse.swapTransaction;
90
+ // Network fee is an upper-bound estimate: Solana's per-signature base fee plus
91
+ // the fee-ATA rent buffer when we prepend a create instruction (charged once
92
+ // per fee mint; idempotent thereafter). Jupiter embeds its own priority fee in
93
+ // the returned tx, which the consumer surfaces at broadcast time.
94
+ const networkFee = BigInt(solanaConfig.baseFee) + (chargesFee ? BigInt(solanaConfig.ataRentLamports) : 0n);
95
+ return {
96
+ dstAmount: quoteResponse.outAmount,
97
+ provider: 'jupiter',
98
+ tx: {
99
+ solana: {
100
+ data,
101
+ networkFee,
102
+ swapFee: {
103
+ amount: swapFeeAmount,
104
+ decimals: to.decimals,
105
+ chain: Chain.Solana,
106
+ id: to.id,
107
+ },
108
+ },
109
+ },
110
+ };
111
+ };
112
+ //# sourceMappingURL=getJupiterSwapQuote.js.map