@kittycorn-labs/universal-router-sdk 1.4.42 → 2.0.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/README.md +312 -0
- package/dist/cjs/src/abis/UniversalRouter.json +740 -0
- package/dist/{entities → cjs/src/entities}/Command.d.ts +12 -12
- package/dist/cjs/src/entities/Command.js +9 -0
- package/dist/cjs/src/entities/Command.js.map +1 -0
- package/dist/cjs/src/entities/actions/across.d.ts +21 -0
- package/dist/cjs/src/entities/actions/across.js +7 -0
- package/dist/cjs/src/entities/actions/across.js.map +1 -0
- package/dist/{entities → cjs/src/entities}/actions/index.d.ts +3 -2
- package/dist/cjs/src/entities/actions/index.js +7 -0
- package/dist/cjs/src/entities/actions/index.js.map +1 -0
- package/dist/cjs/src/entities/actions/uniswap.d.ts +94 -0
- package/dist/cjs/src/entities/actions/uniswap.js +740 -0
- package/dist/cjs/src/entities/actions/uniswap.js.map +1 -0
- package/dist/{entities → cjs/src/entities}/actions/unwrapWETH.d.ts +12 -12
- package/dist/cjs/src/entities/actions/unwrapWETH.js +33 -0
- package/dist/cjs/src/entities/actions/unwrapWETH.js.map +1 -0
- package/dist/{entities → cjs/src/entities}/index.d.ts +2 -2
- package/dist/cjs/src/entities/index.js +6 -0
- package/dist/cjs/src/entities/index.js.map +1 -0
- package/dist/cjs/src/index.d.ts +19 -0
- package/dist/cjs/src/index.js +43 -0
- package/dist/cjs/src/index.js.map +1 -0
- package/dist/cjs/src/kittycorn.d.ts +9 -0
- package/dist/cjs/src/kittycorn.js +26 -0
- package/dist/cjs/src/kittycorn.js.map +1 -0
- package/dist/cjs/src/swapRouter.d.ts +123 -0
- package/dist/cjs/src/swapRouter.js +445 -0
- package/dist/cjs/src/swapRouter.js.map +1 -0
- package/dist/cjs/src/types/encodeSwaps.d.ts +197 -0
- package/dist/cjs/src/types/encodeSwaps.js +3 -0
- package/dist/cjs/src/types/encodeSwaps.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/commandParser.d.ts +36 -34
- package/dist/cjs/src/utils/commandParser.js +154 -0
- package/dist/cjs/src/utils/commandParser.js.map +1 -0
- package/dist/cjs/src/utils/computeEncodeSwapsAmounts.d.ts +8 -0
- package/dist/cjs/src/utils/computeEncodeSwapsAmounts.js +46 -0
- package/dist/cjs/src/utils/computeEncodeSwapsAmounts.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/constants.d.ts +46 -30
- package/dist/cjs/src/utils/constants.js +777 -0
- package/dist/cjs/src/utils/constants.js.map +1 -0
- package/dist/cjs/src/utils/directTransfers.d.ts +15 -0
- package/dist/cjs/src/utils/directTransfers.js +381 -0
- package/dist/cjs/src/utils/directTransfers.js.map +1 -0
- package/dist/cjs/src/utils/eip712.d.ts +17 -0
- package/dist/cjs/src/utils/eip712.js +44 -0
- package/dist/cjs/src/utils/eip712.js.map +1 -0
- package/dist/cjs/src/utils/encodeSwapStep.d.ts +4 -0
- package/dist/cjs/src/utils/encodeSwapStep.js +62 -0
- package/dist/cjs/src/utils/encodeSwapStep.js.map +1 -0
- package/dist/cjs/src/utils/encodeV4Action.d.ts +7 -0
- package/dist/cjs/src/utils/encodeV4Action.js +84 -0
- package/dist/cjs/src/utils/encodeV4Action.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/getCurrencyAddress.d.ts +2 -2
- package/dist/cjs/src/utils/getCurrencyAddress.js +9 -0
- package/dist/cjs/src/utils/getCurrencyAddress.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/inputTokens.d.ts +23 -23
- package/dist/cjs/src/utils/inputTokens.js +58 -0
- package/dist/cjs/src/utils/inputTokens.js.map +1 -0
- package/dist/cjs/src/utils/normalizeEncodeSwapsSpec.d.ts +6 -0
- package/dist/cjs/src/utils/normalizeEncodeSwapsSpec.js +34 -0
- package/dist/cjs/src/utils/normalizeEncodeSwapsSpec.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/numbers.d.ts +7 -6
- package/dist/cjs/src/utils/numbers.js +27 -0
- package/dist/cjs/src/utils/numbers.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/pathCurrency.d.ts +3 -3
- package/dist/cjs/src/utils/pathCurrency.js +27 -0
- package/dist/cjs/src/utils/pathCurrency.js.map +1 -0
- package/dist/cjs/src/utils/portionFees.d.ts +14 -0
- package/dist/cjs/src/utils/portionFees.js +40 -0
- package/dist/cjs/src/utils/portionFees.js.map +1 -0
- package/dist/cjs/src/utils/routerBalanceSteps.d.ts +31 -0
- package/dist/cjs/src/utils/routerBalanceSteps.js +293 -0
- package/dist/cjs/src/utils/routerBalanceSteps.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/routerCommands.d.ts +77 -64
- package/dist/cjs/src/utils/routerCommands.js +331 -0
- package/dist/cjs/src/utils/routerCommands.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/routerTradeAdapter.d.ts +73 -73
- package/dist/cjs/src/utils/routerTradeAdapter.js +139 -0
- package/dist/cjs/src/utils/routerTradeAdapter.js.map +1 -0
- package/dist/cjs/src/utils/toV4URVersion.d.ts +3 -0
- package/dist/cjs/src/utils/toV4URVersion.js +17 -0
- package/dist/cjs/src/utils/toV4URVersion.js.map +1 -0
- package/dist/cjs/src/utils/validateEncodeSwaps.d.ts +2 -0
- package/dist/cjs/src/utils/validateEncodeSwaps.js +344 -0
- package/dist/cjs/src/utils/validateEncodeSwaps.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/src/abis/UniversalRouter.json +740 -0
- package/dist/esm/src/entities/Command.d.ts +12 -0
- package/dist/esm/src/entities/Command.js +6 -0
- package/dist/esm/src/entities/Command.js.map +1 -0
- package/dist/esm/src/entities/actions/across.d.ts +21 -0
- package/dist/esm/src/entities/actions/across.js +3 -0
- package/dist/esm/src/entities/actions/across.js.map +1 -0
- package/dist/esm/src/entities/actions/index.d.ts +3 -0
- package/dist/esm/src/entities/actions/index.js +4 -0
- package/dist/esm/src/entities/actions/index.js.map +1 -0
- package/dist/esm/src/entities/actions/uniswap.d.ts +94 -0
- package/dist/esm/src/entities/actions/uniswap.js +735 -0
- package/dist/esm/src/entities/actions/uniswap.js.map +1 -0
- package/dist/esm/src/entities/actions/unwrapWETH.d.ts +12 -0
- package/dist/esm/src/entities/actions/unwrapWETH.js +28 -0
- package/dist/esm/src/entities/actions/unwrapWETH.js.map +1 -0
- package/dist/esm/src/entities/index.d.ts +2 -0
- package/dist/esm/src/entities/index.js +3 -0
- package/dist/esm/src/entities/index.js.map +1 -0
- package/dist/esm/src/index.d.ts +19 -0
- package/dist/esm/src/index.js +15 -0
- package/dist/esm/src/index.js.map +1 -0
- package/dist/esm/src/kittycorn.d.ts +9 -0
- package/dist/esm/src/kittycorn.js +22 -0
- package/dist/esm/src/kittycorn.js.map +1 -0
- package/dist/esm/src/swapRouter.d.ts +123 -0
- package/dist/esm/src/swapRouter.js +440 -0
- package/dist/esm/src/swapRouter.js.map +1 -0
- package/dist/esm/src/types/encodeSwaps.d.ts +197 -0
- package/dist/esm/src/types/encodeSwaps.js +2 -0
- package/dist/esm/src/types/encodeSwaps.js.map +1 -0
- package/dist/esm/src/utils/commandParser.d.ts +36 -0
- package/dist/esm/src/utils/commandParser.js +146 -0
- package/dist/esm/src/utils/commandParser.js.map +1 -0
- package/dist/esm/src/utils/computeEncodeSwapsAmounts.d.ts +8 -0
- package/dist/esm/src/utils/computeEncodeSwapsAmounts.js +41 -0
- package/dist/esm/src/utils/computeEncodeSwapsAmounts.js.map +1 -0
- package/dist/esm/src/utils/constants.d.ts +46 -0
- package/dist/esm/src/utils/constants.js +768 -0
- package/dist/esm/src/utils/constants.js.map +1 -0
- package/dist/esm/src/utils/directTransfers.d.ts +15 -0
- package/dist/esm/src/utils/directTransfers.js +369 -0
- package/dist/esm/src/utils/directTransfers.js.map +1 -0
- package/dist/esm/src/utils/eip712.d.ts +17 -0
- package/dist/esm/src/utils/eip712.js +39 -0
- package/dist/esm/src/utils/eip712.js.map +1 -0
- package/dist/esm/src/utils/encodeSwapStep.d.ts +4 -0
- package/dist/esm/src/utils/encodeSwapStep.js +58 -0
- package/dist/esm/src/utils/encodeSwapStep.js.map +1 -0
- package/dist/esm/src/utils/encodeV4Action.d.ts +7 -0
- package/dist/esm/src/utils/encodeV4Action.js +80 -0
- package/dist/esm/src/utils/encodeV4Action.js.map +1 -0
- package/dist/esm/src/utils/getCurrencyAddress.d.ts +2 -0
- package/dist/esm/src/utils/getCurrencyAddress.js +5 -0
- package/dist/esm/src/utils/getCurrencyAddress.js.map +1 -0
- package/dist/esm/src/utils/inputTokens.d.ts +23 -0
- package/dist/esm/src/utils/inputTokens.js +51 -0
- package/dist/esm/src/utils/inputTokens.js.map +1 -0
- package/dist/esm/src/utils/normalizeEncodeSwapsSpec.d.ts +6 -0
- package/dist/esm/src/utils/normalizeEncodeSwapsSpec.js +27 -0
- package/dist/esm/src/utils/normalizeEncodeSwapsSpec.js.map +1 -0
- package/dist/esm/src/utils/numbers.d.ts +7 -0
- package/dist/esm/src/utils/numbers.js +19 -0
- package/dist/esm/src/utils/numbers.js.map +1 -0
- package/dist/esm/src/utils/pathCurrency.d.ts +3 -0
- package/dist/esm/src/utils/pathCurrency.js +23 -0
- package/dist/esm/src/utils/pathCurrency.js.map +1 -0
- package/dist/esm/src/utils/portionFees.d.ts +14 -0
- package/dist/esm/src/utils/portionFees.js +35 -0
- package/dist/esm/src/utils/portionFees.js.map +1 -0
- package/dist/esm/src/utils/routerBalanceSteps.d.ts +31 -0
- package/dist/esm/src/utils/routerBalanceSteps.js +282 -0
- package/dist/esm/src/utils/routerBalanceSteps.js.map +1 -0
- package/dist/esm/src/utils/routerCommands.d.ts +77 -0
- package/dist/esm/src/utils/routerCommands.js +326 -0
- package/dist/esm/src/utils/routerCommands.js.map +1 -0
- package/dist/esm/src/utils/routerTradeAdapter.d.ts +73 -0
- package/dist/esm/src/utils/routerTradeAdapter.js +134 -0
- package/dist/esm/src/utils/routerTradeAdapter.js.map +1 -0
- package/dist/esm/src/utils/toV4URVersion.d.ts +3 -0
- package/dist/esm/src/utils/toV4URVersion.js +13 -0
- package/dist/esm/src/utils/toV4URVersion.js.map +1 -0
- package/dist/esm/src/utils/validateEncodeSwaps.d.ts +2 -0
- package/dist/esm/src/utils/validateEncodeSwaps.js +339 -0
- package/dist/esm/src/utils/validateEncodeSwaps.js.map +1 -0
- package/dist/types/src/entities/Command.d.ts +13 -0
- package/dist/types/src/entities/Command.d.ts.map +1 -0
- package/dist/types/src/entities/actions/across.d.ts +22 -0
- package/dist/types/src/entities/actions/across.d.ts.map +1 -0
- package/dist/types/src/entities/actions/index.d.ts +4 -0
- package/dist/types/src/entities/actions/index.d.ts.map +1 -0
- package/dist/types/src/entities/actions/uniswap.d.ts +95 -0
- package/dist/types/src/entities/actions/uniswap.d.ts.map +1 -0
- package/dist/types/src/entities/actions/unwrapWETH.d.ts +13 -0
- package/dist/types/src/entities/actions/unwrapWETH.d.ts.map +1 -0
- package/dist/types/src/entities/index.d.ts +3 -0
- package/dist/types/src/entities/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +20 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/kittycorn.d.ts +10 -0
- package/dist/types/src/kittycorn.d.ts.map +1 -0
- package/dist/types/src/swapRouter.d.ts +124 -0
- package/dist/types/src/swapRouter.d.ts.map +1 -0
- package/dist/types/src/types/encodeSwaps.d.ts +198 -0
- package/dist/types/src/types/encodeSwaps.d.ts.map +1 -0
- package/dist/types/src/utils/commandParser.d.ts +37 -0
- package/dist/types/src/utils/commandParser.d.ts.map +1 -0
- package/dist/types/src/utils/computeEncodeSwapsAmounts.d.ts +9 -0
- package/dist/types/src/utils/computeEncodeSwapsAmounts.d.ts.map +1 -0
- package/dist/types/src/utils/constants.d.ts +47 -0
- package/dist/types/src/utils/constants.d.ts.map +1 -0
- package/dist/types/src/utils/directTransfers.d.ts +16 -0
- package/dist/types/src/utils/directTransfers.d.ts.map +1 -0
- package/dist/types/src/utils/eip712.d.ts +18 -0
- package/dist/types/src/utils/eip712.d.ts.map +1 -0
- package/dist/types/src/utils/encodeSwapStep.d.ts +5 -0
- package/dist/types/src/utils/encodeSwapStep.d.ts.map +1 -0
- package/dist/types/src/utils/encodeV4Action.d.ts +8 -0
- package/dist/types/src/utils/encodeV4Action.d.ts.map +1 -0
- package/dist/types/src/utils/getCurrencyAddress.d.ts +3 -0
- package/dist/types/src/utils/getCurrencyAddress.d.ts.map +1 -0
- package/dist/types/src/utils/inputTokens.d.ts +24 -0
- package/dist/types/src/utils/inputTokens.d.ts.map +1 -0
- package/dist/types/src/utils/normalizeEncodeSwapsSpec.d.ts +7 -0
- package/dist/types/src/utils/normalizeEncodeSwapsSpec.d.ts.map +1 -0
- package/dist/types/src/utils/numbers.d.ts +8 -0
- package/dist/types/src/utils/numbers.d.ts.map +1 -0
- package/dist/types/src/utils/pathCurrency.d.ts +4 -0
- package/dist/types/src/utils/pathCurrency.d.ts.map +1 -0
- package/dist/types/src/utils/portionFees.d.ts +15 -0
- package/dist/types/src/utils/portionFees.d.ts.map +1 -0
- package/dist/types/src/utils/routerBalanceSteps.d.ts +32 -0
- package/dist/types/src/utils/routerBalanceSteps.d.ts.map +1 -0
- package/dist/types/src/utils/routerCommands.d.ts +78 -0
- package/dist/types/src/utils/routerCommands.d.ts.map +1 -0
- package/dist/types/src/utils/routerTradeAdapter.d.ts +74 -0
- package/dist/types/src/utils/routerTradeAdapter.d.ts.map +1 -0
- package/dist/types/src/utils/toV4URVersion.d.ts +4 -0
- package/dist/types/src/utils/toV4URVersion.d.ts.map +1 -0
- package/dist/types/src/utils/validateEncodeSwaps.d.ts +3 -0
- package/dist/types/src/utils/validateEncodeSwaps.d.ts.map +1 -0
- package/package.json +33 -52
- package/dist/entities/actions/uniswap.d.ts +0 -30
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -8
- package/dist/swapRouter.d.ts +0 -37
- package/dist/test/forge/writeInterop.d.ts +0 -2
- package/dist/test/utils/addresses.d.ts +0 -5
- package/dist/test/utils/hexToDecimalString.d.ts +0 -2
- package/dist/test/utils/permit2.d.ts +0 -7
- package/dist/test/utils/uniswapData.d.ts +0 -25
- package/dist/universal-router-sdk.cjs.development.js +0 -1500
- package/dist/universal-router-sdk.cjs.development.js.map +0 -1
- package/dist/universal-router-sdk.cjs.production.min.js +0 -2
- package/dist/universal-router-sdk.cjs.production.min.js.map +0 -1
- package/dist/universal-router-sdk.esm.js +0 -1493
- package/dist/universal-router-sdk.esm.js.map +0 -1
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import { isTokenizeConversion } from '@kittycorn-labs/v4-sdk';
|
|
2
|
+
import invariant from 'tiny-invariant';
|
|
3
|
+
import { CommandType } from '../../utils/routerCommands.js';
|
|
4
|
+
import { Trade as V2Trade, Pair } from '@kittycorn-labs/v2-sdk';
|
|
5
|
+
import { Trade as V3Trade, Pool as V3Pool, encodeRouteToPath } from '@kittycorn-labs/v3-sdk';
|
|
6
|
+
import { Route as V4Route, Trade as V4Trade, Pool as V4Pool, V4Planner, encodeRouteToPath as encodeV4RouteToPath, Actions, } from '@kittycorn-labs/v4-sdk';
|
|
7
|
+
import { MixedRouteTrade, Protocol, MixedRouteSDK, MixedRoute, getOutputOfPools, encodeMixedRouteToPath, partitionMixedRouteByProtocol, } from '@kittycorn-labs/router-sdk';
|
|
8
|
+
import { getPathCurrency } from '../../utils/pathCurrency.js';
|
|
9
|
+
import { toV4URVersion } from '../../utils/toV4URVersion.js';
|
|
10
|
+
import { TradeType, Percent } from '@kittycorn-labs/sdk-core';
|
|
11
|
+
import { RouterActionType } from '../Command.js';
|
|
12
|
+
import { SENDER_AS_RECIPIENT, ROUTER_AS_RECIPIENT, CONTRACT_BALANCE, ETH_ADDRESS, UniversalRouterVersion, UNIVERSAL_ROUTER_ADDRESS, isAtLeastV2_1_1, } from '../../utils/constants.js';
|
|
13
|
+
import { getCurrencyAddress } from '../../utils/getCurrencyAddress.js';
|
|
14
|
+
import { encodeFeeBips, encodeFee1e18 } from '../../utils/numbers.js';
|
|
15
|
+
import { scalePortionFees, simulatePortionFeeDeduction } from '../../utils/portionFees.js';
|
|
16
|
+
import { BigNumber } from '@ethersproject/bignumber';
|
|
17
|
+
// A product decision (interface + partner + integrator + referrer), not a protocol limit; each recipient costs one command.
|
|
18
|
+
export const MAX_FEE_RECIPIENTS = 4;
|
|
19
|
+
// the existing router permit object doesn't include enough data for permit2
|
|
20
|
+
// so we extend swap options with the permit2 permit
|
|
21
|
+
// when safe mode is enabled, the SDK will add an extra ETH sweep for security
|
|
22
|
+
// when useRouterBalance is enabled the SDK will use the balance in the router for the swap
|
|
23
|
+
export var TokenTransferMode;
|
|
24
|
+
(function (TokenTransferMode) {
|
|
25
|
+
TokenTransferMode["Permit2"] = "Permit2";
|
|
26
|
+
TokenTransferMode["ApproveProxy"] = "ApproveProxy";
|
|
27
|
+
})(TokenTransferMode || (TokenTransferMode = {}));
|
|
28
|
+
const REFUND_ETH_PRICE_IMPACT_THRESHOLD = new Percent(50, 100);
|
|
29
|
+
// The amount encoded for a route's FIRST hop. With routerBalanceInput the router spends
|
|
30
|
+
// whatever it holds at execution time, so the quoted amount is replaced by the
|
|
31
|
+
// CONTRACT_BALANCE sentinel; later hops already use it to chain the intermediate token.
|
|
32
|
+
function firstHopInputAmount(options, quotedAmountIn) {
|
|
33
|
+
return options.routerBalanceInput ? CONTRACT_BALANCE : quotedAmountIn;
|
|
34
|
+
}
|
|
35
|
+
// Wrapper for uniswap router-sdk trade entity to encode swaps for Universal Router
|
|
36
|
+
// also translates trade objects from previous (v2, v3) SDKs
|
|
37
|
+
export class UniswapTrade {
|
|
38
|
+
constructor(trade, options) {
|
|
39
|
+
this.trade = trade;
|
|
40
|
+
this.options = options;
|
|
41
|
+
this.tradeType = RouterActionType.UniswapTrade;
|
|
42
|
+
if (trade.swaps.some(({ route }) => route.pools.some(pool => pool instanceof V4Pool && isTokenizeConversion(pool)))) {
|
|
43
|
+
throw new Error('Tokenize conversions require buildKittycornUniversalMethodParameters');
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(options.fee)) {
|
|
46
|
+
// An empty array would still make the router custody the output while paying nobody.
|
|
47
|
+
if (options.fee.length === 0)
|
|
48
|
+
throw new Error('At least one fee recipient required');
|
|
49
|
+
if (options.fee.length > MAX_FEE_RECIPIENTS) {
|
|
50
|
+
throw new Error(`At most ${MAX_FEE_RECIPIENTS} fee recipients permitted`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!!options.fee && !!options.flatFee)
|
|
54
|
+
throw new Error('Only one fee option permitted');
|
|
55
|
+
if (options.nativeErc20Input) {
|
|
56
|
+
// input token is the chain's native gas token exposed via an ERC20 predeploy (e.g. Arc USDC);
|
|
57
|
+
// the router is funded by msg.value instead of Permit2, so it pays pools from its own balance
|
|
58
|
+
if (this.trade.inputAmount.currency.isNative)
|
|
59
|
+
throw new Error('nativeErc20Input requires an ERC20 input token');
|
|
60
|
+
if (options.tokenTransferMode === TokenTransferMode.ApproveProxy) {
|
|
61
|
+
throw new Error('nativeErc20Input is not supported with ApproveProxy');
|
|
62
|
+
}
|
|
63
|
+
if (options.inputTokenPermit)
|
|
64
|
+
throw new Error('nativeErc20Input does not use Permit2; remove inputTokenPermit');
|
|
65
|
+
if (this.inputRequiresUnwrap) {
|
|
66
|
+
throw new Error('nativeErc20Input requires routes quoted against the ERC20 input (native pathInput unsupported)');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (options.routerBalanceInput) {
|
|
70
|
+
// The router spends a balance a third party funded in the same transaction, so the
|
|
71
|
+
// amount is unknown at encode time and msg.sender is the funder, not the beneficiary.
|
|
72
|
+
if (!options.recipient || options.recipient === SENDER_AS_RECIPIENT) {
|
|
73
|
+
throw new Error('Explicit recipient address required with routerBalanceInput (SENDER_AS_RECIPIENT resolves to the caller, who is not the swapper)');
|
|
74
|
+
}
|
|
75
|
+
// The Dispatcher maps recipient sentinels: address(2) resolves to the router itself —
|
|
76
|
+
// unwrapWETH9 then skips the transfer and a native-out execute() SUCCEEDS with the
|
|
77
|
+
// ETH stranded in the permissionless router — and address(0) burns. Both are
|
|
78
|
+
// format-valid addresses, so upstream shape checks pass them straight through.
|
|
79
|
+
if (options.recipient === ROUTER_AS_RECIPIENT || BigNumber.from(options.recipient).isZero()) {
|
|
80
|
+
throw new Error('routerBalanceInput recipient cannot be a UR sentinel or the zero address');
|
|
81
|
+
}
|
|
82
|
+
// Native input is funded as msg.value on execute() — raw transfers to the router
|
|
83
|
+
// revert (its receive() only accepts ETH from WETH) — and wrapped in full by the
|
|
84
|
+
// route's WRAP_ETH. Routes that consume native directly (pure-native v4) have no
|
|
85
|
+
// wrap step to size with CONTRACT_BALANCE, so they stay unsupported.
|
|
86
|
+
if (this.trade.inputAmount.currency.isNative && !this.inputRequiresWrap) {
|
|
87
|
+
throw new Error('routerBalanceInput with a native input requires a route that wraps to WETH');
|
|
88
|
+
}
|
|
89
|
+
if (this.trade.tradeType !== TradeType.EXACT_INPUT) {
|
|
90
|
+
throw new Error('routerBalanceInput requires TradeType.EXACT_INPUT');
|
|
91
|
+
}
|
|
92
|
+
// Split routes: CONTRACT_BALANCE resolves to the router's whole balance,
|
|
93
|
+
// so exactly ONE leg (the largest, encoded last) spends it; the other
|
|
94
|
+
// legs keep their quoted amounts from router custody. With a native
|
|
95
|
+
// input every leg must consume the wrapped token, since the single
|
|
96
|
+
// WRAP_ETH funds them all.
|
|
97
|
+
if (this.trade.swaps.length > 1 &&
|
|
98
|
+
this.trade.inputAmount.currency.isNative &&
|
|
99
|
+
this.trade.swaps.some((swap) => { var _a; return (_a = swap.route.pathInput) === null || _a === void 0 ? void 0 : _a.isNative; })) {
|
|
100
|
+
throw new Error('routerBalanceInput split routes with a native input require every leg to wrap to WETH');
|
|
101
|
+
}
|
|
102
|
+
// A WETH input whose route unwraps: UNWRAP_WETH takes the router's whole WETH
|
|
103
|
+
// balance, so it is the greedy claim and every leg must consume the resulting
|
|
104
|
+
// native currency. Checked across all legs rather than via inputRequiresUnwrap,
|
|
105
|
+
// which only inspects leg 0 and so depends on leg order.
|
|
106
|
+
if (!this.trade.inputAmount.currency.isNative && this.trade.swaps.length > 1) {
|
|
107
|
+
const nativeLegs = this.trade.swaps.filter((swap) => { var _a; return (_a = swap.route.pathInput) === null || _a === void 0 ? void 0 : _a.isNative; }).length;
|
|
108
|
+
if (nativeLegs > 0 && nativeLegs < this.trade.swaps.length) {
|
|
109
|
+
throw new Error('routerBalanceInput with an unwrapped input requires every leg to consume native');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (options.inputTokenPermit) {
|
|
113
|
+
throw new Error('routerBalanceInput does not use Permit2; remove inputTokenPermit');
|
|
114
|
+
}
|
|
115
|
+
if (options.nativeErc20Input) {
|
|
116
|
+
throw new Error('routerBalanceInput is not supported with nativeErc20Input');
|
|
117
|
+
}
|
|
118
|
+
if (options.tokenTransferMode === TokenTransferMode.ApproveProxy) {
|
|
119
|
+
throw new Error('routerBalanceInput is not supported with ApproveProxy');
|
|
120
|
+
}
|
|
121
|
+
if (options.routerBalanceInput.minimumAmount !== undefined && !options.chainId) {
|
|
122
|
+
throw new Error('routerBalanceInput.minimumAmount requires chainId to resolve the router address');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (options.tokenTransferMode === TokenTransferMode.ApproveProxy) {
|
|
126
|
+
if (!options.recipient || options.recipient === SENDER_AS_RECIPIENT) {
|
|
127
|
+
throw new Error('Explicit recipient address required when using SwapProxy (SENDER_AS_RECIPIENT resolves to proxy)');
|
|
128
|
+
}
|
|
129
|
+
this.payerIsUser = false;
|
|
130
|
+
}
|
|
131
|
+
else if (this.inputRequiresWrap ||
|
|
132
|
+
this.inputRequiresUnwrap ||
|
|
133
|
+
this.options.useRouterBalance ||
|
|
134
|
+
this.options.nativeErc20Input ||
|
|
135
|
+
this.options.routerBalanceInput) {
|
|
136
|
+
this.payerIsUser = false;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
this.payerIsUser = true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
get isAllV4() {
|
|
143
|
+
let result = true;
|
|
144
|
+
for (const swap of this.trade.swaps) {
|
|
145
|
+
result = result && swap.route.protocol == Protocol.V4;
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
// this.trade.swaps is an array of swaps / trades.
|
|
150
|
+
// we are iterating over one swap (trade) at a time so length is 1
|
|
151
|
+
// route is either v2, v3, v4, or mixed
|
|
152
|
+
// pathInput and pathOutput are the currencies of the input and output of the route
|
|
153
|
+
// this.trade.inputAmount is the input currency of the trade (could be different from pathInput)
|
|
154
|
+
// this.trade.outputAmount is the output currency of the trade (could be different from pathOutput)
|
|
155
|
+
// each route can have multiple pools
|
|
156
|
+
get inputRequiresWrap() {
|
|
157
|
+
const swap = this.trade.swaps[0];
|
|
158
|
+
const route = swap.route;
|
|
159
|
+
const firstPool = route.pools[0];
|
|
160
|
+
if (firstPool instanceof V4Pool) {
|
|
161
|
+
// If first pool is a v4 pool and input currency is native and the path input currency in the route is not native, we need to wrap.
|
|
162
|
+
return (this.trade.inputAmount.currency.isNative &&
|
|
163
|
+
!this.trade.swaps[0].route.pathInput.isNative);
|
|
164
|
+
}
|
|
165
|
+
// If first pool is not a v4 pool and input currency is native, we need to wrap
|
|
166
|
+
return this.trade.inputAmount.currency.isNative;
|
|
167
|
+
}
|
|
168
|
+
get inputRequiresUnwrap() {
|
|
169
|
+
const swap = this.trade.swaps[0];
|
|
170
|
+
const route = swap.route;
|
|
171
|
+
const firstPool = route.pools[0];
|
|
172
|
+
if (firstPool instanceof V4Pool) {
|
|
173
|
+
// If the first pool is a v4 pool and input currency is not native and the path input currency is native, we need to unwrap
|
|
174
|
+
return (!this.trade.inputAmount.currency.isNative &&
|
|
175
|
+
this.trade.swaps[0].route.pathInput.isNative);
|
|
176
|
+
}
|
|
177
|
+
// If the first pool is not a v4 pool, we don't need to unwrap.
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
get outputRequiresWrap() {
|
|
181
|
+
const swap = this.trade.swaps[0];
|
|
182
|
+
const lastRoute = swap.route;
|
|
183
|
+
const lastPool = lastRoute.pools[lastRoute.pools.length - 1];
|
|
184
|
+
// if last pool is v4:
|
|
185
|
+
if (lastPool instanceof V4Pool) {
|
|
186
|
+
// If output currency is not native but path currency output is native, we need to wrap
|
|
187
|
+
if (!this.trade.outputAmount.currency.isNative) {
|
|
188
|
+
if (lastRoute.pathOutput.isNative) {
|
|
189
|
+
// this means path output is native and we need to wrap
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
else if (lastPool.currency1.equals(lastPool.currency0.wrapped) && lastRoute.pools.length > 1) {
|
|
193
|
+
let poolBefore = lastRoute.pools[lastRoute.pools.length - 2];
|
|
194
|
+
// this means last pool is eth-weth and pool before contains weth
|
|
195
|
+
if (poolBefore instanceof V4Pool &&
|
|
196
|
+
(poolBefore.currency0.equals(lastPool.currency1) || poolBefore.currency1.equals(lastPool.currency1))) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
else if (poolBefore.token0.equals(lastPool.currency1) || poolBefore.token1.equals(lastPool.currency1)) {
|
|
200
|
+
// same for v2 and v3 pools
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// if last pool is not v4:
|
|
207
|
+
// we do not need to wrap because v2 and v3 pools already require wrapped tokens
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
get outputRequiresUnwrap() {
|
|
211
|
+
const swap = this.trade.swaps[0];
|
|
212
|
+
const lastRoute = swap.route;
|
|
213
|
+
const lastPool = lastRoute.pools[lastRoute.pools.length - 1];
|
|
214
|
+
// if last pool is v4:
|
|
215
|
+
if (lastPool instanceof V4Pool) {
|
|
216
|
+
// If output currency is native and path currency output is not native, we need to unwrap
|
|
217
|
+
if (this.trade.outputAmount.currency.isNative) {
|
|
218
|
+
if (!this.trade.swaps[0].route.pathOutput.isNative) {
|
|
219
|
+
// this means path output is weth and we need to unwrap
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
else if (lastRoute.pools.length > 1 &&
|
|
223
|
+
lastRoute.pools[lastRoute.pools.length - 2] instanceof V4Pool &&
|
|
224
|
+
lastRoute.pools[lastRoute.pools.length - 2].currency0.isNative &&
|
|
225
|
+
lastPool.currency1.equals(lastPool.currency0.wrapped)) {
|
|
226
|
+
// this means last pool is eth-weth and we need to unwrap
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// else: if path output currency is native, we need to unwrap because v2 and v3 pools already require wrapped tokens
|
|
235
|
+
return this.trade.outputAmount.currency.isNative;
|
|
236
|
+
}
|
|
237
|
+
get outputRequiresTransition() {
|
|
238
|
+
return this.outputRequiresWrap || this.outputRequiresUnwrap;
|
|
239
|
+
}
|
|
240
|
+
encode(planner, _config) {
|
|
241
|
+
var _a, _b, _c, _d;
|
|
242
|
+
// Input floor first, so an under-funded router reverts before any swap runs. A native
|
|
243
|
+
// balance input has no balance-check command, so its floor is asserted post-wrap as WETH.
|
|
244
|
+
const balanceInputIsNative = !!this.options.routerBalanceInput && this.trade.inputAmount.currency.isNative;
|
|
245
|
+
const minimumRouterBalance = (_a = this.options.routerBalanceInput) === null || _a === void 0 ? void 0 : _a.minimumAmount;
|
|
246
|
+
if (minimumRouterBalance !== undefined && !balanceInputIsNative) {
|
|
247
|
+
// BALANCE_CHECK_ERC20 reads `owner` verbatim, without the sentinel resolution the
|
|
248
|
+
// recipient params get, so this must be the router's real address.
|
|
249
|
+
planner.addCommand(CommandType.BALANCE_CHECK_ERC20, [
|
|
250
|
+
UNIVERSAL_ROUTER_ADDRESS((_b = this.options.urVersion) !== null && _b !== void 0 ? _b : UniversalRouterVersion.V2_0, this.options.chainId),
|
|
251
|
+
this.trade.inputAmount.currency.address,
|
|
252
|
+
minimumRouterBalance,
|
|
253
|
+
]);
|
|
254
|
+
}
|
|
255
|
+
// If the input currency is the native currency, we need to wrap it with the router as the recipient
|
|
256
|
+
if (this.inputRequiresWrap) {
|
|
257
|
+
// TODO: optimize if only one v2 pool we can directly send this to the pool
|
|
258
|
+
planner.addCommand(CommandType.WRAP_ETH, [
|
|
259
|
+
ROUTER_AS_RECIPIENT,
|
|
260
|
+
// Balance input: wrap everything the router holds (attached msg.value plus any
|
|
261
|
+
// stray native), so no value is left behind — UR never refunds msg.value.
|
|
262
|
+
this.options.routerBalanceInput
|
|
263
|
+
? CONTRACT_BALANCE
|
|
264
|
+
: this.trade.maximumAmountIn(this.options.slippageTolerance).quotient.toString(),
|
|
265
|
+
]);
|
|
266
|
+
if (minimumRouterBalance !== undefined && balanceInputIsNative) {
|
|
267
|
+
planner.addCommand(CommandType.BALANCE_CHECK_ERC20, [
|
|
268
|
+
UNIVERSAL_ROUTER_ADDRESS((_c = this.options.urVersion) !== null && _c !== void 0 ? _c : UniversalRouterVersion.V2_0, this.options.chainId),
|
|
269
|
+
this.trade.inputAmount.currency.wrapped.address,
|
|
270
|
+
minimumRouterBalance,
|
|
271
|
+
]);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
else if (this.inputRequiresUnwrap) {
|
|
275
|
+
// Balance input: the WETH is already in the router and msg.sender is the funder,
|
|
276
|
+
// not the swapper — pulling from them would spend the filler's own tokens.
|
|
277
|
+
if (this.options.tokenTransferMode !== TokenTransferMode.ApproveProxy && !this.options.routerBalanceInput) {
|
|
278
|
+
// send wrapped token to router to unwrap via Permit2
|
|
279
|
+
planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [
|
|
280
|
+
this.trade.inputAmount.currency.address,
|
|
281
|
+
ROUTER_AS_RECIPIENT,
|
|
282
|
+
this.trade.maximumAmountIn(this.options.slippageTolerance).quotient.toString(),
|
|
283
|
+
]);
|
|
284
|
+
}
|
|
285
|
+
// In proxy mode, the proxy already transferred tokens to the UR; just unwrap
|
|
286
|
+
planner.addCommand(CommandType.UNWRAP_WETH, [ROUTER_AS_RECIPIENT, 0]);
|
|
287
|
+
}
|
|
288
|
+
// The overall recipient at the end of the trade, SENDER_AS_RECIPIENT uses the msg.sender
|
|
289
|
+
this.options.recipient = (_d = this.options.recipient) !== null && _d !== void 0 ? _d : SENDER_AS_RECIPIENT;
|
|
290
|
+
// flag for whether we want to perform slippage check on aggregate output of multiple routes
|
|
291
|
+
// 1. when there are >2 exact input trades. this is only a heuristic,
|
|
292
|
+
// as it's still more gas-expensive even in this case, but has benefits
|
|
293
|
+
// in that the reversion probability is lower
|
|
294
|
+
const performAggregatedSlippageCheck = this.trade.tradeType === TradeType.EXACT_INPUT && this.trade.routes.length > 2;
|
|
295
|
+
// v2SwapExactOutput only bounds amountIn and forwards whatever the pair actually produced, so a
|
|
296
|
+
// V2 exact-out leg paying the recipient directly has no output check. V3 (V3InvalidAmountOut)
|
|
297
|
+
// and V4 (exact TAKE) legs assert their own output; V2 legs need custody + a floored SWEEP.
|
|
298
|
+
const v2ExactOutputNeedsFloor = this.trade.tradeType === TradeType.EXACT_OUTPUT &&
|
|
299
|
+
this.trade.routes.some((route) => route.pools.some((pool) => pool instanceof Pair));
|
|
300
|
+
const routerMustCustody = performAggregatedSlippageCheck ||
|
|
301
|
+
this.outputRequiresTransition ||
|
|
302
|
+
hasFeeOption(this.options) ||
|
|
303
|
+
// Balance-swap splits: the remainder leg's output varies with delivery,
|
|
304
|
+
// so the minimum is enforced on the aggregate sweep, never per leg.
|
|
305
|
+
(!!this.options.routerBalanceInput && this.trade.swaps.length > 1) ||
|
|
306
|
+
v2ExactOutputNeedsFloor;
|
|
307
|
+
// Balance-swap splits: the fixed legs run first with their quoted amounts
|
|
308
|
+
// (per-leg options drop routerBalanceInput, so they encode normally from
|
|
309
|
+
// router custody); the largest leg runs LAST and spends CONTRACT_BALANCE,
|
|
310
|
+
// absorbing all delivery variance. The fill only reverts when delivery
|
|
311
|
+
// cannot cover the fixed legs, so the tolerance is the largest leg's share.
|
|
312
|
+
let swapsInOrder = this.trade.swaps;
|
|
313
|
+
let remainderSwap;
|
|
314
|
+
if (this.options.routerBalanceInput && swapsInOrder.length > 1) {
|
|
315
|
+
remainderSwap = swapsInOrder.reduce((largest, swap) => swap.inputAmount.greaterThan(largest.inputAmount) ? swap : largest);
|
|
316
|
+
swapsInOrder = [...swapsInOrder.filter((swap) => swap !== remainderSwap), remainderSwap];
|
|
317
|
+
}
|
|
318
|
+
for (const swap of swapsInOrder) {
|
|
319
|
+
const legOptions = remainderSwap !== undefined && swap !== remainderSwap
|
|
320
|
+
? { ...this.options, routerBalanceInput: undefined }
|
|
321
|
+
: this.options;
|
|
322
|
+
switch (swap.route.protocol) {
|
|
323
|
+
case Protocol.V2:
|
|
324
|
+
addV2Swap(planner, swap, this.trade.tradeType, legOptions, this.payerIsUser, routerMustCustody);
|
|
325
|
+
break;
|
|
326
|
+
case Protocol.V3:
|
|
327
|
+
addV3Swap(planner, swap, this.trade.tradeType, legOptions, this.payerIsUser, routerMustCustody);
|
|
328
|
+
break;
|
|
329
|
+
case Protocol.V4:
|
|
330
|
+
addV4Swap(planner, swap, this.trade.tradeType, legOptions, this.payerIsUser, routerMustCustody);
|
|
331
|
+
break;
|
|
332
|
+
case Protocol.MIXED:
|
|
333
|
+
addMixedSwap(planner, swap, this.trade.tradeType, legOptions, this.payerIsUser, routerMustCustody);
|
|
334
|
+
break;
|
|
335
|
+
default:
|
|
336
|
+
throw new Error('UNSUPPORTED_TRADE_PROTOCOL');
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
let minimumAmountOut = BigNumber.from(this.trade.minimumAmountOut(this.options.slippageTolerance).quotient.toString());
|
|
340
|
+
// The router custodies for 3 reasons: to unwrap, to take a fee, and/or to do a slippage check
|
|
341
|
+
if (routerMustCustody) {
|
|
342
|
+
const pools = this.trade.swaps[0].route.pools;
|
|
343
|
+
const pathOutputCurrencyAddress = getCurrencyAddress(getPathCurrency(this.trade.outputAmount.currency, pools[pools.length - 1]));
|
|
344
|
+
// UR >= V2_1_1 has PAY_PORTION_FULL_PRECISION (1e18); older versions only bips.
|
|
345
|
+
const useFullPrecision = isAtLeastV2_1_1(this.options.urVersion);
|
|
346
|
+
// If there is a fee, that percentage is sent to the fee recipient. One PAY_PORTION per
|
|
347
|
+
// recipient, emitted in the caller's order, ahead of the settlement command below.
|
|
348
|
+
// In the case where ETH is the output currency, the fee is taken in WETH (for gas reasons)
|
|
349
|
+
const feeList = toFeeOptionsList(this.options.fee);
|
|
350
|
+
// Rescaling gross fractions against a shrinking balance yields fractional bips, which PAY_PORTION cannot represent.
|
|
351
|
+
invariant(feeList.length <= 1 || useFullPrecision, 'Multiple fee recipients require Universal Router version V2_1_1 or higher');
|
|
352
|
+
// Rescales fee i to f_i / (1 - sum of earlier fees), so each recipient gets its stated fraction of gross.
|
|
353
|
+
const scaledFees = scalePortionFees(feeList);
|
|
354
|
+
for (const { recipient, grossFee, scaledFee } of scaledFees) {
|
|
355
|
+
if (useFullPrecision) {
|
|
356
|
+
planner.addCommand(CommandType.PAY_PORTION_FULL_PRECISION, [pathOutputCurrencyAddress, recipient, encodeFee1e18(scaledFee)], false, this.options.urVersion);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
// Reject fractional bips fees on older UR versions to prevent silent precision loss
|
|
360
|
+
if (!grossFee.multiply(10000).remainder.equalTo(0)) {
|
|
361
|
+
throw new Error('Fractional fee bips require Universal Router version V2_1_1 or higher');
|
|
362
|
+
}
|
|
363
|
+
// single fee: scaled == gross, so the legacy bips encoding is unchanged
|
|
364
|
+
planner.addCommand(CommandType.PAY_PORTION, [pathOutputCurrencyAddress, recipient, encodeFeeBips(grossFee)]);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// The sweep floor must expect what the commands leave behind, so the deduction replays their cascade.
|
|
368
|
+
let feeDeduction = simulatePortionFeeDeduction(minimumAmountOut, scaledFees, useFullPrecision);
|
|
369
|
+
// If there is a flat fee, that absolute amount is sent to the fee recipient
|
|
370
|
+
// In the case where ETH is the output currency, the fee is taken in WETH (for gas reasons)
|
|
371
|
+
if (!!this.options.flatFee) {
|
|
372
|
+
const feeAmount = this.options.flatFee.amount;
|
|
373
|
+
if (minimumAmountOut.lt(feeAmount))
|
|
374
|
+
throw new Error('Flat fee amount greater than minimumAmountOut');
|
|
375
|
+
planner.addCommand(CommandType.TRANSFER, [pathOutputCurrencyAddress, this.options.flatFee.recipient, feeAmount]);
|
|
376
|
+
feeDeduction = BigNumber.from(feeAmount);
|
|
377
|
+
}
|
|
378
|
+
// If the trade is exact output, and a fee was taken, we must adjust the amount out to be the amount after the fee
|
|
379
|
+
// Otherwise we continue as expected with the trade's normal expected output
|
|
380
|
+
if (this.trade.tradeType === TradeType.EXACT_OUTPUT) {
|
|
381
|
+
minimumAmountOut = minimumAmountOut.sub(feeDeduction);
|
|
382
|
+
}
|
|
383
|
+
// The remaining tokens that need to be sent to the user after the fee is taken will be caught
|
|
384
|
+
// by this if-else clause.
|
|
385
|
+
if (this.outputRequiresUnwrap) {
|
|
386
|
+
planner.addCommand(CommandType.UNWRAP_WETH, [this.options.recipient, minimumAmountOut]);
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
// Custodied legs are encoded with amountOutMinimum 0, so this SWEEP is the trade's only
|
|
390
|
+
// output check. WRAP_ETH has no minimum, so wrap into the router and let the SWEEP floor it.
|
|
391
|
+
if (this.outputRequiresWrap) {
|
|
392
|
+
planner.addCommand(CommandType.WRAP_ETH, [ROUTER_AS_RECIPIENT, CONTRACT_BALANCE]);
|
|
393
|
+
}
|
|
394
|
+
planner.addCommand(CommandType.SWEEP, [
|
|
395
|
+
getCurrencyAddress(this.trade.outputAmount.currency),
|
|
396
|
+
this.options.recipient,
|
|
397
|
+
minimumAmountOut,
|
|
398
|
+
]);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// for exactOutput swaps with native input or that perform an inputToken transition (wrap or unwrap)
|
|
402
|
+
// we need to send back the change to the user
|
|
403
|
+
if (this.trade.tradeType === TradeType.EXACT_OUTPUT || riskOfPartialFill(this.trade)) {
|
|
404
|
+
if (this.inputRequiresWrap) {
|
|
405
|
+
planner.addCommand(CommandType.UNWRAP_WETH, [this.options.recipient, 0]);
|
|
406
|
+
}
|
|
407
|
+
else if (this.inputRequiresUnwrap) {
|
|
408
|
+
planner.addCommand(CommandType.WRAP_ETH, [this.options.recipient, CONTRACT_BALANCE]);
|
|
409
|
+
}
|
|
410
|
+
else if (this.options.tokenTransferMode === TokenTransferMode.ApproveProxy) {
|
|
411
|
+
// Proxy pulled maximumAmountIn into the UR; sweep any unused input back to the user
|
|
412
|
+
planner.addCommand(CommandType.SWEEP, [
|
|
413
|
+
getCurrencyAddress(this.trade.inputAmount.currency),
|
|
414
|
+
this.options.recipient,
|
|
415
|
+
0,
|
|
416
|
+
]);
|
|
417
|
+
}
|
|
418
|
+
else if (this.options.nativeErc20Input || this.trade.inputAmount.currency.isNative) {
|
|
419
|
+
// must refund extra native currency sent along (nativeErc20Input or native v4 trades).
|
|
420
|
+
// For nativeErc20Input the leftover lives in the router's native balance (18 decimals), so
|
|
421
|
+
// sweep it as native: an ERC20 sweep would floor to the token's decimals and strand dust.
|
|
422
|
+
planner.addCommand(CommandType.SWEEP, [ETH_ADDRESS, this.options.recipient, 0]);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// Native balance input always sweeps trailing ETH dust to the recipient: the funder is
|
|
426
|
+
// a third party, so anything left on the router would otherwise be stranded or swept
|
|
427
|
+
// by a stranger.
|
|
428
|
+
if (this.options.safeMode || (this.options.routerBalanceInput && this.trade.inputAmount.currency.isNative)) {
|
|
429
|
+
planner.addCommand(CommandType.SWEEP, [ETH_ADDRESS, this.options.recipient, 0]);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// encode a uniswap v2 swap
|
|
434
|
+
function addV2Swap(planner, { route, inputAmount, outputAmount, minHopPriceX36 }, tradeType, options, payerIsUser, routerMustCustody) {
|
|
435
|
+
if ((minHopPriceX36 === null || minHopPriceX36 === void 0 ? void 0 : minHopPriceX36.length) && minHopPriceX36.length !== route.pools.length) {
|
|
436
|
+
throw new Error(`minHopPriceX36 length (${minHopPriceX36.length}) must equal route.pools.length (${route.pools.length})`);
|
|
437
|
+
}
|
|
438
|
+
const trade = new V2Trade(route, tradeType == TradeType.EXACT_INPUT ? inputAmount : outputAmount, tradeType);
|
|
439
|
+
const useV2_1_1 = isAtLeastV2_1_1(options.urVersion);
|
|
440
|
+
if (tradeType == TradeType.EXACT_INPUT) {
|
|
441
|
+
const params = [
|
|
442
|
+
// if native, we have to unwrap so keep in the router for now
|
|
443
|
+
routerMustCustody ? ROUTER_AS_RECIPIENT : options.recipient,
|
|
444
|
+
firstHopInputAmount(options, trade.maximumAmountIn(options.slippageTolerance).quotient.toString()),
|
|
445
|
+
// if router will custody funds, we do aggregated slippage check from router
|
|
446
|
+
routerMustCustody ? 0 : trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
|
|
447
|
+
route.path.map((token) => token.wrapped.address),
|
|
448
|
+
payerIsUser,
|
|
449
|
+
];
|
|
450
|
+
if (useV2_1_1)
|
|
451
|
+
params.push(minHopPriceX36 !== null && minHopPriceX36 !== void 0 ? minHopPriceX36 : []);
|
|
452
|
+
planner.addCommand(CommandType.V2_SWAP_EXACT_IN, params, false, options.urVersion);
|
|
453
|
+
}
|
|
454
|
+
else if (tradeType == TradeType.EXACT_OUTPUT) {
|
|
455
|
+
const params = [
|
|
456
|
+
routerMustCustody ? ROUTER_AS_RECIPIENT : options.recipient,
|
|
457
|
+
trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
|
|
458
|
+
trade.maximumAmountIn(options.slippageTolerance).quotient.toString(),
|
|
459
|
+
route.path.map((token) => token.wrapped.address),
|
|
460
|
+
payerIsUser,
|
|
461
|
+
];
|
|
462
|
+
if (useV2_1_1)
|
|
463
|
+
params.push(minHopPriceX36 !== null && minHopPriceX36 !== void 0 ? minHopPriceX36 : []);
|
|
464
|
+
planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, params, false, options.urVersion);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
// encode a uniswap v3 swap
|
|
468
|
+
function addV3Swap(planner, { route, inputAmount, outputAmount, minHopPriceX36 }, tradeType, options, payerIsUser, routerMustCustody) {
|
|
469
|
+
if ((minHopPriceX36 === null || minHopPriceX36 === void 0 ? void 0 : minHopPriceX36.length) && minHopPriceX36.length !== route.pools.length) {
|
|
470
|
+
throw new Error(`minHopPriceX36 length (${minHopPriceX36.length}) must equal route.pools.length (${route.pools.length})`);
|
|
471
|
+
}
|
|
472
|
+
const trade = V3Trade.createUncheckedTrade({
|
|
473
|
+
route: route,
|
|
474
|
+
inputAmount,
|
|
475
|
+
outputAmount,
|
|
476
|
+
tradeType,
|
|
477
|
+
});
|
|
478
|
+
const useV2_1_1 = isAtLeastV2_1_1(options.urVersion);
|
|
479
|
+
const path = encodeRouteToPath(route, trade.tradeType === TradeType.EXACT_OUTPUT);
|
|
480
|
+
if (tradeType == TradeType.EXACT_INPUT) {
|
|
481
|
+
const params = [
|
|
482
|
+
routerMustCustody ? ROUTER_AS_RECIPIENT : options.recipient,
|
|
483
|
+
firstHopInputAmount(options, trade.maximumAmountIn(options.slippageTolerance).quotient.toString()),
|
|
484
|
+
routerMustCustody ? 0 : trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
|
|
485
|
+
path,
|
|
486
|
+
payerIsUser,
|
|
487
|
+
];
|
|
488
|
+
if (useV2_1_1)
|
|
489
|
+
params.push(minHopPriceX36 !== null && minHopPriceX36 !== void 0 ? minHopPriceX36 : []);
|
|
490
|
+
planner.addCommand(CommandType.V3_SWAP_EXACT_IN, params, false, options.urVersion);
|
|
491
|
+
}
|
|
492
|
+
else if (tradeType == TradeType.EXACT_OUTPUT) {
|
|
493
|
+
const params = [
|
|
494
|
+
routerMustCustody ? ROUTER_AS_RECIPIENT : options.recipient,
|
|
495
|
+
trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
|
|
496
|
+
trade.maximumAmountIn(options.slippageTolerance).quotient.toString(),
|
|
497
|
+
path,
|
|
498
|
+
payerIsUser,
|
|
499
|
+
];
|
|
500
|
+
if (useV2_1_1)
|
|
501
|
+
params.push(minHopPriceX36 !== null && minHopPriceX36 !== void 0 ? minHopPriceX36 : []);
|
|
502
|
+
planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, params, false, options.urVersion);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function addV4Swap(planner, { inputAmount, outputAmount, route, minHopPriceX36 }, tradeType, options, payerIsUser, routerMustCustody) {
|
|
506
|
+
var _a, _b;
|
|
507
|
+
if ((minHopPriceX36 === null || minHopPriceX36 === void 0 ? void 0 : minHopPriceX36.length) && minHopPriceX36.length !== route.pools.length) {
|
|
508
|
+
throw new Error(`minHopPriceX36 length (${minHopPriceX36.length}) must equal route.pools.length (${route.pools.length})`);
|
|
509
|
+
}
|
|
510
|
+
// create a deep copy of pools since v4Planner encoding tampers with array
|
|
511
|
+
const pools = route.pools.map((p) => p);
|
|
512
|
+
const v4Route = new V4Route(pools, inputAmount.currency, outputAmount.currency);
|
|
513
|
+
const trade = V4Trade.createUncheckedTrade({
|
|
514
|
+
route: v4Route,
|
|
515
|
+
inputAmount,
|
|
516
|
+
outputAmount,
|
|
517
|
+
tradeType,
|
|
518
|
+
});
|
|
519
|
+
const slippageToleranceOnSwap = routerMustCustody && tradeType == TradeType.EXACT_INPUT ? undefined : options.slippageTolerance;
|
|
520
|
+
const perHopSlippage = (_a = minHopPriceX36 === null || minHopPriceX36 === void 0 ? void 0 : minHopPriceX36.map((s) => BigNumber.from(s))) !== null && _a !== void 0 ? _a : [];
|
|
521
|
+
const settleBeforeSwap = routeHasSettleBeforeSwapHook(pools, options.settleBeforeSwapHooks);
|
|
522
|
+
invariant(!settleBeforeSwap || tradeType === TradeType.EXACT_INPUT, 'SETTLE_BEFORE_SWAP_HOOK_EXACT_INPUT_ONLY');
|
|
523
|
+
const v4Planner = new V4Planner();
|
|
524
|
+
if (options.routerBalanceInput || settleBeforeSwap) {
|
|
525
|
+
// V4Planner.addTrade would bake the quoted amountIn into the swap action, so build the
|
|
526
|
+
// pair explicitly instead: SETTLE the input first, then swap the resulting open delta.
|
|
527
|
+
// Same shape the mixed-route encoder uses for its v4 sections.
|
|
528
|
+
const pathInput = trade.route.pathInput;
|
|
529
|
+
if (options.routerBalanceInput) {
|
|
530
|
+
v4Planner.addSettle(pathInput, false, CONTRACT_BALANCE);
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
v4Planner.addSettle(pathInput, payerIsUser, BigNumber.from(inputAmount.quotient.toString()));
|
|
534
|
+
}
|
|
535
|
+
v4Planner.addAction(Actions.SWAP_EXACT_IN, [
|
|
536
|
+
{
|
|
537
|
+
currencyIn: pathInput.isNative ? ETH_ADDRESS : pathInput.wrapped.address,
|
|
538
|
+
path: encodeV4RouteToPath(v4Route),
|
|
539
|
+
minHopPriceX36: perHopSlippage,
|
|
540
|
+
amountIn: 0,
|
|
541
|
+
amountOutMinimum: slippageToleranceOnSwap
|
|
542
|
+
? trade.minimumAmountOut(slippageToleranceOnSwap).quotient.toString()
|
|
543
|
+
: 0,
|
|
544
|
+
},
|
|
545
|
+
], toV4URVersion(options.urVersion));
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
v4Planner.addTrade(trade, slippageToleranceOnSwap, perHopSlippage, toV4URVersion(options.urVersion));
|
|
549
|
+
v4Planner.addSettle(trade.route.pathInput, payerIsUser);
|
|
550
|
+
}
|
|
551
|
+
// Handle split route output consistency:
|
|
552
|
+
// - If output is ETH and some routes output WETH: force all to output WETH, then unwrap
|
|
553
|
+
// - If output is WETH and some routes output ETH: force all to output ETH, then wrap
|
|
554
|
+
let pathOutputForTake = trade.route.pathOutput;
|
|
555
|
+
let lastPool = v4Route.pools[v4Route.pools.length - 1];
|
|
556
|
+
let ethWethPool = lastPool.currency1.equals(lastPool.currency0.wrapped);
|
|
557
|
+
if (ethWethPool && v4Route.pools.length > 1) {
|
|
558
|
+
let poolBefore = v4Route.pools[v4Route.pools.length - 2];
|
|
559
|
+
if (pathOutputForTake.isNative && poolBefore.currency0.isNative) {
|
|
560
|
+
pathOutputForTake = pathOutputForTake.wrapped;
|
|
561
|
+
}
|
|
562
|
+
else if (!pathOutputForTake.isNative &&
|
|
563
|
+
(poolBefore.currency0.equals(lastPool.currency1) || poolBefore.currency1.equals(lastPool.currency1))) {
|
|
564
|
+
pathOutputForTake = lastPool.currency0;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
// Floor exact-out delivery: TAKE the exact leg amountOut instead of OPEN_DELTA, so an
|
|
568
|
+
// under-delivering pool (e.g. liquidity exhausted at the price limit) leaves an unsettled
|
|
569
|
+
// delta and reverts at unlock instead of silently forwarding a partial fill.
|
|
570
|
+
let takeAmount;
|
|
571
|
+
if (tradeType === TradeType.EXACT_OUTPUT) {
|
|
572
|
+
takeAmount = BigNumber.from(outputAmount.quotient.toString());
|
|
573
|
+
// amount 0 encodes as the OPEN_DELTA sentinel, which would silently drop the floor
|
|
574
|
+
invariant(!takeAmount.isZero(), 'ZERO_EXACT_OUTPUT_AMOUNT');
|
|
575
|
+
}
|
|
576
|
+
v4Planner.addTake(pathOutputForTake, routerMustCustody ? ROUTER_AS_RECIPIENT : (_b = options.recipient) !== null && _b !== void 0 ? _b : SENDER_AS_RECIPIENT, takeAmount);
|
|
577
|
+
planner.addCommand(CommandType.V4_SWAP, [v4Planner.finalize()]);
|
|
578
|
+
}
|
|
579
|
+
function routeHasSettleBeforeSwapHook(pools, settleBeforeSwapHooks) {
|
|
580
|
+
if (!(settleBeforeSwapHooks === null || settleBeforeSwapHooks === void 0 ? void 0 : settleBeforeSwapHooks.length))
|
|
581
|
+
return false;
|
|
582
|
+
const hooks = new Set(settleBeforeSwapHooks.map((hook) => hook.toLowerCase()));
|
|
583
|
+
return pools.some((pool) => hooks.has(pool.hooks.toLowerCase()));
|
|
584
|
+
}
|
|
585
|
+
// encode a mixed route swap, i.e. including both v2 and v3 pools
|
|
586
|
+
function addMixedSwap(planner, swap, tradeType, options, payerIsUser, routerMustCustody) {
|
|
587
|
+
var _a, _b, _c, _d;
|
|
588
|
+
const route = swap.route;
|
|
589
|
+
if (((_a = swap.minHopPriceX36) === null || _a === void 0 ? void 0 : _a.length) && swap.minHopPriceX36.length !== route.pools.length) {
|
|
590
|
+
throw new Error(`minHopPriceX36 length (${swap.minHopPriceX36.length}) must equal route.pools.length (${route.pools.length})`);
|
|
591
|
+
}
|
|
592
|
+
const inputAmount = swap.inputAmount;
|
|
593
|
+
const outputAmount = swap.outputAmount;
|
|
594
|
+
const tradeRecipient = routerMustCustody ? ROUTER_AS_RECIPIENT : (_b = options.recipient) !== null && _b !== void 0 ? _b : SENDER_AS_RECIPIENT;
|
|
595
|
+
// single hop, so it can be reduced to plain swap logic for one protocol version
|
|
596
|
+
if (route.pools.length === 1) {
|
|
597
|
+
if (route.pools[0] instanceof V4Pool) {
|
|
598
|
+
return addV4Swap(planner, swap, tradeType, options, payerIsUser, routerMustCustody);
|
|
599
|
+
}
|
|
600
|
+
else if (route.pools[0] instanceof V3Pool) {
|
|
601
|
+
return addV3Swap(planner, swap, tradeType, options, payerIsUser, routerMustCustody);
|
|
602
|
+
}
|
|
603
|
+
else if (route.pools[0] instanceof Pair) {
|
|
604
|
+
return addV2Swap(planner, swap, tradeType, options, payerIsUser, routerMustCustody);
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
throw new Error('Invalid route type');
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
const trade = MixedRouteTrade.createUncheckedTrade({
|
|
611
|
+
route: route,
|
|
612
|
+
inputAmount,
|
|
613
|
+
outputAmount,
|
|
614
|
+
tradeType,
|
|
615
|
+
});
|
|
616
|
+
const amountIn = trade.maximumAmountIn(options.slippageTolerance, inputAmount).quotient.toString();
|
|
617
|
+
const amountOut = routerMustCustody
|
|
618
|
+
? 0
|
|
619
|
+
: trade.minimumAmountOut(options.slippageTolerance, outputAmount).quotient.toString();
|
|
620
|
+
// logic from
|
|
621
|
+
// https://github.com/Uniswap/router-sdk/blob/d8eed164e6c79519983844ca8b6a3fc24ebcb8f8/src/swapRouter.ts#L276
|
|
622
|
+
const sections = partitionMixedRouteByProtocol(route);
|
|
623
|
+
const isLastSectionInRoute = (i) => {
|
|
624
|
+
return i === sections.length - 1;
|
|
625
|
+
};
|
|
626
|
+
const useV2_1_1 = isAtLeastV2_1_1(options.urVersion);
|
|
627
|
+
let inputToken = route.pathInput;
|
|
628
|
+
let hopOffset = 0;
|
|
629
|
+
for (let i = 0; i < sections.length; i++) {
|
|
630
|
+
const section = sections[i];
|
|
631
|
+
const routePool = section[0];
|
|
632
|
+
const outputToken = getOutputOfPools(section, inputToken);
|
|
633
|
+
const subRoute = new MixedRoute(new MixedRouteSDK([...section], inputToken, outputToken));
|
|
634
|
+
// Slice this section's portion of minHopPriceX36 from the flat array
|
|
635
|
+
const sectionHopSlippage = (_c = swap.minHopPriceX36) === null || _c === void 0 ? void 0 : _c.slice(hopOffset, hopOffset + section.length);
|
|
636
|
+
let nextInputToken;
|
|
637
|
+
let swapRecipient;
|
|
638
|
+
if (isLastSectionInRoute(i)) {
|
|
639
|
+
nextInputToken = outputToken;
|
|
640
|
+
swapRecipient = tradeRecipient;
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
const nextPool = sections[i + 1][0];
|
|
644
|
+
nextInputToken = getPathCurrency(outputToken, nextPool);
|
|
645
|
+
const v2PoolIsSwapRecipient = nextPool instanceof Pair && outputToken.equals(nextInputToken);
|
|
646
|
+
swapRecipient = v2PoolIsSwapRecipient ? nextPool.liquidityToken.address : ROUTER_AS_RECIPIENT;
|
|
647
|
+
}
|
|
648
|
+
if (routePool instanceof V4Pool) {
|
|
649
|
+
const v4Planner = new V4Planner();
|
|
650
|
+
const v4SubRoute = new V4Route(section, subRoute.input, subRoute.output);
|
|
651
|
+
const v4SectionSlippage = (_d = sectionHopSlippage === null || sectionHopSlippage === void 0 ? void 0 : sectionHopSlippage.map((s) => BigNumber.from(s))) !== null && _d !== void 0 ? _d : [];
|
|
652
|
+
v4Planner.addSettle(inputToken, payerIsUser && i === 0, (i == 0 ? firstHopInputAmount(options, amountIn) : CONTRACT_BALANCE));
|
|
653
|
+
v4Planner.addAction(Actions.SWAP_EXACT_IN, [
|
|
654
|
+
{
|
|
655
|
+
currencyIn: inputToken.isNative ? ETH_ADDRESS : inputToken.address,
|
|
656
|
+
path: encodeV4RouteToPath(v4SubRoute),
|
|
657
|
+
minHopPriceX36: v4SectionSlippage,
|
|
658
|
+
amountIn: 0,
|
|
659
|
+
amountOutMinimum: !isLastSectionInRoute(i) ? 0 : amountOut,
|
|
660
|
+
},
|
|
661
|
+
], toV4URVersion(options.urVersion));
|
|
662
|
+
// Handle split route output consistency for V4 sections in mixed routes
|
|
663
|
+
let outputTokenForTake = outputToken;
|
|
664
|
+
if (isLastSectionInRoute(i)) {
|
|
665
|
+
let lastPool = route.pools[route.pools.length - 1];
|
|
666
|
+
let v4Pool = lastPool instanceof V4Pool;
|
|
667
|
+
let ethWethPool = v4Pool ? lastPool.currency1.equals(lastPool.currency0.wrapped) : false;
|
|
668
|
+
let poolBefore = route.pools[route.pools.length - 2];
|
|
669
|
+
if (ethWethPool) {
|
|
670
|
+
if (outputToken.isNative && poolBefore.token0.isNative) {
|
|
671
|
+
outputTokenForTake = outputToken.wrapped;
|
|
672
|
+
}
|
|
673
|
+
else if (!outputToken.isNative &&
|
|
674
|
+
(poolBefore.token0.equals(lastPool.token1) || poolBefore.token1.equals(lastPool.token1))) {
|
|
675
|
+
outputTokenForTake = lastPool.token0;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
v4Planner.addTake(outputTokenForTake, swapRecipient);
|
|
680
|
+
planner.addCommand(CommandType.V4_SWAP, [v4Planner.finalize()]);
|
|
681
|
+
}
|
|
682
|
+
else if (routePool instanceof V3Pool) {
|
|
683
|
+
const v3Params = [
|
|
684
|
+
swapRecipient,
|
|
685
|
+
i == 0 ? firstHopInputAmount(options, amountIn) : CONTRACT_BALANCE,
|
|
686
|
+
!isLastSectionInRoute(i) ? 0 : amountOut,
|
|
687
|
+
encodeMixedRouteToPath(subRoute),
|
|
688
|
+
payerIsUser && i === 0, // payerIsUser
|
|
689
|
+
];
|
|
690
|
+
if (useV2_1_1)
|
|
691
|
+
v3Params.push(sectionHopSlippage !== null && sectionHopSlippage !== void 0 ? sectionHopSlippage : []);
|
|
692
|
+
planner.addCommand(CommandType.V3_SWAP_EXACT_IN, v3Params, false, options.urVersion);
|
|
693
|
+
}
|
|
694
|
+
else if (routePool instanceof Pair) {
|
|
695
|
+
const v2Params = [
|
|
696
|
+
swapRecipient,
|
|
697
|
+
i === 0 ? firstHopInputAmount(options, amountIn) : CONTRACT_BALANCE,
|
|
698
|
+
!isLastSectionInRoute(i) ? 0 : amountOut,
|
|
699
|
+
subRoute.path.map((token) => token.wrapped.address),
|
|
700
|
+
payerIsUser && i === 0,
|
|
701
|
+
];
|
|
702
|
+
if (useV2_1_1)
|
|
703
|
+
v2Params.push(sectionHopSlippage !== null && sectionHopSlippage !== void 0 ? sectionHopSlippage : []);
|
|
704
|
+
planner.addCommand(CommandType.V2_SWAP_EXACT_IN, v2Params, false, options.urVersion);
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
throw new Error('Unexpected Pool Type');
|
|
708
|
+
}
|
|
709
|
+
// perform a token transition (wrap/unwrap if necessary)
|
|
710
|
+
if (!isLastSectionInRoute(i)) {
|
|
711
|
+
if (outputToken.isNative && !nextInputToken.isNative) {
|
|
712
|
+
planner.addCommand(CommandType.WRAP_ETH, [ROUTER_AS_RECIPIENT, CONTRACT_BALANCE]);
|
|
713
|
+
}
|
|
714
|
+
else if (!outputToken.isNative && nextInputToken.isNative) {
|
|
715
|
+
planner.addCommand(CommandType.UNWRAP_WETH, [ROUTER_AS_RECIPIENT, 0]);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
hopOffset += section.length;
|
|
719
|
+
inputToken = nextInputToken;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
// if price impact is very high, there's a chance of hitting max/min prices resulting in a partial fill of the swap
|
|
723
|
+
function riskOfPartialFill(trade) {
|
|
724
|
+
return trade.priceImpact.greaterThan(REFUND_ETH_PRICE_IMPACT_THRESHOLD);
|
|
725
|
+
}
|
|
726
|
+
// A lone FeeOptions becomes a one-element list, so the single-recipient path is unchanged.
|
|
727
|
+
function toFeeOptionsList(fee) {
|
|
728
|
+
if (!fee)
|
|
729
|
+
return [];
|
|
730
|
+
return Array.isArray(fee) ? fee : [fee];
|
|
731
|
+
}
|
|
732
|
+
function hasFeeOption(swapOptions) {
|
|
733
|
+
return toFeeOptionsList(swapOptions.fee).length > 0 || !!swapOptions.flatFee;
|
|
734
|
+
}
|
|
735
|
+
//# sourceMappingURL=uniswap.js.map
|