@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.
- package/CHANGELOG.md +29 -0
- package/dist/chains/ton/address.d.ts +9 -0
- package/dist/chains/ton/address.d.ts.map +1 -1
- package/dist/chains/ton/address.js +13 -0
- package/dist/chains/ton/address.js.map +1 -1
- package/dist/chains/ton/staking.d.ts +104 -0
- package/dist/chains/ton/staking.d.ts.map +1 -0
- package/dist/chains/ton/staking.js +137 -0
- package/dist/chains/ton/staking.js.map +1 -0
- package/dist/coin/balance/getEvmChainBalances.d.ts +11 -0
- package/dist/coin/balance/getEvmChainBalances.d.ts.map +1 -0
- package/dist/coin/balance/getEvmChainBalances.js +57 -0
- package/dist/coin/balance/getEvmChainBalances.js.map +1 -0
- package/dist/swap/general/GeneralSwapProvider.d.ts +1 -1
- package/dist/swap/general/GeneralSwapProvider.d.ts.map +1 -1
- package/dist/swap/general/GeneralSwapProvider.js +2 -1
- package/dist/swap/general/GeneralSwapProvider.js.map +1 -1
- package/dist/swap/general/jupiter/JupiterSwapEnabledChains.d.ts +9 -0
- package/dist/swap/general/jupiter/JupiterSwapEnabledChains.d.ts.map +1 -0
- package/dist/swap/general/jupiter/JupiterSwapEnabledChains.js +9 -0
- package/dist/swap/general/jupiter/JupiterSwapEnabledChains.js.map +1 -0
- package/dist/swap/general/jupiter/api/getJupiterSwapQuote.d.ts +25 -0
- package/dist/swap/general/jupiter/api/getJupiterSwapQuote.d.ts.map +1 -0
- package/dist/swap/general/jupiter/api/getJupiterSwapQuote.js +112 -0
- package/dist/swap/general/jupiter/api/getJupiterSwapQuote.js.map +1 -0
- package/dist/swap/general/jupiter/api/jupiterFeeAta.d.ts +48 -0
- package/dist/swap/general/jupiter/api/jupiterFeeAta.d.ts.map +1 -0
- package/dist/swap/general/jupiter/api/jupiterFeeAta.js +115 -0
- package/dist/swap/general/jupiter/api/jupiterFeeAta.js.map +1 -0
- package/dist/swap/general/jupiter/config.d.ts +13 -0
- package/dist/swap/general/jupiter/config.d.ts.map +1 -0
- package/dist/swap/general/jupiter/config.js +52 -0
- package/dist/swap/general/jupiter/config.js.map +1 -0
- package/dist/swap/quote/findSwapQuote.d.ts +1 -1
- package/dist/swap/quote/findSwapQuote.d.ts.map +1 -1
- package/dist/swap/quote/findSwapQuote.js +33 -1
- package/dist/swap/quote/findSwapQuote.js.map +1 -1
- package/dist/swap/utils/getSwapExplorerUrl/index.d.ts +2 -2
- package/dist/swap/utils/getSwapExplorerUrl/index.d.ts.map +1 -1
- package/dist/swap/utils/getSwapExplorerUrl/index.js +11 -2
- package/dist/swap/utils/getSwapExplorerUrl/index.js.map +1 -1
- 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":"
|
|
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,
|
|
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;
|
|
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
|