@vultisig/core-chain 2.24.0 → 2.24.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/CHANGELOG.md +8 -0
- package/dist/chains/cosmos/cosmosDecimal.d.ts +15 -0
- package/dist/chains/cosmos/cosmosDecimal.d.ts.map +1 -0
- package/dist/chains/cosmos/cosmosDecimal.js +15 -0
- package/dist/chains/cosmos/cosmosDecimal.js.map +1 -0
- package/dist/chains/cosmos/gas.d.ts +2 -6
- package/dist/chains/cosmos/gas.d.ts.map +1 -1
- package/dist/chains/cosmos/gas.js +20 -13
- package/dist/chains/cosmos/gas.js.map +1 -1
- package/dist/chains/cosmos/osmosisDynamicFee.d.ts +13 -0
- package/dist/chains/cosmos/osmosisDynamicFee.d.ts.map +1 -0
- package/dist/chains/cosmos/osmosisDynamicFee.js +108 -0
- package/dist/chains/cosmos/osmosisDynamicFee.js.map +1 -0
- package/dist/chains/cosmos/resolveCosmosGasFee.d.ts +22 -1
- package/dist/chains/cosmos/resolveCosmosGasFee.d.ts.map +1 -1
- package/dist/chains/cosmos/resolveCosmosGasFee.js +32 -3
- package/dist/chains/cosmos/resolveCosmosGasFee.js.map +1 -1
- package/dist/chains/utxo/minUtxo.d.ts.map +1 -1
- package/dist/chains/utxo/minUtxo.js +7 -1
- package/dist/chains/utxo/minUtxo.js.map +1 -1
- package/dist/coin/utils/findByTicker.d.ts.map +1 -1
- package/dist/coin/utils/findByTicker.js +15 -1
- package/dist/coin/utils/findByTicker.js.map +1 -1
- package/dist/swap/general/cowswap/api/getCowSwapQuote.d.ts.map +1 -1
- package/dist/swap/general/cowswap/api/getCowSwapQuote.js +68 -3
- package/dist/swap/general/cowswap/api/getCowSwapQuote.js.map +1 -1
- package/dist/swap/general/jupiter/api/getJupiterSwapQuote.d.ts.map +1 -1
- package/dist/swap/general/jupiter/api/getJupiterSwapQuote.js +7 -0
- package/dist/swap/general/jupiter/api/getJupiterSwapQuote.js.map +1 -1
- package/dist/swap/general/knownAggregatorRouters.d.ts +14 -0
- package/dist/swap/general/knownAggregatorRouters.d.ts.map +1 -0
- package/dist/swap/general/knownAggregatorRouters.js +83 -0
- package/dist/swap/general/knownAggregatorRouters.js.map +1 -0
- package/dist/swap/general/kyber/api/tx.d.ts.map +1 -1
- package/dist/swap/general/kyber/api/tx.js +19 -2
- package/dist/swap/general/kyber/api/tx.js.map +1 -1
- package/dist/swap/general/lifi/api/getLifiSwapQuote.d.ts.map +1 -1
- package/dist/swap/general/lifi/api/getLifiSwapQuote.js +8 -1
- package/dist/swap/general/lifi/api/getLifiSwapQuote.js.map +1 -1
- package/dist/swap/general/oneInch/api/getOneInchSwapQuote.d.ts +6 -1
- package/dist/swap/general/oneInch/api/getOneInchSwapQuote.d.ts.map +1 -1
- package/dist/swap/general/oneInch/api/getOneInchSwapQuote.js +24 -1
- package/dist/swap/general/oneInch/api/getOneInchSwapQuote.js.map +1 -1
- package/dist/swap/general/priceImpactGuard.d.ts +73 -0
- package/dist/swap/general/priceImpactGuard.d.ts.map +1 -0
- package/dist/swap/general/priceImpactGuard.js +89 -0
- package/dist/swap/general/priceImpactGuard.js.map +1 -0
- package/dist/swap/general/swapkit/api/getSwapKitQuote.d.ts.map +1 -1
- package/dist/swap/general/swapkit/api/getSwapKitQuote.js +5 -0
- package/dist/swap/general/swapkit/api/getSwapKitQuote.js.map +1 -1
- package/dist/swap/native/limitSwapMemo.d.ts.map +1 -1
- package/dist/swap/native/limitSwapMemo.js +12 -1
- package/dist/swap/native/limitSwapMemo.js.map +1 -1
- package/dist/swap/native/thorchainDepositMemo.d.ts +19 -0
- package/dist/swap/native/thorchainDepositMemo.d.ts.map +1 -0
- package/dist/swap/native/thorchainDepositMemo.js +75 -0
- package/dist/swap/native/thorchainDepositMemo.js.map +1 -0
- package/dist/swap/quote/findSwapQuote.d.ts.map +1 -1
- package/dist/swap/quote/findSwapQuote.js +20 -3
- package/dist/swap/quote/findSwapQuote.js.map +1 -1
- package/dist/tx/broadcast/resolvers/cardano.d.ts.map +1 -1
- package/dist/tx/broadcast/resolvers/cardano.js +4 -4
- package/dist/tx/broadcast/resolvers/cardano.js.map +1 -1
- package/dist/tx/broadcast/resolvers/utxo.d.ts.map +1 -1
- package/dist/tx/broadcast/resolvers/utxo.js +4 -4
- package/dist/tx/broadcast/resolvers/utxo.js.map +1 -1
- package/dist/tx/fee/evm/clampEvmPriorityFee.d.ts +8 -0
- package/dist/tx/fee/evm/clampEvmPriorityFee.d.ts.map +1 -0
- package/dist/tx/fee/evm/clampEvmPriorityFee.js +50 -0
- package/dist/tx/fee/evm/clampEvmPriorityFee.js.map +1 -0
- package/dist/tx/status/resolvers/utxo.d.ts.map +1 -1
- package/dist/tx/status/resolvers/utxo.js +12 -2
- package/dist/tx/status/resolvers/utxo.js.map +1 -1
- package/package.json +31 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @vultisig/core-chain
|
|
2
2
|
|
|
3
|
+
## 2.24.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1107](https://github.com/vultisig/vultisig-sdk/pull/1107) [`c5e89cb`](https://github.com/vultisig/vultisig-sdk/commit/c5e89cb317ae6f4ca00eb6c628ad6bac636e4821) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - fix(swap): populate 1inch affiliateFee for display (AGG-05) — 1inch general-swap quotes never populated `affiliateFee` on the returned `GeneralSwapQuote`, unlike Kyber and LI.FI, leaving the fee-transparency row blank for 1inch even though a real affiliate cut is taken via its `fee`/`referrer` params. `getOneInchSwapQuote` now grosses the post-fee `dstAmount` back up (same bps-based calc `getKyberSwapAffiliateFee` uses) to compute and attach a display-only `affiliateFee`.
|
|
8
|
+
|
|
9
|
+
- [#1101](https://github.com/vultisig/vultisig-sdk/pull/1101) [`9a1fc02`](https://github.com/vultisig/vultisig-sdk/commit/9a1fc0276ddc8fc905fab392875499d39011520d) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - fix(swap): surface non-integer dstAmount drops + validate THORChain/MayaChain MsgDeposit memos (SDK-CORRECTNESS-04/06/08) — a drifted provider's non-integer `dstAmount` used to silently drop that quote from ranking with no signal it was a parse failure; `findSwapQuote` now `console.warn`s the provider + raw value before rethrowing. `prepareThorchainMsgDepositTxFromKeys` accepted an arbitrary memo string with no structural validation, unlike the fully-validated limit-swap memo path; it now fails closed on non-printable/oversized memos and unrecognized THORChain/MayaChain deposit actions (and, for the two documented LP actions, a malformed pool id), while still accepting non-LP operator-style memos (BOND, UNBOND, etc.) verbatim. Also replaced an `as any` cast on the deposit's chain-specific proto binding with per-chain branches so the `case`/`value` pairing is statically checked instead of bypassed.
|
|
10
|
+
|
|
3
11
|
## 2.24.0
|
|
4
12
|
|
|
5
13
|
### Minor Changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact-rational decimal parsing/fee math shared by every cosmos gas-price
|
|
3
|
+
* resolver (the generic node-config path and Osmosis's dynamic EIP-1559
|
|
4
|
+
* path). Kept in its own module so both can depend on it without a circular
|
|
5
|
+
* import between them.
|
|
6
|
+
*/
|
|
7
|
+
export type ParsedDecimal = {
|
|
8
|
+
numerator: bigint;
|
|
9
|
+
denominator: bigint;
|
|
10
|
+
};
|
|
11
|
+
/** Parses a non-negative decimal string (e.g. "0.030000000000000000") into an exact fraction. */
|
|
12
|
+
export declare const parseDecimal: (value: string) => ParsedDecimal | undefined;
|
|
13
|
+
/** ceil(gasLimit * gasPrice), computed as an exact fraction - never loses precision to IEEE-754 rounding. */
|
|
14
|
+
export declare const getFeeAmountFromGasPrice: (gasLimit: bigint, gasPrice: ParsedDecimal) => bigint;
|
|
15
|
+
//# sourceMappingURL=cosmosDecimal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cosmosDecimal.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/cosmosDecimal.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,iGAAiG;AACjG,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,aAAa,GAAG,SAQ5D,CAAA;AAED,6GAA6G;AAC7G,eAAO,MAAM,wBAAwB,GAAI,UAAU,MAAM,EAAE,UAAU,aAAa,KAAG,MAIpF,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Parses a non-negative decimal string (e.g. "0.030000000000000000") into an exact fraction. */
|
|
2
|
+
export const parseDecimal = (value) => {
|
|
3
|
+
if (!/^\d+(?:\.\d+)?$/.test(value))
|
|
4
|
+
return undefined;
|
|
5
|
+
const [whole, fraction = ''] = value.split('.');
|
|
6
|
+
const denominator = 10n ** BigInt(fraction.length);
|
|
7
|
+
const numerator = BigInt(`${whole}${fraction}`);
|
|
8
|
+
return { numerator, denominator };
|
|
9
|
+
};
|
|
10
|
+
/** ceil(gasLimit * gasPrice), computed as an exact fraction - never loses precision to IEEE-754 rounding. */
|
|
11
|
+
export const getFeeAmountFromGasPrice = (gasLimit, gasPrice) => {
|
|
12
|
+
const total = gasLimit * gasPrice.numerator;
|
|
13
|
+
return (total + gasPrice.denominator - 1n) / gasPrice.denominator;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=cosmosDecimal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cosmosDecimal.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/cosmosDecimal.ts"],"names":[],"mappings":"AAWA,iGAAiG;AACjG,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAa,EAA6B,EAAE;IACvE,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IAEpD,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC/C,MAAM,WAAW,GAAG,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAClD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,KAAK,GAAG,QAAQ,EAAE,CAAC,CAAA;IAE/C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAAA;AACnC,CAAC,CAAA;AAED,6GAA6G;AAC7G,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,QAAgB,EAAE,QAAuB,EAAU,EAAE;IAC5F,MAAM,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAA;IAE3C,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC,WAAW,GAAG,EAAE,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAA;AACnE,CAAC,CAAA"}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { IbcEnabledCosmosChain } from '../../Chain.js';
|
|
2
2
|
import type { CoinKey } from '../../coin/Coin.js';
|
|
3
|
+
import { type ParsedDecimal } from './cosmosDecimal.js';
|
|
4
|
+
export { getFeeAmountFromGasPrice } from './cosmosDecimal.js';
|
|
3
5
|
export declare const cosmosGasRecord: Record<IbcEnabledCosmosChain, bigint>;
|
|
4
6
|
type FetchOpts = {
|
|
5
7
|
fetchImpl?: typeof fetch;
|
|
6
8
|
signal?: AbortSignal;
|
|
7
9
|
};
|
|
8
|
-
type ParsedDecimal = {
|
|
9
|
-
numerator: bigint;
|
|
10
|
-
denominator: bigint;
|
|
11
|
-
};
|
|
12
|
-
export declare const getFeeAmountFromGasPrice: (gasLimit: bigint, gasPrice: ParsedDecimal) => bigint;
|
|
13
10
|
export declare const getMinGasPriceForDenom: (minimumGasPrice: string, targetDenom: string) => ParsedDecimal | undefined;
|
|
14
11
|
export declare const getCosmosFeeAmount: (coin: CoinKey<IbcEnabledCosmosChain>, opts?: FetchOpts) => Promise<bigint>;
|
|
15
|
-
export {};
|
|
16
12
|
//# sourceMappingURL=gas.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gas.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/gas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAC1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"gas.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/gas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAC1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAA4B,KAAK,aAAa,EAAgB,MAAM,iBAAiB,CAAA;AAM5F,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAI1D,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CASjE,CAAA;AAED,KAAK,SAAS,GAAG;IACf,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,CAAA;AAqBD,eAAO,MAAM,sBAAsB,GAAI,iBAAiB,MAAM,EAAE,aAAa,MAAM,KAAG,aAAa,GAAG,SAYrG,CAAA;AA0ED,eAAO,MAAM,kBAAkB,GAC7B,MAAM,OAAO,CAAC,qBAAqB,CAAC,EACpC,OAAM,SAAc,KACnB,OAAO,CAAC,MAAM,CAehB,CAAA"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Chain } from '../../Chain.js';
|
|
2
|
+
import { getFeeAmountFromGasPrice, parseDecimal } from './cosmosDecimal.js';
|
|
2
3
|
import { cosmosFeeCoinDenom } from './cosmosFeeCoinDenom.js';
|
|
3
4
|
import { getCosmosGasLimit } from './cosmosGasLimitRecord.js';
|
|
4
5
|
import { getCosmosRpcUrl } from './getCosmosRpcUrl.js';
|
|
6
|
+
import { getOsmosisDynamicFeeFloor } from './osmosisDynamicFee.js';
|
|
7
|
+
export { getFeeAmountFromGasPrice } from './cosmosDecimal.js';
|
|
5
8
|
const defaultGas = 7500n;
|
|
6
9
|
export const cosmosGasRecord = {
|
|
7
10
|
[Chain.Cosmos]: defaultGas,
|
|
@@ -16,14 +19,6 @@ export const cosmosGasRecord = {
|
|
|
16
19
|
const minGasPriceConfigPath = '/cosmos/base/node/v1beta1/config';
|
|
17
20
|
const minGasPriceFetchTimeoutMs = 3_000;
|
|
18
21
|
const maxLiveFeeMultiplier = 10n;
|
|
19
|
-
const parseDecimal = (value) => {
|
|
20
|
-
if (!/^\d+(?:\.\d+)?$/.test(value))
|
|
21
|
-
return undefined;
|
|
22
|
-
const [whole, fraction = ''] = value.split('.');
|
|
23
|
-
const denominator = 10n ** BigInt(fraction.length);
|
|
24
|
-
const numerator = BigInt(`${whole}${fraction}`);
|
|
25
|
-
return { numerator, denominator };
|
|
26
|
-
};
|
|
27
22
|
const parseMinGasPriceEntry = (entry) => {
|
|
28
23
|
const match = entry.trim().match(/^(\d+(?:\.\d+)?)([a-zA-Z][a-zA-Z0-9/._:-]*)$/);
|
|
29
24
|
if (!match)
|
|
@@ -34,10 +29,6 @@ const parseMinGasPriceEntry = (entry) => {
|
|
|
34
29
|
return undefined;
|
|
35
30
|
return { ...decimal, denom };
|
|
36
31
|
};
|
|
37
|
-
export const getFeeAmountFromGasPrice = (gasLimit, gasPrice) => {
|
|
38
|
-
const total = gasLimit * gasPrice.numerator;
|
|
39
|
-
return (total + gasPrice.denominator - 1n) / gasPrice.denominator;
|
|
40
|
-
};
|
|
41
32
|
export const getMinGasPriceForDenom = (minimumGasPrice, targetDenom) => {
|
|
42
33
|
for (const entry of minimumGasPrice.split(',')) {
|
|
43
34
|
const parsed = parseMinGasPriceEntry(entry);
|
|
@@ -97,7 +88,7 @@ const fetchMinGasPrice = async (chain, { fetchImpl = fetch, signal } = {}) => {
|
|
|
97
88
|
timeoutController.cleanup();
|
|
98
89
|
}
|
|
99
90
|
};
|
|
100
|
-
|
|
91
|
+
const getGenericCosmosFeeAmount = async (coin, opts) => {
|
|
101
92
|
const floor = cosmosGasRecord[coin.chain];
|
|
102
93
|
try {
|
|
103
94
|
const minimumGasPrice = await fetchMinGasPrice(coin.chain, opts);
|
|
@@ -113,4 +104,20 @@ export const getCosmosFeeAmount = async (coin, opts = {}) => {
|
|
|
113
104
|
return floor;
|
|
114
105
|
}
|
|
115
106
|
};
|
|
107
|
+
export const getCosmosFeeAmount = async (coin, opts = {}) => {
|
|
108
|
+
if (coin.chain !== Chain.Osmosis)
|
|
109
|
+
return getGenericCosmosFeeAmount(coin, opts);
|
|
110
|
+
// Osmosis's real fee floor is enforced by its EIP-1559 `x/txfees` module,
|
|
111
|
+
// NOT the generic node-config `minimum-gas-price` (a per-node/operator-
|
|
112
|
+
// configurable value that doesn't track the live protocol floor, and can
|
|
113
|
+
// be clamped away by the anomaly guard above when it legitimately spikes).
|
|
114
|
+
// Run both lookups concurrently (each has its own timeout budget) rather
|
|
115
|
+
// than sequentially, and never pay less than the higher of the two -
|
|
116
|
+
// see osmosisDynamicFee.ts.
|
|
117
|
+
const [genericFee, dynamicFloor] = await Promise.all([
|
|
118
|
+
getGenericCosmosFeeAmount(coin, opts),
|
|
119
|
+
getOsmosisDynamicFeeFloor(getCosmosGasLimit(coin), opts),
|
|
120
|
+
]);
|
|
121
|
+
return dynamicFloor !== null && dynamicFloor > genericFee ? dynamicFloor : genericFee;
|
|
122
|
+
};
|
|
116
123
|
//# sourceMappingURL=gas.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gas.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/gas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAyB,MAAM,aAAa,CAAA;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"gas.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/gas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAyB,MAAM,aAAa,CAAA;AAE1D,OAAO,EAAE,wBAAwB,EAAsB,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC5F,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAA;AAE/D,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAE1D,MAAM,UAAU,GAAG,KAAK,CAAA;AAExB,MAAM,CAAC,MAAM,eAAe,GAA0C;IACpE,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU;IAC1B,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK;IACtB,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU;IAC1B,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,UAAU;IACzB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,iBAAiB;IAC/B,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,SAAS;IAC/B,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM;IACrB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,OAAO;CACvB,CAAA;AAWD,MAAM,qBAAqB,GAAG,kCAAkC,CAAA;AAChE,MAAM,yBAAyB,GAAG,KAAK,CAAA;AACvC,MAAM,oBAAoB,GAAG,GAAG,CAAA;AAEhC,MAAM,qBAAqB,GAAG,CAAC,KAAa,EAAE,EAAE;IAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAA;IAChF,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAE5B,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,KAAK,CAAA;IAC/B,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;IACpC,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAA;IAE9B,OAAO,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,CAAA;AAC9B,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,eAAuB,EAAE,WAAmB,EAA6B,EAAE;IAChH,KAAK,MAAM,KAAK,IAAI,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,MAAM,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAA;QAC3C,IAAI,MAAM,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,WAAW,CAAC,WAAW,EAAE,EAAE,CAAC;YAC9D,OAAO;gBACL,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;aAChC,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAED,MAAM,oBAAoB,GAAG,CAAC,MAAoB,EAAE,EAAE;IACpD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IAExC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAA;IACtC,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,KAAK,EAAE,CAAA;IACT,CAAC;SAAM,CAAC;QACN,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IAC1D,CAAC;IAED,OAAO;QACL,UAAU;QACV,OAAO,EAAE,GAAG,EAAE;YACZ,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC7C,CAAC;KACF,CAAA;AACH,CAAC,CAAA;AAED,MAAM,gBAAgB,GAAG,KAAK,EAAE,KAA4B,EAAE,EAAE,SAAS,GAAG,KAAK,EAAE,MAAM,KAAgB,EAAE,EAAE,EAAE;IAC7G,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAA;IACtD,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,yBAAyB,CAAC,CAAA;IACjG,MAAM,cAAc,GAAG,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;QAC7D,MAAM,aAAa,GAAG,GAAG,EAAE,CACzB,MAAM,CAAC,IAAI,KAAK,CAAC,iDAAiD,yBAAyB,IAAI,CAAC,CAAC,CAAA;QACnG,IAAI,iBAAiB,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAChD,aAAa,EAAE,CAAA;YACf,OAAM;QACR,CAAC;QAED,iBAAiB,CAAC,UAAU,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IAC9F,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC;YACxB,CAAC,KAAK,IAAI,EAAE;gBACV,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,GAAG,qBAAqB,EAAE,EAAE;oBACpF,MAAM,EAAE,iBAAiB,CAAC,UAAU,CAAC,MAAM;iBAC5C,CAAC,CAAA;gBAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;oBACjB,MAAM,IAAI,KAAK,CAAC,UAAU,KAAK,mCAAmC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;gBACtF,CAAC;gBAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA6B,CAAA;gBAEhE,OAAO,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAA;YACrC,CAAC,CAAC,EAAE;YACJ,cAAc;SACf,CAAC,CAAA;IACJ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,OAAO,CAAC,CAAA;QACrB,iBAAiB,CAAC,OAAO,EAAE,CAAA;IAC7B,CAAC;AACH,CAAC,CAAA;AAED,MAAM,yBAAyB,GAAG,KAAK,EAAE,IAAoC,EAAE,IAAe,EAAmB,EAAE;IACjH,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAEzC,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAChE,MAAM,QAAQ,GAAG,sBAAsB,CAAC,eAAe,EAAE,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QACxF,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAA;QAE3B,MAAM,WAAW,GAAG,wBAAwB,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAA;QAC/E,IAAI,WAAW,GAAG,KAAK,GAAG,oBAAoB;YAAE,OAAO,KAAK,CAAA;QAE5D,OAAO,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAA;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,IAAoC,EACpC,OAAkB,EAAE,EACH,EAAE;IACnB,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,OAAO;QAAE,OAAO,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAE9E,0EAA0E;IAC1E,wEAAwE;IACxE,yEAAyE;IACzE,2EAA2E;IAC3E,yEAAyE;IACzE,qEAAqE;IACrE,4BAA4B;IAC5B,MAAM,CAAC,UAAU,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACnD,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC;QACrC,yBAAyB,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;KACzD,CAAC,CAAA;IACF,OAAO,YAAY,KAAK,IAAI,IAAI,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,CAAA;AACvF,CAAC,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type FetchOpts = {
|
|
2
|
+
fetchImpl?: typeof fetch;
|
|
3
|
+
signal?: AbortSignal;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Live fee floor for Osmosis (base-denom units for `gasLimit`), or null when
|
|
7
|
+
* the lookup failed OR the computed floor exceeds the sanity ceiling
|
|
8
|
+
* (fail-open in both cases - caller should fall back to its own static or
|
|
9
|
+
* generically-derived fee).
|
|
10
|
+
*/
|
|
11
|
+
export declare function getOsmosisDynamicFeeFloor(gasLimit: bigint, opts?: FetchOpts): Promise<bigint | null>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=osmosisDynamicFee.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"osmosisDynamicFee.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/osmosisDynamicFee.ts"],"names":[],"mappings":"AAiDA,KAAK,SAAS,GAAG;IACf,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,CAAA;AA8CD;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAa9G"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Chain } from '../../Chain.js';
|
|
2
|
+
import { getFeeAmountFromGasPrice, parseDecimal } from './cosmosDecimal.js';
|
|
3
|
+
import { getCosmosRpcUrl } from './getCosmosRpcUrl.js';
|
|
4
|
+
/**
|
|
5
|
+
* Osmosis runs an EIP-1559-style dynamic base fee (`x/txfees` module): the
|
|
6
|
+
* real minimum required fee floats and is enforced at the protocol/consensus
|
|
7
|
+
* level, separate from a node's own locally-configured `minimum-gas-prices`
|
|
8
|
+
* (the generic `/cosmos/base/node/v1beta1/config` value `getCosmosFeeAmount`
|
|
9
|
+
* otherwise queries for every ibc-enabled chain). A tx priced against only
|
|
10
|
+
* the generic node config can still be rejected at broadcast with sdk error
|
|
11
|
+
* code 13 ("insufficient fees") when the live EIP-1559 base fee has risen
|
|
12
|
+
* above it.
|
|
13
|
+
*
|
|
14
|
+
* Live-verified in production (vultiagent-app): a broadcast failed with
|
|
15
|
+
* "base fee was 0.03 -> required 12000uosmo" - the generic min-gas-price
|
|
16
|
+
* mechanism did not reflect the real floor.
|
|
17
|
+
*
|
|
18
|
+
* Query Osmosis's own `cur_eip_base_fee` endpoint directly and apply
|
|
19
|
+
* headroom, rather than relying on the generic node-config value.
|
|
20
|
+
*
|
|
21
|
+
* Trust boundary (review finding, 2026-07-08): this floor feeds directly
|
|
22
|
+
* into the SIGNABLE fee, so a compromised/malfunctioning/MITM'd LCD (this
|
|
23
|
+
* endpoint honors a user-configured RPC override) returning a pathological
|
|
24
|
+
* `base_fee` must never (a) inflate the signable fee unboundedly, or (b)
|
|
25
|
+
* crash fee resolution outright (violating the fail-open contract every
|
|
26
|
+
* other error path here honors). Both are closed by computing the floor
|
|
27
|
+
* with exact rational arithmetic (no IEEE-754 `Number`, so no
|
|
28
|
+
* overflow-to-Infinity path) and clamping the result to a generous sanity
|
|
29
|
+
* ceiling before returning it.
|
|
30
|
+
*/
|
|
31
|
+
const OSMOSIS_BASE_FEE_HEADROOM = { numerator: 5n, denominator: 4n }; // 1.25x, exact
|
|
32
|
+
// Hard ceiling on the base-fee lookup: fail-open only covers ERRORS - a
|
|
33
|
+
// stalled txfees LCD (TCP up, no bytes) would otherwise hang the fee
|
|
34
|
+
// resolution indefinitely.
|
|
35
|
+
const OSMOSIS_BASE_FEE_TIMEOUT_MS = 5_000;
|
|
36
|
+
// Sanity backstop on the computed floor itself, independent of the fetch
|
|
37
|
+
// succeeding: the live-verified real incident this guards against needed
|
|
38
|
+
// ~12_000 uosmo against a 9_000n static floor (~1.3x) - 1000x is enormous
|
|
39
|
+
// headroom above any realistic base-fee spike while still bounding a
|
|
40
|
+
// pathological/malicious LCD response (e.g. a `base_fee` of "1000" would
|
|
41
|
+
// otherwise compute a ~375 OSMO fee for a standard send).
|
|
42
|
+
const OSMOSIS_DYNAMIC_FLOOR_MAX_MULTIPLIER = 1000n;
|
|
43
|
+
const OSMOSIS_STATIC_FLOOR = 9000n; // must match cosmosGasRecord[Chain.Osmosis] in gas.ts
|
|
44
|
+
async function fetchOsmosisBaseFeePerGas({ fetchImpl = fetch, signal } = {}) {
|
|
45
|
+
const controller = new AbortController();
|
|
46
|
+
const onAbort = () => controller.abort();
|
|
47
|
+
if (signal?.aborted) {
|
|
48
|
+
controller.abort();
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
52
|
+
}
|
|
53
|
+
const timeout = setTimeout(() => controller.abort(), OSMOSIS_BASE_FEE_TIMEOUT_MS);
|
|
54
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
55
|
+
const rejectTimeout = () => reject(new Error('Osmosis base-fee request timed out'));
|
|
56
|
+
if (controller.signal.aborted) {
|
|
57
|
+
rejectTimeout();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
controller.signal.addEventListener('abort', rejectTimeout, { once: true });
|
|
61
|
+
});
|
|
62
|
+
try {
|
|
63
|
+
const body = await Promise.race([
|
|
64
|
+
(async () => {
|
|
65
|
+
const res = await fetchImpl(`${getCosmosRpcUrl(Chain.Osmosis)}/osmosis/txfees/v1beta1/cur_eip_base_fee`, {
|
|
66
|
+
signal: controller.signal,
|
|
67
|
+
});
|
|
68
|
+
if (!res.ok)
|
|
69
|
+
throw new Error(`Osmosis base-fee request failed: ${res.status}`);
|
|
70
|
+
return (await res.json());
|
|
71
|
+
})(),
|
|
72
|
+
timeoutPromise,
|
|
73
|
+
]);
|
|
74
|
+
const baseFee = body?.base_fee;
|
|
75
|
+
if (typeof baseFee !== 'string')
|
|
76
|
+
return null;
|
|
77
|
+
// Exact rational parse - no `Number()` coercion, so no path to
|
|
78
|
+
// overflow-to-Infinity on a pathologically long-but-regex-valid string.
|
|
79
|
+
return parseDecimal(baseFee) ?? null;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
clearTimeout(timeout);
|
|
86
|
+
signal?.removeEventListener('abort', onAbort);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Live fee floor for Osmosis (base-denom units for `gasLimit`), or null when
|
|
91
|
+
* the lookup failed OR the computed floor exceeds the sanity ceiling
|
|
92
|
+
* (fail-open in both cases - caller should fall back to its own static or
|
|
93
|
+
* generically-derived fee).
|
|
94
|
+
*/
|
|
95
|
+
export async function getOsmosisDynamicFeeFloor(gasLimit, opts = {}) {
|
|
96
|
+
const baseFee = await fetchOsmosisBaseFeePerGas(opts);
|
|
97
|
+
if (baseFee === null)
|
|
98
|
+
return null;
|
|
99
|
+
const withHeadroom = {
|
|
100
|
+
numerator: baseFee.numerator * OSMOSIS_BASE_FEE_HEADROOM.numerator,
|
|
101
|
+
denominator: baseFee.denominator * OSMOSIS_BASE_FEE_HEADROOM.denominator,
|
|
102
|
+
};
|
|
103
|
+
const fee = getFeeAmountFromGasPrice(gasLimit, withHeadroom);
|
|
104
|
+
if (fee > OSMOSIS_STATIC_FLOOR * OSMOSIS_DYNAMIC_FLOOR_MAX_MULTIPLIER)
|
|
105
|
+
return null;
|
|
106
|
+
return fee;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=osmosisDynamicFee.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"osmosisDynamicFee.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/osmosisDynamicFee.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,18 @@
|
|
|
11
11
|
* the extra gas. Below or equal to the static limit, both values are left
|
|
12
12
|
* untouched, keeping the non-dynamic path byte-identical.
|
|
13
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* COSMOS-02: an IBC transfer (ICS-20 `MsgTransfer`, optionally PFM-forwarded
|
|
16
|
+
* via memo) does measurably more work on the source leg than a plain bank
|
|
17
|
+
* send — channel-state writes plus a relayer event — so the flat per-chain
|
|
18
|
+
* limit (calibrated for `MsgSend`) is undersized and can run out of gas
|
|
19
|
+
* mid-execution: the fee is spent, the transfer fails, and funds don't move
|
|
20
|
+
* but the fee is still burned. PFM hops are budgeted by each forwarding
|
|
21
|
+
* chain, not the source leg here, so ×2 headroom on the source leg is
|
|
22
|
+
* sufficient. Mirrors the app's own `IBC_GAS_MULTIPLIER`
|
|
23
|
+
* (vultiagent-app/src/services/cosmosTx.ts).
|
|
24
|
+
*/
|
|
25
|
+
export declare const IBC_GAS_MULTIPLIER = 2n;
|
|
14
26
|
type ResolveCosmosGasFeeInput = {
|
|
15
27
|
/** static per-chain fee amount already stored in `CosmosSpecific.gas` */
|
|
16
28
|
gas: bigint;
|
|
@@ -18,11 +30,20 @@ type ResolveCosmosGasFeeInput = {
|
|
|
18
30
|
relayedGasLimit: bigint | undefined;
|
|
19
31
|
/** static per-chain gas limit (`getCosmosGasLimit`) */
|
|
20
32
|
staticGasLimit: bigint;
|
|
33
|
+
/**
|
|
34
|
+
* Whether this tx is an IBC transfer (`MsgTransfer`, optionally
|
|
35
|
+
* PFM-forwarded). Applies `IBC_GAS_MULTIPLIER` to both the static gas
|
|
36
|
+
* limit and its matching fee before resolving against a relayed limit.
|
|
37
|
+
* Defaults to `false` so every other Cosmos message (plain sends, wasm
|
|
38
|
+
* executes, staking) keeps paying the calibrated flat fee — a blanket
|
|
39
|
+
* multiplier on all Cosmos messages would overpay non-IBC sends.
|
|
40
|
+
*/
|
|
41
|
+
isIbcTransfer?: boolean;
|
|
21
42
|
};
|
|
22
43
|
type ResolveCosmosGasFeeResult = {
|
|
23
44
|
resolvedGasLimit: bigint;
|
|
24
45
|
feeAmount: bigint;
|
|
25
46
|
};
|
|
26
|
-
export declare const resolveCosmosGasFee: ({ gas, relayedGasLimit, staticGasLimit, }: ResolveCosmosGasFeeInput) => ResolveCosmosGasFeeResult;
|
|
47
|
+
export declare const resolveCosmosGasFee: ({ gas, relayedGasLimit, staticGasLimit, isIbcTransfer, }: ResolveCosmosGasFeeInput) => ResolveCosmosGasFeeResult;
|
|
27
48
|
export {};
|
|
28
49
|
//# sourceMappingURL=resolveCosmosGasFee.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveCosmosGasFee.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/resolveCosmosGasFee.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,KAAK,wBAAwB,GAAG;IAC9B,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAA;IACX,gFAAgF;IAChF,eAAe,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"resolveCosmosGasFee.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/resolveCosmosGasFee.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAA;AAEpC,KAAK,wBAAwB,GAAG;IAC9B,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAA;IACX,gFAAgF;IAChF,eAAe,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAA;IACtB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,KAAK,yBAAyB,GAAG;IAC/B,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,0DAKjC,wBAAwB,KAAG,yBAY7B,CAAA"}
|
|
@@ -1,6 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the effective Cosmos gas limit and fee amount for a native send,
|
|
3
|
+
* honoring a relayed dynamic `gas_limit` when present.
|
|
4
|
+
*
|
|
5
|
+
* Shared by the signing-inputs resolver (what gets signed) and the fee-display
|
|
6
|
+
* resolver (what the user sees) so the shown and signed fee can never drift —
|
|
7
|
+
* the gas limit is part of the SignDoc, so both sides MUST agree.
|
|
8
|
+
*
|
|
9
|
+
* When the relayed limit exceeds the static per-chain limit, the fee amount is
|
|
10
|
+
* scaled proportionally with a ceiling (exact integer math) so the tx pays for
|
|
11
|
+
* the extra gas. Below or equal to the static limit, both values are left
|
|
12
|
+
* untouched, keeping the non-dynamic path byte-identical.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* COSMOS-02: an IBC transfer (ICS-20 `MsgTransfer`, optionally PFM-forwarded
|
|
16
|
+
* via memo) does measurably more work on the source leg than a plain bank
|
|
17
|
+
* send — channel-state writes plus a relayer event — so the flat per-chain
|
|
18
|
+
* limit (calibrated for `MsgSend`) is undersized and can run out of gas
|
|
19
|
+
* mid-execution: the fee is spent, the transfer fails, and funds don't move
|
|
20
|
+
* but the fee is still burned. PFM hops are budgeted by each forwarding
|
|
21
|
+
* chain, not the source leg here, so ×2 headroom on the source leg is
|
|
22
|
+
* sufficient. Mirrors the app's own `IBC_GAS_MULTIPLIER`
|
|
23
|
+
* (vultiagent-app/src/services/cosmosTx.ts).
|
|
24
|
+
*/
|
|
25
|
+
export const IBC_GAS_MULTIPLIER = 2n;
|
|
26
|
+
export const resolveCosmosGasFee = ({ gas, relayedGasLimit, staticGasLimit, isIbcTransfer = false, }) => {
|
|
27
|
+
const effectiveStaticGasLimit = isIbcTransfer ? staticGasLimit * IBC_GAS_MULTIPLIER : staticGasLimit;
|
|
28
|
+
const effectiveGas = isIbcTransfer ? gas * IBC_GAS_MULTIPLIER : gas;
|
|
29
|
+
const resolvedGasLimit = relayedGasLimit && relayedGasLimit > 0n ? relayedGasLimit : effectiveStaticGasLimit;
|
|
30
|
+
const feeAmount = resolvedGasLimit > effectiveStaticGasLimit
|
|
31
|
+
? (effectiveGas * resolvedGasLimit + effectiveStaticGasLimit - 1n) / effectiveStaticGasLimit
|
|
32
|
+
: effectiveGas;
|
|
4
33
|
return { resolvedGasLimit, feeAmount };
|
|
5
34
|
};
|
|
6
35
|
//# sourceMappingURL=resolveCosmosGasFee.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveCosmosGasFee.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/resolveCosmosGasFee.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolveCosmosGasFee.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/cosmos/resolveCosmosGasFee.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAA;AAyBpC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,EAClC,GAAG,EACH,eAAe,EACf,cAAc,EACd,aAAa,GAAG,KAAK,GACI,EAA6B,EAAE;IACxD,MAAM,uBAAuB,GAAG,aAAa,CAAC,CAAC,CAAC,cAAc,GAAG,kBAAkB,CAAC,CAAC,CAAC,cAAc,CAAA;IACpG,MAAM,YAAY,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAA;IAEnE,MAAM,gBAAgB,GAAG,eAAe,IAAI,eAAe,GAAG,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,uBAAuB,CAAA;IAE5G,MAAM,SAAS,GACb,gBAAgB,GAAG,uBAAuB;QACxC,CAAC,CAAC,CAAC,YAAY,GAAG,gBAAgB,GAAG,uBAAuB,GAAG,EAAE,CAAC,GAAG,uBAAuB;QAC5F,CAAC,CAAC,YAAY,CAAA;IAElB,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,CAAA;AACxC,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"minUtxo.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/utxo/minUtxo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,cAAc,EAAE,MAAM,aAAa,CAAA;AAEnD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"minUtxo.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/utxo/minUtxo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,cAAc,EAAE,MAAM,aAAa,CAAA;AAEnD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAclD,CAAA"}
|
|
@@ -3,7 +3,13 @@ export const minUtxo = {
|
|
|
3
3
|
[Chain.Cardano]: 1400000n,
|
|
4
4
|
[Chain.Bitcoin]: 546n,
|
|
5
5
|
[Chain.Dogecoin]: 1000000n,
|
|
6
|
-
|
|
6
|
+
// UTXO-02 (audit r2): Litecoin's DUST_RELAY_TX_FEE is ~10x Bitcoin's, so its real standard dust threshold
|
|
7
|
+
// is ~2_940 litoshi for a P2WPKH output (the scriptType LTC uses), ~5_460 legacy P2PKH — NOT 1_000. At the
|
|
8
|
+
// old 1_000n, a change output of 1_001..2_939 litoshi was treated as spendable but is non-standard dust, so
|
|
9
|
+
// the tx could be rejected by relays / get stuck. Use the P2WPKH standard (2_940). KEEP IN SYNC with the
|
|
10
|
+
// per-chain dustLimit map in packages/sdk/src/chains/utxo/tx.ts (UTXO_SPECS). BCH/Dash/Zcash stay at 1_000n
|
|
11
|
+
// — that's already at/above their real ~546 dust (BTC-like relay fee), so it's conservative, not dangerous.
|
|
12
|
+
[Chain.Litecoin]: 2940n,
|
|
7
13
|
[Chain.BitcoinCash]: 1000n,
|
|
8
14
|
[Chain.Dash]: 1000n,
|
|
9
15
|
[Chain.Zcash]: 1000n,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"minUtxo.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/utxo/minUtxo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAkB,MAAM,aAAa,CAAA;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAmC;IACrD,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,QAAU;IAC3B,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI;IACrB,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,QAAU;IAC5B,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAM;IACxB,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,KAAM;IAC3B,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAM;IACpB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAM;CACtB,CAAA"}
|
|
1
|
+
{"version":3,"file":"minUtxo.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/utxo/minUtxo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAkB,MAAM,aAAa,CAAA;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAmC;IACrD,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,QAAU;IAC3B,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI;IACrB,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,QAAU;IAC5B,0GAA0G;IAC1G,2GAA2G;IAC3G,4GAA4G;IAC5G,yGAAyG;IACzG,4GAA4G;IAC5G,4GAA4G;IAC5G,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAM;IACxB,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,KAAM;IAC3B,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAM;IACpB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAM;CACtB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"findByTicker.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/coin/utils/findByTicker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAE9B,KAAK,iBAAiB,CAAC,CAAC,SAAS,IAAI,IAAI;IACvC,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;
|
|
1
|
+
{"version":3,"file":"findByTicker.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/coin/utils/findByTicker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAE9B,KAAK,iBAAiB,CAAC,CAAC,SAAS,IAAI,IAAI;IACvC,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAOD,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,IAAI,EAAE,mBAAmB,iBAAiB,CAAC,CAAC,CAAC,KAAG,CAAC,GAAG,IAU1F,CAAA"}
|
|
@@ -1,2 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
// SDK2-02 (audit r2): a bare ticker like "USDC" exists on many chains (Ethereum/Polygon/Arbitrum/Base/…).
|
|
2
|
+
// The old `coins.find(c => c.ticker === ticker)` returned the ARRAY-ORDER first match — if this ever got
|
|
3
|
+
// wired into a fund path, "USDC" would silently resolve to whichever chain happened to be first, sending to
|
|
4
|
+
// the wrong network. Refuse to guess: return the unique match (or null when absent), and THROW when the
|
|
5
|
+
// ticker is ambiguous across more than one chain so the caller must disambiguate by chain.
|
|
6
|
+
export const findByTicker = ({ coins, ticker }) => {
|
|
7
|
+
const matches = coins.filter(c => c.ticker === ticker);
|
|
8
|
+
if (matches.length === 0)
|
|
9
|
+
return null;
|
|
10
|
+
const chains = new Set(matches.map(c => c.chain));
|
|
11
|
+
if (chains.size > 1) {
|
|
12
|
+
throw new Error(`findByTicker: ticker "${ticker}" is ambiguous across ${chains.size} chains (${[...chains].join(', ')}); pass a chain-scoped lookup instead of a bare ticker`);
|
|
13
|
+
}
|
|
14
|
+
return matches[0] ?? null;
|
|
15
|
+
};
|
|
2
16
|
//# sourceMappingURL=findByTicker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"findByTicker.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/coin/utils/findByTicker.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,YAAY,GAAG,CAAiB,EAAE,KAAK,EAAE,MAAM,EAAwB,EAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"findByTicker.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/coin/utils/findByTicker.ts"],"names":[],"mappings":"AAOA,0GAA0G;AAC1G,yGAAyG;AACzG,4GAA4G;AAC5G,wGAAwG;AACxG,2FAA2F;AAC3F,MAAM,CAAC,MAAM,YAAY,GAAG,CAAiB,EAAE,KAAK,EAAE,MAAM,EAAwB,EAAY,EAAE;IAChG,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAA;IACtD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACrC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;IACjD,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,yBAAyB,MAAM,yBAAyB,MAAM,CAAC,IAAI,YAAY,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,wDAAwD,CAC9J,CAAA;IACH,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;AAC3B,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getCowSwapQuote.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/swap/general/cowswap/api/getCowSwapQuote.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,
|
|
1
|
+
{"version":3,"file":"getCowSwapQuote.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/swap/general/cowswap/api/getCowSwapQuote.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAIL,kBAAkB,EAEnB,MAAM,WAAW,CAAA;AAiBlB,KAAK,oBAAoB,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,kBAAkB,CAAA;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AASD,wBAAsB,eAAe,CAAC,EACpC,SAAS,EACT,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,WAAW,EACX,YAAY,GACb,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsIlD"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { queryUrl } from '@vultisig/lib-utils/query/queryUrl';
|
|
2
|
-
import { COWSWAP_DEFAULT_AFFILIATE_BPS, COWSWAP_FEE_RECIPIENT, KNOWN_PERMIT_TOKENS, } from '../config.js';
|
|
2
|
+
import { COWSWAP_DEFAULT_AFFILIATE_BPS, COWSWAP_FEE_RECIPIENT, COWSWAP_VALID_TO_SECONDS, KNOWN_PERMIT_TOKENS, } from '../config.js';
|
|
3
3
|
import { buildCowSwapAppData, keccak256Hex } from '../sign/buildCowSwapOrder.js';
|
|
4
|
+
// This SDK only ever requests a sell order that must fill completely — named here (rather than
|
|
5
|
+
// inlined at both the request body below AND the AGG-01 response-echo checks) so the two stay
|
|
6
|
+
// in lockstep if that ever changes; comparing against a variable is more robust than duplicating
|
|
7
|
+
// the literal in two places (codex review, PR #1082).
|
|
8
|
+
const REQUESTED_KIND = 'sell';
|
|
9
|
+
const REQUESTED_PARTIALLY_FILLABLE = false;
|
|
4
10
|
export async function getCowSwapQuote({ sellToken, buyToken, sellAmount, from, receiver, chainConfig, affiliateBps, }) {
|
|
5
11
|
const bps = affiliateBps ?? COWSWAP_DEFAULT_AFFILIATE_BPS;
|
|
6
12
|
const appData = buildCowSwapAppData(bps, COWSWAP_FEE_RECIPIENT);
|
|
@@ -11,9 +17,9 @@ export async function getCowSwapQuote({ sellToken, buyToken, sellAmount, from, r
|
|
|
11
17
|
sellAmountBeforeFee: sellAmount.toString(),
|
|
12
18
|
from: from.toLowerCase(),
|
|
13
19
|
receiver: receiver.toLowerCase(),
|
|
14
|
-
kind:
|
|
20
|
+
kind: REQUESTED_KIND,
|
|
15
21
|
signingScheme: 'eip712',
|
|
16
|
-
partiallyFillable:
|
|
22
|
+
partiallyFillable: REQUESTED_PARTIALLY_FILLABLE,
|
|
17
23
|
appData,
|
|
18
24
|
appDataHash,
|
|
19
25
|
};
|
|
@@ -22,6 +28,52 @@ export async function getCowSwapQuote({ sellToken, buyToken, sellAmount, from, r
|
|
|
22
28
|
body,
|
|
23
29
|
});
|
|
24
30
|
const { quote } = response;
|
|
31
|
+
// AGG-01 fund-safety fix (round-2 spec-level audit): sellToken/buyToken/kind/
|
|
32
|
+
// partiallyFillable were previously taken straight from this untrusted /quote response and
|
|
33
|
+
// signed as-is via the EIP-712 GPv2 Order struct (buildCowSwapOrderTypedData.ts). `receiver`
|
|
34
|
+
// just below already uses the caller-supplied local, not quote.receiver — the "don't trust
|
|
35
|
+
// the API for security-critical fields" pattern already existed here, just not applied to
|
|
36
|
+
// these 4 fields. A compromised/buggy apiBase response substituting a token address, or
|
|
37
|
+
// flipping `kind` from 'sell' to 'buy', would get signed: a `kind` flip specifically inverts
|
|
38
|
+
// GPv2's semantics (sell: sellAmount is authoritative post-fee; buy: buyAmount is the fixed
|
|
39
|
+
// target, sellAmount becomes a ceiling) while grossSellAmount below is computed assuming
|
|
40
|
+
// sell-order semantics — the SDK would sign a fundamentally different economic contract than
|
|
41
|
+
// the one it believes it built. This SDK only ever requests kind:'sell' + partiallyFillable:
|
|
42
|
+
// false (the request body above), so any other value in the response is definitionally wrong.
|
|
43
|
+
if (quote.sellToken.toLowerCase() !== sellToken.toLowerCase()) {
|
|
44
|
+
throw new Error(`CowSwap quote returned a mismatched sellToken (requested ${sellToken}, got ${quote.sellToken}) — refusing to sign.`);
|
|
45
|
+
}
|
|
46
|
+
if (quote.buyToken.toLowerCase() !== buyToken.toLowerCase()) {
|
|
47
|
+
throw new Error(`CowSwap quote returned a mismatched buyToken (requested ${buyToken}, got ${quote.buyToken}) — refusing to sign.`);
|
|
48
|
+
}
|
|
49
|
+
if (quote.kind !== REQUESTED_KIND) {
|
|
50
|
+
throw new Error(`CowSwap quote returned kind '${quote.kind}' (requested '${REQUESTED_KIND}') — refusing to sign.`);
|
|
51
|
+
}
|
|
52
|
+
if (quote.partiallyFillable !== REQUESTED_PARTIALLY_FILLABLE) {
|
|
53
|
+
throw new Error(`CowSwap quote returned partiallyFillable=${quote.partiallyFillable} (requested ${REQUESTED_PARTIALLY_FILLABLE}) — refusing to sign.`);
|
|
54
|
+
}
|
|
55
|
+
// AGG-01 follow-up (codex review, PR #1082): validTo is entirely server-determined (the
|
|
56
|
+
// request sends no validTo of its own, so there is no request-side value to equality-check
|
|
57
|
+
// against — unlike the fields above). Still part of the signed Order struct, so bound it to a
|
|
58
|
+
// generous ceiling (buildCowSwapOrder.ts's sibling implementation computes its OWN local
|
|
59
|
+
// validTo instead of trusting the response at all — this is the softer version of that same
|
|
60
|
+
// instinct): reject a response whose validTo is unreasonably far in the future (a malicious/
|
|
61
|
+
// malfunctioning response making the order executable far beyond user intent) or already in
|
|
62
|
+
// the past (an expired/garbage quote).
|
|
63
|
+
//
|
|
64
|
+
// The ceiling is 4x COWSWAP_VALID_TO_SECONDS, not 2x — live-verified (2026-07-08, real
|
|
65
|
+
// api.cow.fi/mainnet request) that CoW's actual returned validTo is `now + 1800s` (30 min),
|
|
66
|
+
// NOT ~15 min as COWSWAP_VALID_TO_SECONDS alone would suggest (that constant is
|
|
67
|
+
// buildCowSwapOrder.ts's OWN locally-computed TTL, a different value from what the live API
|
|
68
|
+
// returns). A 2x/30-min cap would have sat RIGHT AT the observed real value with zero headroom
|
|
69
|
+
// for clock skew or normal timing variance — exactly the false-block risk the zkSync router
|
|
70
|
+
// case flagged (team-lead review). 4x/60-min gives real headroom over the observed 30-min
|
|
71
|
+
// reality while still catching genuinely-absurd values (hours/days out — the actual threat).
|
|
72
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
73
|
+
const maxReasonableValidTo = nowSeconds + 4 * COWSWAP_VALID_TO_SECONDS;
|
|
74
|
+
if (quote.validTo <= nowSeconds || quote.validTo > maxReasonableValidTo) {
|
|
75
|
+
throw new Error(`CowSwap quote returned an unreasonable validTo (${quote.validTo}, now=${nowSeconds}, max=${maxReasonableValidTo}) — refusing to sign.`);
|
|
76
|
+
}
|
|
25
77
|
const permitRequired = KNOWN_PERMIT_TOKENS[chainConfig.chainId]?.some(addr => addr.toLowerCase() === sellToken.toLowerCase());
|
|
26
78
|
// The CoW orderbook requires submitted orders to carry `feeAmount = 0` — the
|
|
27
79
|
// network/solver fee is folded into the price (taken from order surplus), not
|
|
@@ -32,6 +84,19 @@ export async function getCowSwapQuote({ sellToken, buyToken, sellAmount, from, r
|
|
|
32
84
|
// quoted for the net amount. This value is signed AND submitted, so both stay
|
|
33
85
|
// consistent.
|
|
34
86
|
const grossSellAmount = (BigInt(quote.sellAmount) + BigInt(quote.feeAmount)).toString();
|
|
87
|
+
// AGG-01 follow-up (codex review, PR #1082): grossSellAmount becomes the signed Order's
|
|
88
|
+
// sellAmount — the SDK's authorized spend — and was, like the 4 fields above, taken entirely
|
|
89
|
+
// from the untrusted response with no check against what was actually requested. CoW's
|
|
90
|
+
// `sellAmountBeforeFee` request param is specifically designed so the API solves for
|
|
91
|
+
// `quote.sellAmount + quote.feeAmount === sellAmountBeforeFee` exactly — live-verified against
|
|
92
|
+
// the real api.cow.fi/mainnet quote endpoint on two different pairs/amounts on 2026-07-08,
|
|
93
|
+
// both matched byte-for-byte, not just approximately. A compromised/buggy response inflating
|
|
94
|
+
// sellAmount or feeAmount would sign authorization to sell MORE than the user requested —
|
|
95
|
+
// the most direct fund-safety consequence of any field in this struct. Same "refuse to sign
|
|
96
|
+
// on mismatch" treatment as the fields above.
|
|
97
|
+
if (grossSellAmount !== sellAmount.toString()) {
|
|
98
|
+
throw new Error(`CowSwap quote's sellAmount+feeAmount (${grossSellAmount}) does not match the requested sellAmountBeforeFee (${sellAmount}) — refusing to sign.`);
|
|
99
|
+
}
|
|
35
100
|
return {
|
|
36
101
|
dstAmount: quote.buyAmount,
|
|
37
102
|
provider: 'cowswap',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getCowSwapQuote.js","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/swap/general/cowswap/api/getCowSwapQuote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAA;AAG7D,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,
|
|
1
|
+
{"version":3,"file":"getCowSwapQuote.js","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/swap/general/cowswap/api/getCowSwapQuote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAA;AAG7D,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,wBAAwB,EAExB,mBAAmB,GACpB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AA0B7E,+FAA+F;AAC/F,8FAA8F;AAC9F,iGAAiG;AACjG,sDAAsD;AACtD,MAAM,cAAc,GAAG,MAAe,CAAA;AACtC,MAAM,4BAA4B,GAAG,KAAc,CAAA;AAEnD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EACpC,SAAS,EACT,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,WAAW,EACX,YAAY,GACS;IACrB,MAAM,GAAG,GAAG,YAAY,IAAI,6BAA6B,CAAA;IACzD,MAAM,OAAO,GAAG,mBAAmB,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAA;IAC/D,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IAEzC,MAAM,IAAI,GAAwB;QAChC,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE;QAClC,QAAQ,EAAE,QAAQ,CAAC,WAAW,EAAE;QAChC,mBAAmB,EAAE,UAAU,CAAC,QAAQ,EAAE;QAC1C,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE;QACxB,QAAQ,EAAE,QAAQ,CAAC,WAAW,EAAE;QAChC,IAAI,EAAE,cAAc;QACpB,aAAa,EAAE,QAAQ;QACvB,iBAAiB,EAAE,4BAA4B;QAC/C,OAAO;QACP,WAAW;KACZ,CAAA;IAED,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAA0B,GAAG,WAAW,CAAC,OAAO,eAAe,EAAE;QAC9F,MAAM,EAAE,MAAM;QACd,IAAI;KACL,CAAC,CAAA;IAEF,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAA;IAE1B,8EAA8E;IAC9E,2FAA2F;IAC3F,6FAA6F;IAC7F,2FAA2F;IAC3F,0FAA0F;IAC1F,wFAAwF;IACxF,6FAA6F;IAC7F,4FAA4F;IAC5F,yFAAyF;IACzF,6FAA6F;IAC7F,6FAA6F;IAC7F,8FAA8F;IAC9F,IAAI,KAAK,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC;QAC9D,MAAM,IAAI,KAAK,CACb,4DAA4D,SAAS,SAAS,KAAK,CAAC,SAAS,uBAAuB,CACrH,CAAA;IACH,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CACb,2DAA2D,QAAQ,SAAS,KAAK,CAAC,QAAQ,uBAAuB,CAClH,CAAA;IACH,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,CAAC,IAAI,iBAAiB,cAAc,wBAAwB,CAAC,CAAA;IACpH,CAAC;IACD,IAAI,KAAK,CAAC,iBAAiB,KAAK,4BAA4B,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CACb,4CAA4C,KAAK,CAAC,iBAAiB,eAAe,4BAA4B,uBAAuB,CACtI,CAAA;IACH,CAAC;IACD,wFAAwF;IACxF,2FAA2F;IAC3F,8FAA8F;IAC9F,yFAAyF;IACzF,4FAA4F;IAC5F,6FAA6F;IAC7F,4FAA4F;IAC5F,uCAAuC;IACvC,EAAE;IACF,uFAAuF;IACvF,4FAA4F;IAC5F,gFAAgF;IAChF,4FAA4F;IAC5F,+FAA+F;IAC/F,4FAA4F;IAC5F,0FAA0F;IAC1F,6FAA6F;IAC7F,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IAChD,MAAM,oBAAoB,GAAG,UAAU,GAAG,CAAC,GAAG,wBAAwB,CAAA;IACtE,IAAI,KAAK,CAAC,OAAO,IAAI,UAAU,IAAI,KAAK,CAAC,OAAO,GAAG,oBAAoB,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CACb,mDAAmD,KAAK,CAAC,OAAO,SAAS,UAAU,SAAS,oBAAoB,uBAAuB,CACxI,CAAA;IACH,CAAC;IAED,MAAM,cAAc,GAAG,mBAAmB,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,IAAI,CACnE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,CACvD,CAAA;IAED,6EAA6E;IAC7E,8EAA8E;IAC9E,+DAA+D;IAC/D,4EAA4E;IAC5E,+EAA+E;IAC/E,4EAA4E;IAC5E,8EAA8E;IAC9E,cAAc;IACd,MAAM,eAAe,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;IAEvF,wFAAwF;IACxF,6FAA6F;IAC7F,uFAAuF;IACvF,qFAAqF;IACrF,+FAA+F;IAC/F,2FAA2F;IAC3F,6FAA6F;IAC7F,0FAA0F;IAC1F,4FAA4F;IAC5F,8CAA8C;IAC9C,IAAI,eAAe,KAAK,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,yCAAyC,eAAe,uDAAuD,UAAU,uBAAuB,CACjJ,CAAA;IACH,CAAC;IAED,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,QAAQ,EAAE,SAAS;QACnB,EAAE,EAAE;YACF,aAAa,EAAE;gBACb,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,QAAQ;gBACR,UAAU,EAAE,eAAe;gBAC3B,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,OAAO;gBACP,WAAW;gBACX,SAAS,EAAE,GAAG;gBACd,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;gBAC1C,gBAAgB,EAAE,OAAO;gBACzB,eAAe,EAAE,OAAO;gBACxB,OAAO,EAAE,WAAW,CAAC,OAAO;gBAC5B,OAAO,EAAE,WAAW,CAAC,OAAO;gBAC5B,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpD;SACF;KACF,CAAA;AACH,CAAC"}
|
|
@@ -1 +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;AACrF,OAAO,EAEL,KAAK,sBAAsB,EAG5B,MAAM,kDAAkD,CAAA;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,oEAAoE,CAAA;
|
|
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;AACrF,OAAO,EAEL,KAAK,sBAAsB,EAG5B,MAAM,kDAAkD,CAAA;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,oEAAoE,CAAA;AAE5G,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,aAAa,CAAC,EAAE,sBAAsB,CAAA;IACtC,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AA2CD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB,GAAU,iEAOvC,KAAK,KAAG,OAAO,CAAC,gBAAgB,CA8FlC,CAAA"}
|