@uniswap/universal-router-sdk 4.34.2 → 4.35.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 +104 -96
- 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/{entities → cjs/src/entities}/actions/across.d.ts +21 -21
- 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 -3
- package/dist/cjs/src/entities/actions/index.js +7 -0
- package/dist/cjs/src/entities/actions/index.js.map +1 -0
- package/dist/{entities → cjs/src/entities}/actions/uniswap.d.ts +38 -39
- package/dist/cjs/src/entities/actions/uniswap.js +532 -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/{index.d.ts → cjs/src/index.d.ts} +12 -12
- package/dist/cjs/src/index.js +33 -0
- package/dist/cjs/src/index.js.map +1 -0
- package/dist/{swapRouter.d.ts → cjs/src/swapRouter.d.ts} +87 -87
- package/dist/cjs/src/swapRouter.js +276 -0
- package/dist/cjs/src/swapRouter.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/commandParser.d.ts +34 -34
- package/dist/cjs/src/utils/commandParser.js +145 -0
- package/dist/cjs/src/utils/commandParser.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/constants.d.ts +33 -33
- package/dist/cjs/src/utils/constants.js +541 -0
- package/dist/cjs/src/utils/constants.js.map +1 -0
- package/dist/{utils → cjs/src/utils}/eip712.d.ts +17 -17
- package/dist/cjs/src/utils/eip712.js +43 -0
- package/dist/cjs/src/utils/eip712.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/{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/{utils → cjs/src/utils}/routerCommands.d.ts +77 -72
- package/dist/cjs/src/utils/routerCommands.js +334 -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/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 +38 -0
- package/dist/esm/src/entities/actions/uniswap.js +528 -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 +12 -0
- package/dist/esm/src/index.js +9 -0
- package/dist/esm/src/index.js.map +1 -0
- package/dist/esm/src/swapRouter.d.ts +87 -0
- package/dist/esm/src/swapRouter.js +271 -0
- package/dist/esm/src/swapRouter.js.map +1 -0
- package/dist/esm/src/utils/commandParser.d.ts +34 -0
- package/dist/esm/src/utils/commandParser.js +137 -0
- package/dist/esm/src/utils/commandParser.js.map +1 -0
- package/dist/esm/src/utils/constants.d.ts +33 -0
- package/dist/esm/src/utils/constants.js +534 -0
- package/dist/esm/src/utils/constants.js.map +1 -0
- package/dist/esm/src/utils/eip712.d.ts +17 -0
- package/dist/esm/src/utils/eip712.js +38 -0
- package/dist/esm/src/utils/eip712.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/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/routerCommands.d.ts +77 -0
- package/dist/esm/src/utils/routerCommands.js +329 -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/types/src/entities/Command.d.ts +12 -0
- package/dist/types/src/entities/actions/across.d.ts +21 -0
- package/dist/types/src/entities/actions/index.d.ts +3 -0
- package/dist/types/src/entities/actions/uniswap.d.ts +38 -0
- package/dist/types/src/entities/actions/unwrapWETH.d.ts +12 -0
- package/dist/types/src/entities/index.d.ts +2 -0
- package/dist/types/src/index.d.ts +12 -0
- package/dist/types/src/swapRouter.d.ts +87 -0
- package/dist/types/src/utils/commandParser.d.ts +34 -0
- package/dist/types/src/utils/constants.d.ts +33 -0
- package/dist/types/src/utils/eip712.d.ts +17 -0
- package/dist/types/src/utils/getCurrencyAddress.d.ts +2 -0
- package/dist/types/src/utils/inputTokens.d.ts +23 -0
- package/dist/types/src/utils/numbers.d.ts +7 -0
- package/dist/types/src/utils/pathCurrency.d.ts +3 -0
- package/dist/types/src/utils/routerCommands.d.ts +77 -0
- package/dist/types/src/utils/routerTradeAdapter.d.ts +73 -0
- package/package.json +31 -22
- package/dist/index.js +0 -8
- 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 -24
- package/dist/universal-router-sdk.cjs.development.js +0 -1925
- 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 -1912
- package/dist/universal-router-sdk.esm.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Command.js","sourceRoot":"","sources":["../../../../src/entities/Command.ts"],"names":[],"mappings":"AAMA,MAAM,CAAN,IAAY,gBAGX;AAHD,WAAY,gBAAgB;IAC1B,iDAA6B,CAAA;IAC7B,6CAAyB,CAAA;AAC3B,CAAC,EAHW,gBAAgB,KAAhB,gBAAgB,QAG3B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BigNumberish } from 'ethers';
|
|
2
|
+
/**
|
|
3
|
+
* Parameters for Across V4 Deposit V3 command
|
|
4
|
+
* Used for cross-chain bridging via Across Protocol V3 SpokePool
|
|
5
|
+
*/
|
|
6
|
+
export type AcrossV4DepositV3Params = {
|
|
7
|
+
depositor: string;
|
|
8
|
+
recipient: string;
|
|
9
|
+
inputToken: string;
|
|
10
|
+
outputToken: string;
|
|
11
|
+
inputAmount: BigNumberish;
|
|
12
|
+
outputAmount: BigNumberish;
|
|
13
|
+
destinationChainId: number;
|
|
14
|
+
exclusiveRelayer: string;
|
|
15
|
+
quoteTimestamp: number;
|
|
16
|
+
fillDeadline: number;
|
|
17
|
+
exclusivityDeadline: number;
|
|
18
|
+
message: string;
|
|
19
|
+
useNative: boolean;
|
|
20
|
+
};
|
|
21
|
+
export { CONTRACT_BALANCE } from '../../utils/constants';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"across.js","sourceRoot":"","sources":["../../../../../src/entities/actions/across.ts"],"names":[],"mappings":"AAsBA,mDAAmD;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/entities/actions/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { RoutePlanner } from '../../utils/routerCommands';
|
|
2
|
+
import { URVersion } from '@uniswap/v4-sdk';
|
|
3
|
+
import { Trade as RouterTrade, SwapOptions as RouterSwapOptions } from '@uniswap/router-sdk';
|
|
4
|
+
import { Permit2Permit } from '../../utils/inputTokens';
|
|
5
|
+
import { Currency, TradeType } from '@uniswap/sdk-core';
|
|
6
|
+
import { Command, RouterActionType, TradeConfig } from '../Command';
|
|
7
|
+
import { BigNumberish } from 'ethers';
|
|
8
|
+
export type FlatFeeOptions = {
|
|
9
|
+
amount: BigNumberish;
|
|
10
|
+
recipient: string;
|
|
11
|
+
};
|
|
12
|
+
export declare enum TokenTransferMode {
|
|
13
|
+
Permit2 = "Permit2",
|
|
14
|
+
ApproveProxy = "ApproveProxy"
|
|
15
|
+
}
|
|
16
|
+
export type SwapOptions = Omit<RouterSwapOptions, 'inputTokenPermit'> & {
|
|
17
|
+
useRouterBalance?: boolean;
|
|
18
|
+
inputTokenPermit?: Permit2Permit;
|
|
19
|
+
flatFee?: FlatFeeOptions;
|
|
20
|
+
safeMode?: boolean;
|
|
21
|
+
urVersion?: URVersion;
|
|
22
|
+
tokenTransferMode?: TokenTransferMode;
|
|
23
|
+
chainId?: number;
|
|
24
|
+
};
|
|
25
|
+
export declare class UniswapTrade implements Command {
|
|
26
|
+
trade: RouterTrade<Currency, Currency, TradeType>;
|
|
27
|
+
options: SwapOptions;
|
|
28
|
+
readonly tradeType: RouterActionType;
|
|
29
|
+
readonly payerIsUser: boolean;
|
|
30
|
+
constructor(trade: RouterTrade<Currency, Currency, TradeType>, options: SwapOptions);
|
|
31
|
+
get isAllV4(): boolean;
|
|
32
|
+
get inputRequiresWrap(): boolean;
|
|
33
|
+
get inputRequiresUnwrap(): boolean;
|
|
34
|
+
get outputRequiresWrap(): boolean;
|
|
35
|
+
get outputRequiresUnwrap(): boolean;
|
|
36
|
+
get outputRequiresTransition(): boolean;
|
|
37
|
+
encode(planner: RoutePlanner, _config: TradeConfig): void;
|
|
38
|
+
}
|
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
import { CommandType } from '../../utils/routerCommands';
|
|
2
|
+
import { Trade as V2Trade, Pair } from '@uniswap/v2-sdk';
|
|
3
|
+
import { Trade as V3Trade, Pool as V3Pool, encodeRouteToPath } from '@uniswap/v3-sdk';
|
|
4
|
+
import { Route as V4Route, Trade as V4Trade, Pool as V4Pool, V4Planner, encodeRouteToPath as encodeV4RouteToPath, Actions, URVersion, isAtLeastV2_1_1, } from '@uniswap/v4-sdk';
|
|
5
|
+
import { MixedRouteTrade, Protocol, MixedRouteSDK, MixedRoute, getOutputOfPools, encodeMixedRouteToPath, partitionMixedRouteByProtocol, } from '@uniswap/router-sdk';
|
|
6
|
+
import { getPathCurrency } from '../../utils/pathCurrency';
|
|
7
|
+
import { TradeType, Percent } from '@uniswap/sdk-core';
|
|
8
|
+
import { RouterActionType } from '../Command';
|
|
9
|
+
import { SENDER_AS_RECIPIENT, ROUTER_AS_RECIPIENT, CONTRACT_BALANCE, ETH_ADDRESS } from '../../utils/constants';
|
|
10
|
+
import { getCurrencyAddress } from '../../utils/getCurrencyAddress';
|
|
11
|
+
import { encodeFeeBips, encodeFee1e18 } from '../../utils/numbers';
|
|
12
|
+
import { BigNumber } from 'ethers';
|
|
13
|
+
// the existing router permit object doesn't include enough data for permit2
|
|
14
|
+
// so we extend swap options with the permit2 permit
|
|
15
|
+
// when safe mode is enabled, the SDK will add an extra ETH sweep for security
|
|
16
|
+
// when useRouterBalance is enabled the SDK will use the balance in the router for the swap
|
|
17
|
+
export var TokenTransferMode;
|
|
18
|
+
(function (TokenTransferMode) {
|
|
19
|
+
TokenTransferMode["Permit2"] = "Permit2";
|
|
20
|
+
TokenTransferMode["ApproveProxy"] = "ApproveProxy";
|
|
21
|
+
})(TokenTransferMode || (TokenTransferMode = {}));
|
|
22
|
+
const REFUND_ETH_PRICE_IMPACT_THRESHOLD = new Percent(50, 100);
|
|
23
|
+
// Wrapper for uniswap router-sdk trade entity to encode swaps for Universal Router
|
|
24
|
+
// also translates trade objects from previous (v2, v3) SDKs
|
|
25
|
+
export class UniswapTrade {
|
|
26
|
+
constructor(trade, options) {
|
|
27
|
+
this.trade = trade;
|
|
28
|
+
this.options = options;
|
|
29
|
+
this.tradeType = RouterActionType.UniswapTrade;
|
|
30
|
+
if (!!options.fee && !!options.flatFee)
|
|
31
|
+
throw new Error('Only one fee option permitted');
|
|
32
|
+
if (options.tokenTransferMode === TokenTransferMode.ApproveProxy) {
|
|
33
|
+
if (!options.recipient || options.recipient === SENDER_AS_RECIPIENT) {
|
|
34
|
+
throw new Error('Explicit recipient address required when using SwapProxy (SENDER_AS_RECIPIENT resolves to proxy)');
|
|
35
|
+
}
|
|
36
|
+
this.payerIsUser = false;
|
|
37
|
+
}
|
|
38
|
+
else if (this.inputRequiresWrap || this.inputRequiresUnwrap || this.options.useRouterBalance) {
|
|
39
|
+
this.payerIsUser = false;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
this.payerIsUser = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
get isAllV4() {
|
|
46
|
+
let result = true;
|
|
47
|
+
for (const swap of this.trade.swaps) {
|
|
48
|
+
result = result && swap.route.protocol == Protocol.V4;
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
// this.trade.swaps is an array of swaps / trades.
|
|
53
|
+
// we are iterating over one swap (trade) at a time so length is 1
|
|
54
|
+
// route is either v2, v3, v4, or mixed
|
|
55
|
+
// pathInput and pathOutput are the currencies of the input and output of the route
|
|
56
|
+
// this.trade.inputAmount is the input currency of the trade (could be different from pathInput)
|
|
57
|
+
// this.trade.outputAmount is the output currency of the trade (could be different from pathOutput)
|
|
58
|
+
// each route can have multiple pools
|
|
59
|
+
get inputRequiresWrap() {
|
|
60
|
+
const swap = this.trade.swaps[0];
|
|
61
|
+
const route = swap.route;
|
|
62
|
+
const firstPool = route.pools[0];
|
|
63
|
+
if (firstPool instanceof V4Pool) {
|
|
64
|
+
// 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.
|
|
65
|
+
return (this.trade.inputAmount.currency.isNative &&
|
|
66
|
+
!this.trade.swaps[0].route.pathInput.isNative);
|
|
67
|
+
}
|
|
68
|
+
// If first pool is not a v4 pool and input currency is native, we need to wrap
|
|
69
|
+
return this.trade.inputAmount.currency.isNative;
|
|
70
|
+
}
|
|
71
|
+
get inputRequiresUnwrap() {
|
|
72
|
+
const swap = this.trade.swaps[0];
|
|
73
|
+
const route = swap.route;
|
|
74
|
+
const firstPool = route.pools[0];
|
|
75
|
+
if (firstPool instanceof V4Pool) {
|
|
76
|
+
// 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
|
|
77
|
+
return (!this.trade.inputAmount.currency.isNative &&
|
|
78
|
+
this.trade.swaps[0].route.pathInput.isNative);
|
|
79
|
+
}
|
|
80
|
+
// If the first pool is not a v4 pool, we don't need to unwrap.
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
get outputRequiresWrap() {
|
|
84
|
+
const swap = this.trade.swaps[0];
|
|
85
|
+
const lastRoute = swap.route;
|
|
86
|
+
const lastPool = lastRoute.pools[lastRoute.pools.length - 1];
|
|
87
|
+
// if last pool is v4:
|
|
88
|
+
if (lastPool instanceof V4Pool) {
|
|
89
|
+
// If output currency is not native but path currency output is native, we need to wrap
|
|
90
|
+
if (!this.trade.outputAmount.currency.isNative) {
|
|
91
|
+
if (lastRoute.pathOutput.isNative) {
|
|
92
|
+
// this means path output is native and we need to wrap
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
else if (lastPool.currency1.equals(lastPool.currency0.wrapped) && lastRoute.pools.length > 1) {
|
|
96
|
+
let poolBefore = lastRoute.pools[lastRoute.pools.length - 2];
|
|
97
|
+
// this means last pool is eth-weth and pool before contains weth
|
|
98
|
+
if (poolBefore instanceof V4Pool &&
|
|
99
|
+
(poolBefore.currency0.equals(lastPool.currency1) || poolBefore.currency1.equals(lastPool.currency1))) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
else if (poolBefore.token0.equals(lastPool.currency1) || poolBefore.token1.equals(lastPool.currency1)) {
|
|
103
|
+
// same for v2 and v3 pools
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// if last pool is not v4:
|
|
110
|
+
// we do not need to wrap because v2 and v3 pools already require wrapped tokens
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
get outputRequiresUnwrap() {
|
|
114
|
+
const swap = this.trade.swaps[0];
|
|
115
|
+
const lastRoute = swap.route;
|
|
116
|
+
const lastPool = lastRoute.pools[lastRoute.pools.length - 1];
|
|
117
|
+
// if last pool is v4:
|
|
118
|
+
if (lastPool instanceof V4Pool) {
|
|
119
|
+
// If output currency is native and path currency output is not native, we need to unwrap
|
|
120
|
+
if (this.trade.outputAmount.currency.isNative) {
|
|
121
|
+
if (!this.trade.swaps[0].route.pathOutput.isNative) {
|
|
122
|
+
// this means path output is weth and we need to unwrap
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
else if (lastRoute.pools.length > 1 &&
|
|
126
|
+
lastRoute.pools[lastRoute.pools.length - 2] instanceof V4Pool &&
|
|
127
|
+
lastRoute.pools[lastRoute.pools.length - 2].currency0.isNative &&
|
|
128
|
+
lastPool.currency1.equals(lastPool.currency0.wrapped)) {
|
|
129
|
+
// this means last pool is eth-weth and we need to unwrap
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// else: if path output currency is native, we need to unwrap because v2 and v3 pools already require wrapped tokens
|
|
138
|
+
return this.trade.outputAmount.currency.isNative;
|
|
139
|
+
}
|
|
140
|
+
get outputRequiresTransition() {
|
|
141
|
+
return this.outputRequiresWrap || this.outputRequiresUnwrap;
|
|
142
|
+
}
|
|
143
|
+
encode(planner, _config) {
|
|
144
|
+
var _a;
|
|
145
|
+
// If the input currency is the native currency, we need to wrap it with the router as the recipient
|
|
146
|
+
if (this.inputRequiresWrap) {
|
|
147
|
+
// TODO: optimize if only one v2 pool we can directly send this to the pool
|
|
148
|
+
planner.addCommand(CommandType.WRAP_ETH, [
|
|
149
|
+
ROUTER_AS_RECIPIENT,
|
|
150
|
+
this.trade.maximumAmountIn(this.options.slippageTolerance).quotient.toString(),
|
|
151
|
+
]);
|
|
152
|
+
}
|
|
153
|
+
else if (this.inputRequiresUnwrap) {
|
|
154
|
+
if (this.options.tokenTransferMode !== TokenTransferMode.ApproveProxy) {
|
|
155
|
+
// send wrapped token to router to unwrap via Permit2
|
|
156
|
+
planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [
|
|
157
|
+
this.trade.inputAmount.currency.address,
|
|
158
|
+
ROUTER_AS_RECIPIENT,
|
|
159
|
+
this.trade.maximumAmountIn(this.options.slippageTolerance).quotient.toString(),
|
|
160
|
+
]);
|
|
161
|
+
}
|
|
162
|
+
// In proxy mode, the proxy already transferred tokens to the UR; just unwrap
|
|
163
|
+
planner.addCommand(CommandType.UNWRAP_WETH, [ROUTER_AS_RECIPIENT, 0]);
|
|
164
|
+
}
|
|
165
|
+
// The overall recipient at the end of the trade, SENDER_AS_RECIPIENT uses the msg.sender
|
|
166
|
+
this.options.recipient = (_a = this.options.recipient) !== null && _a !== void 0 ? _a : SENDER_AS_RECIPIENT;
|
|
167
|
+
// flag for whether we want to perform slippage check on aggregate output of multiple routes
|
|
168
|
+
// 1. when there are >2 exact input trades. this is only a heuristic,
|
|
169
|
+
// as it's still more gas-expensive even in this case, but has benefits
|
|
170
|
+
// in that the reversion probability is lower
|
|
171
|
+
const performAggregatedSlippageCheck = this.trade.tradeType === TradeType.EXACT_INPUT && this.trade.routes.length > 2;
|
|
172
|
+
const routerMustCustody = performAggregatedSlippageCheck || this.outputRequiresTransition || hasFeeOption(this.options);
|
|
173
|
+
for (const swap of this.trade.swaps) {
|
|
174
|
+
switch (swap.route.protocol) {
|
|
175
|
+
case Protocol.V2:
|
|
176
|
+
addV2Swap(planner, swap, this.trade.tradeType, this.options, this.payerIsUser, routerMustCustody);
|
|
177
|
+
break;
|
|
178
|
+
case Protocol.V3:
|
|
179
|
+
addV3Swap(planner, swap, this.trade.tradeType, this.options, this.payerIsUser, routerMustCustody);
|
|
180
|
+
break;
|
|
181
|
+
case Protocol.V4:
|
|
182
|
+
addV4Swap(planner, swap, this.trade.tradeType, this.options, this.payerIsUser, routerMustCustody);
|
|
183
|
+
break;
|
|
184
|
+
case Protocol.MIXED:
|
|
185
|
+
addMixedSwap(planner, swap, this.trade.tradeType, this.options, this.payerIsUser, routerMustCustody);
|
|
186
|
+
break;
|
|
187
|
+
default:
|
|
188
|
+
throw new Error('UNSUPPORTED_TRADE_PROTOCOL');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
let minimumAmountOut = BigNumber.from(this.trade.minimumAmountOut(this.options.slippageTolerance).quotient.toString());
|
|
192
|
+
// The router custodies for 3 reasons: to unwrap, to take a fee, and/or to do a slippage check
|
|
193
|
+
if (routerMustCustody) {
|
|
194
|
+
const pools = this.trade.swaps[0].route.pools;
|
|
195
|
+
const pathOutputCurrencyAddress = getCurrencyAddress(getPathCurrency(this.trade.outputAmount.currency, pools[pools.length - 1]));
|
|
196
|
+
let feeDeduction = BigNumber.from(0);
|
|
197
|
+
// If there is a fee, that percentage is sent to the fee recipient
|
|
198
|
+
// In the case where ETH is the output currency, the fee is taken in WETH (for gas reasons)
|
|
199
|
+
if (!!this.options.fee) {
|
|
200
|
+
// UR >= V2_1_1 supports PAY_PORTION_FULL_PRECISION (1e18 precision),
|
|
201
|
+
// older versions only support PAY_PORTION (bips)
|
|
202
|
+
const useFullPrecision = this.options.urVersion !== undefined && this.options.urVersion >= URVersion.V2_1_1;
|
|
203
|
+
// Reject fractional bips fees on older UR versions to prevent silent precision loss
|
|
204
|
+
if (!useFullPrecision && !this.options.fee.fee.multiply(10000).remainder.equalTo(0)) {
|
|
205
|
+
throw new Error('Fractional fee bips require Universal Router version V2_1_1 or higher');
|
|
206
|
+
}
|
|
207
|
+
if (useFullPrecision) {
|
|
208
|
+
const fee1e18 = encodeFee1e18(this.options.fee.fee);
|
|
209
|
+
planner.addCommand(CommandType.PAY_PORTION_FULL_PRECISION, [pathOutputCurrencyAddress, this.options.fee.recipient, fee1e18], false, this.options.urVersion);
|
|
210
|
+
feeDeduction = minimumAmountOut.mul(fee1e18).div(BigNumber.from(10).pow(18));
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
const feeBips = encodeFeeBips(this.options.fee.fee);
|
|
214
|
+
planner.addCommand(CommandType.PAY_PORTION, [pathOutputCurrencyAddress, this.options.fee.recipient, feeBips]);
|
|
215
|
+
feeDeduction = minimumAmountOut.mul(feeBips).div(10000);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// If there is a flat fee, that absolute amount is sent to the fee recipient
|
|
219
|
+
// In the case where ETH is the output currency, the fee is taken in WETH (for gas reasons)
|
|
220
|
+
if (!!this.options.flatFee) {
|
|
221
|
+
const feeAmount = this.options.flatFee.amount;
|
|
222
|
+
if (minimumAmountOut.lt(feeAmount))
|
|
223
|
+
throw new Error('Flat fee amount greater than minimumAmountOut');
|
|
224
|
+
planner.addCommand(CommandType.TRANSFER, [pathOutputCurrencyAddress, this.options.flatFee.recipient, feeAmount]);
|
|
225
|
+
feeDeduction = BigNumber.from(feeAmount);
|
|
226
|
+
}
|
|
227
|
+
// If the trade is exact output, and a fee was taken, we must adjust the amount out to be the amount after the fee
|
|
228
|
+
// Otherwise we continue as expected with the trade's normal expected output
|
|
229
|
+
if (this.trade.tradeType === TradeType.EXACT_OUTPUT) {
|
|
230
|
+
minimumAmountOut = minimumAmountOut.sub(feeDeduction);
|
|
231
|
+
}
|
|
232
|
+
// The remaining tokens that need to be sent to the user after the fee is taken will be caught
|
|
233
|
+
// by this if-else clause.
|
|
234
|
+
if (this.outputRequiresUnwrap) {
|
|
235
|
+
planner.addCommand(CommandType.UNWRAP_WETH, [this.options.recipient, minimumAmountOut]);
|
|
236
|
+
}
|
|
237
|
+
else if (this.outputRequiresWrap) {
|
|
238
|
+
planner.addCommand(CommandType.WRAP_ETH, [this.options.recipient, CONTRACT_BALANCE]);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
planner.addCommand(CommandType.SWEEP, [
|
|
242
|
+
getCurrencyAddress(this.trade.outputAmount.currency),
|
|
243
|
+
this.options.recipient,
|
|
244
|
+
minimumAmountOut,
|
|
245
|
+
]);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// for exactOutput swaps with native input or that perform an inputToken transition (wrap or unwrap)
|
|
249
|
+
// we need to send back the change to the user
|
|
250
|
+
if (this.trade.tradeType === TradeType.EXACT_OUTPUT || riskOfPartialFill(this.trade)) {
|
|
251
|
+
if (this.inputRequiresWrap) {
|
|
252
|
+
planner.addCommand(CommandType.UNWRAP_WETH, [this.options.recipient, 0]);
|
|
253
|
+
}
|
|
254
|
+
else if (this.inputRequiresUnwrap) {
|
|
255
|
+
planner.addCommand(CommandType.WRAP_ETH, [this.options.recipient, CONTRACT_BALANCE]);
|
|
256
|
+
}
|
|
257
|
+
else if (this.options.tokenTransferMode === TokenTransferMode.ApproveProxy) {
|
|
258
|
+
// Proxy pulled maximumAmountIn into the UR; sweep any unused input back to the user
|
|
259
|
+
planner.addCommand(CommandType.SWEEP, [
|
|
260
|
+
getCurrencyAddress(this.trade.inputAmount.currency),
|
|
261
|
+
this.options.recipient,
|
|
262
|
+
0,
|
|
263
|
+
]);
|
|
264
|
+
}
|
|
265
|
+
else if (this.trade.inputAmount.currency.isNative) {
|
|
266
|
+
// must refund extra native currency sent along for native v4 trades (no input transition)
|
|
267
|
+
planner.addCommand(CommandType.SWEEP, [ETH_ADDRESS, this.options.recipient, 0]);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (this.options.safeMode)
|
|
271
|
+
planner.addCommand(CommandType.SWEEP, [ETH_ADDRESS, this.options.recipient, 0]);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// encode a uniswap v2 swap
|
|
275
|
+
function addV2Swap(planner, { route, inputAmount, outputAmount, maxHopSlippage }, tradeType, options, payerIsUser, routerMustCustody) {
|
|
276
|
+
if ((maxHopSlippage === null || maxHopSlippage === void 0 ? void 0 : maxHopSlippage.length) && maxHopSlippage.length !== route.pools.length) {
|
|
277
|
+
throw new Error(`maxHopSlippage length (${maxHopSlippage.length}) must equal route.pools.length (${route.pools.length})`);
|
|
278
|
+
}
|
|
279
|
+
const trade = new V2Trade(route, tradeType == TradeType.EXACT_INPUT ? inputAmount : outputAmount, tradeType);
|
|
280
|
+
const useV2_1_1 = isAtLeastV2_1_1(options.urVersion);
|
|
281
|
+
if (tradeType == TradeType.EXACT_INPUT) {
|
|
282
|
+
const params = [
|
|
283
|
+
// if native, we have to unwrap so keep in the router for now
|
|
284
|
+
routerMustCustody ? ROUTER_AS_RECIPIENT : options.recipient,
|
|
285
|
+
trade.maximumAmountIn(options.slippageTolerance).quotient.toString(),
|
|
286
|
+
// if router will custody funds, we do aggregated slippage check from router
|
|
287
|
+
routerMustCustody ? 0 : trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
|
|
288
|
+
route.path.map((token) => token.wrapped.address),
|
|
289
|
+
payerIsUser,
|
|
290
|
+
];
|
|
291
|
+
if (useV2_1_1)
|
|
292
|
+
params.push(maxHopSlippage !== null && maxHopSlippage !== void 0 ? maxHopSlippage : []);
|
|
293
|
+
planner.addCommand(CommandType.V2_SWAP_EXACT_IN, params, false, options.urVersion);
|
|
294
|
+
}
|
|
295
|
+
else if (tradeType == TradeType.EXACT_OUTPUT) {
|
|
296
|
+
const params = [
|
|
297
|
+
routerMustCustody ? ROUTER_AS_RECIPIENT : options.recipient,
|
|
298
|
+
trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
|
|
299
|
+
trade.maximumAmountIn(options.slippageTolerance).quotient.toString(),
|
|
300
|
+
route.path.map((token) => token.wrapped.address),
|
|
301
|
+
payerIsUser,
|
|
302
|
+
];
|
|
303
|
+
if (useV2_1_1)
|
|
304
|
+
params.push(maxHopSlippage !== null && maxHopSlippage !== void 0 ? maxHopSlippage : []);
|
|
305
|
+
planner.addCommand(CommandType.V2_SWAP_EXACT_OUT, params, false, options.urVersion);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// encode a uniswap v3 swap
|
|
309
|
+
function addV3Swap(planner, { route, inputAmount, outputAmount, maxHopSlippage }, tradeType, options, payerIsUser, routerMustCustody) {
|
|
310
|
+
if ((maxHopSlippage === null || maxHopSlippage === void 0 ? void 0 : maxHopSlippage.length) && maxHopSlippage.length !== route.pools.length) {
|
|
311
|
+
throw new Error(`maxHopSlippage length (${maxHopSlippage.length}) must equal route.pools.length (${route.pools.length})`);
|
|
312
|
+
}
|
|
313
|
+
const trade = V3Trade.createUncheckedTrade({
|
|
314
|
+
route: route,
|
|
315
|
+
inputAmount,
|
|
316
|
+
outputAmount,
|
|
317
|
+
tradeType,
|
|
318
|
+
});
|
|
319
|
+
const useV2_1_1 = isAtLeastV2_1_1(options.urVersion);
|
|
320
|
+
const path = encodeRouteToPath(route, trade.tradeType === TradeType.EXACT_OUTPUT);
|
|
321
|
+
if (tradeType == TradeType.EXACT_INPUT) {
|
|
322
|
+
const params = [
|
|
323
|
+
routerMustCustody ? ROUTER_AS_RECIPIENT : options.recipient,
|
|
324
|
+
trade.maximumAmountIn(options.slippageTolerance).quotient.toString(),
|
|
325
|
+
routerMustCustody ? 0 : trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
|
|
326
|
+
path,
|
|
327
|
+
payerIsUser,
|
|
328
|
+
];
|
|
329
|
+
if (useV2_1_1)
|
|
330
|
+
params.push(maxHopSlippage !== null && maxHopSlippage !== void 0 ? maxHopSlippage : []);
|
|
331
|
+
planner.addCommand(CommandType.V3_SWAP_EXACT_IN, params, false, options.urVersion);
|
|
332
|
+
}
|
|
333
|
+
else if (tradeType == TradeType.EXACT_OUTPUT) {
|
|
334
|
+
const params = [
|
|
335
|
+
routerMustCustody ? ROUTER_AS_RECIPIENT : options.recipient,
|
|
336
|
+
trade.minimumAmountOut(options.slippageTolerance).quotient.toString(),
|
|
337
|
+
trade.maximumAmountIn(options.slippageTolerance).quotient.toString(),
|
|
338
|
+
path,
|
|
339
|
+
payerIsUser,
|
|
340
|
+
];
|
|
341
|
+
if (useV2_1_1)
|
|
342
|
+
params.push(maxHopSlippage !== null && maxHopSlippage !== void 0 ? maxHopSlippage : []);
|
|
343
|
+
planner.addCommand(CommandType.V3_SWAP_EXACT_OUT, params, false, options.urVersion);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function addV4Swap(planner, { inputAmount, outputAmount, route, maxHopSlippage }, tradeType, options, payerIsUser, routerMustCustody) {
|
|
347
|
+
var _a, _b;
|
|
348
|
+
if ((maxHopSlippage === null || maxHopSlippage === void 0 ? void 0 : maxHopSlippage.length) && maxHopSlippage.length !== route.pools.length) {
|
|
349
|
+
throw new Error(`maxHopSlippage length (${maxHopSlippage.length}) must equal route.pools.length (${route.pools.length})`);
|
|
350
|
+
}
|
|
351
|
+
// create a deep copy of pools since v4Planner encoding tampers with array
|
|
352
|
+
const pools = route.pools.map((p) => p);
|
|
353
|
+
const v4Route = new V4Route(pools, inputAmount.currency, outputAmount.currency);
|
|
354
|
+
const trade = V4Trade.createUncheckedTrade({
|
|
355
|
+
route: v4Route,
|
|
356
|
+
inputAmount,
|
|
357
|
+
outputAmount,
|
|
358
|
+
tradeType,
|
|
359
|
+
});
|
|
360
|
+
const slippageToleranceOnSwap = routerMustCustody && tradeType == TradeType.EXACT_INPUT ? undefined : options.slippageTolerance;
|
|
361
|
+
const perHopSlippage = (_a = maxHopSlippage === null || maxHopSlippage === void 0 ? void 0 : maxHopSlippage.map((s) => BigNumber.from(s))) !== null && _a !== void 0 ? _a : [];
|
|
362
|
+
const v4Planner = new V4Planner();
|
|
363
|
+
v4Planner.addTrade(trade, slippageToleranceOnSwap, perHopSlippage, options.urVersion);
|
|
364
|
+
v4Planner.addSettle(trade.route.pathInput, payerIsUser);
|
|
365
|
+
// Handle split route output consistency:
|
|
366
|
+
// - If output is ETH and some routes output WETH: force all to output WETH, then unwrap
|
|
367
|
+
// - If output is WETH and some routes output ETH: force all to output ETH, then wrap
|
|
368
|
+
let pathOutputForTake = trade.route.pathOutput;
|
|
369
|
+
let lastPool = v4Route.pools[v4Route.pools.length - 1];
|
|
370
|
+
let ethWethPool = lastPool.currency1.equals(lastPool.currency0.wrapped);
|
|
371
|
+
if (ethWethPool && v4Route.pools.length > 1) {
|
|
372
|
+
let poolBefore = v4Route.pools[v4Route.pools.length - 2];
|
|
373
|
+
if (pathOutputForTake.isNative && poolBefore.currency0.isNative) {
|
|
374
|
+
pathOutputForTake = pathOutputForTake.wrapped;
|
|
375
|
+
}
|
|
376
|
+
else if (!pathOutputForTake.isNative &&
|
|
377
|
+
(poolBefore.currency0.equals(lastPool.currency1) || poolBefore.currency1.equals(lastPool.currency1))) {
|
|
378
|
+
pathOutputForTake = lastPool.currency0;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
v4Planner.addTake(pathOutputForTake, routerMustCustody ? ROUTER_AS_RECIPIENT : (_b = options.recipient) !== null && _b !== void 0 ? _b : SENDER_AS_RECIPIENT);
|
|
382
|
+
planner.addCommand(CommandType.V4_SWAP, [v4Planner.finalize()]);
|
|
383
|
+
}
|
|
384
|
+
// encode a mixed route swap, i.e. including both v2 and v3 pools
|
|
385
|
+
function addMixedSwap(planner, swap, tradeType, options, payerIsUser, routerMustCustody) {
|
|
386
|
+
var _a, _b, _c, _d;
|
|
387
|
+
const route = swap.route;
|
|
388
|
+
if (((_a = swap.maxHopSlippage) === null || _a === void 0 ? void 0 : _a.length) && swap.maxHopSlippage.length !== route.pools.length) {
|
|
389
|
+
throw new Error(`maxHopSlippage length (${swap.maxHopSlippage.length}) must equal route.pools.length (${route.pools.length})`);
|
|
390
|
+
}
|
|
391
|
+
const inputAmount = swap.inputAmount;
|
|
392
|
+
const outputAmount = swap.outputAmount;
|
|
393
|
+
const tradeRecipient = routerMustCustody ? ROUTER_AS_RECIPIENT : (_b = options.recipient) !== null && _b !== void 0 ? _b : SENDER_AS_RECIPIENT;
|
|
394
|
+
// single hop, so it can be reduced to plain swap logic for one protocol version
|
|
395
|
+
if (route.pools.length === 1) {
|
|
396
|
+
if (route.pools[0] instanceof V4Pool) {
|
|
397
|
+
return addV4Swap(planner, swap, tradeType, options, payerIsUser, routerMustCustody);
|
|
398
|
+
}
|
|
399
|
+
else if (route.pools[0] instanceof V3Pool) {
|
|
400
|
+
return addV3Swap(planner, swap, tradeType, options, payerIsUser, routerMustCustody);
|
|
401
|
+
}
|
|
402
|
+
else if (route.pools[0] instanceof Pair) {
|
|
403
|
+
return addV2Swap(planner, swap, tradeType, options, payerIsUser, routerMustCustody);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
throw new Error('Invalid route type');
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const trade = MixedRouteTrade.createUncheckedTrade({
|
|
410
|
+
route: route,
|
|
411
|
+
inputAmount,
|
|
412
|
+
outputAmount,
|
|
413
|
+
tradeType,
|
|
414
|
+
});
|
|
415
|
+
const amountIn = trade.maximumAmountIn(options.slippageTolerance, inputAmount).quotient.toString();
|
|
416
|
+
const amountOut = routerMustCustody
|
|
417
|
+
? 0
|
|
418
|
+
: trade.minimumAmountOut(options.slippageTolerance, outputAmount).quotient.toString();
|
|
419
|
+
// logic from
|
|
420
|
+
// https://github.com/Uniswap/router-sdk/blob/d8eed164e6c79519983844ca8b6a3fc24ebcb8f8/src/swapRouter.ts#L276
|
|
421
|
+
const sections = partitionMixedRouteByProtocol(route);
|
|
422
|
+
const isLastSectionInRoute = (i) => {
|
|
423
|
+
return i === sections.length - 1;
|
|
424
|
+
};
|
|
425
|
+
const useV2_1_1 = isAtLeastV2_1_1(options.urVersion);
|
|
426
|
+
let inputToken = route.pathInput;
|
|
427
|
+
let hopOffset = 0;
|
|
428
|
+
for (let i = 0; i < sections.length; i++) {
|
|
429
|
+
const section = sections[i];
|
|
430
|
+
const routePool = section[0];
|
|
431
|
+
const outputToken = getOutputOfPools(section, inputToken);
|
|
432
|
+
const subRoute = new MixedRoute(new MixedRouteSDK([...section], inputToken, outputToken));
|
|
433
|
+
// Slice this section's portion of maxHopSlippage from the flat array
|
|
434
|
+
const sectionHopSlippage = (_c = swap.maxHopSlippage) === null || _c === void 0 ? void 0 : _c.slice(hopOffset, hopOffset + section.length);
|
|
435
|
+
let nextInputToken;
|
|
436
|
+
let swapRecipient;
|
|
437
|
+
if (isLastSectionInRoute(i)) {
|
|
438
|
+
nextInputToken = outputToken;
|
|
439
|
+
swapRecipient = tradeRecipient;
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
const nextPool = sections[i + 1][0];
|
|
443
|
+
nextInputToken = getPathCurrency(outputToken, nextPool);
|
|
444
|
+
const v2PoolIsSwapRecipient = nextPool instanceof Pair && outputToken.equals(nextInputToken);
|
|
445
|
+
swapRecipient = v2PoolIsSwapRecipient ? nextPool.liquidityToken.address : ROUTER_AS_RECIPIENT;
|
|
446
|
+
}
|
|
447
|
+
if (routePool instanceof V4Pool) {
|
|
448
|
+
const v4Planner = new V4Planner();
|
|
449
|
+
const v4SubRoute = new V4Route(section, subRoute.input, subRoute.output);
|
|
450
|
+
const v4SectionSlippage = (_d = sectionHopSlippage === null || sectionHopSlippage === void 0 ? void 0 : sectionHopSlippage.map((s) => BigNumber.from(s))) !== null && _d !== void 0 ? _d : [];
|
|
451
|
+
v4Planner.addSettle(inputToken, payerIsUser && i === 0, (i == 0 ? amountIn : CONTRACT_BALANCE));
|
|
452
|
+
v4Planner.addAction(Actions.SWAP_EXACT_IN, [
|
|
453
|
+
{
|
|
454
|
+
currencyIn: inputToken.isNative ? ETH_ADDRESS : inputToken.address,
|
|
455
|
+
path: encodeV4RouteToPath(v4SubRoute),
|
|
456
|
+
maxHopSlippage: v4SectionSlippage,
|
|
457
|
+
amountIn: 0,
|
|
458
|
+
amountOutMinimum: !isLastSectionInRoute(i) ? 0 : amountOut,
|
|
459
|
+
},
|
|
460
|
+
], options.urVersion);
|
|
461
|
+
// Handle split route output consistency for V4 sections in mixed routes
|
|
462
|
+
let outputTokenForTake = outputToken;
|
|
463
|
+
if (isLastSectionInRoute(i)) {
|
|
464
|
+
let lastPool = route.pools[route.pools.length - 1];
|
|
465
|
+
let v4Pool = lastPool instanceof V4Pool;
|
|
466
|
+
let ethWethPool = v4Pool ? lastPool.currency1.equals(lastPool.currency0.wrapped) : false;
|
|
467
|
+
let poolBefore = route.pools[route.pools.length - 2];
|
|
468
|
+
if (ethWethPool) {
|
|
469
|
+
if (outputToken.isNative && poolBefore.token0.isNative) {
|
|
470
|
+
outputTokenForTake = outputToken.wrapped;
|
|
471
|
+
}
|
|
472
|
+
else if (!outputToken.isNative &&
|
|
473
|
+
(poolBefore.token0.equals(lastPool.token1) || poolBefore.token1.equals(lastPool.token1))) {
|
|
474
|
+
outputTokenForTake = lastPool.token0;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
v4Planner.addTake(outputTokenForTake, swapRecipient);
|
|
479
|
+
planner.addCommand(CommandType.V4_SWAP, [v4Planner.finalize()]);
|
|
480
|
+
}
|
|
481
|
+
else if (routePool instanceof V3Pool) {
|
|
482
|
+
const v3Params = [
|
|
483
|
+
swapRecipient,
|
|
484
|
+
i == 0 ? amountIn : CONTRACT_BALANCE,
|
|
485
|
+
!isLastSectionInRoute(i) ? 0 : amountOut,
|
|
486
|
+
encodeMixedRouteToPath(subRoute),
|
|
487
|
+
payerIsUser && i === 0, // payerIsUser
|
|
488
|
+
];
|
|
489
|
+
if (useV2_1_1)
|
|
490
|
+
v3Params.push(sectionHopSlippage !== null && sectionHopSlippage !== void 0 ? sectionHopSlippage : []);
|
|
491
|
+
planner.addCommand(CommandType.V3_SWAP_EXACT_IN, v3Params, false, options.urVersion);
|
|
492
|
+
}
|
|
493
|
+
else if (routePool instanceof Pair) {
|
|
494
|
+
const v2Params = [
|
|
495
|
+
swapRecipient,
|
|
496
|
+
i === 0 ? amountIn : CONTRACT_BALANCE,
|
|
497
|
+
!isLastSectionInRoute(i) ? 0 : amountOut,
|
|
498
|
+
subRoute.path.map((token) => token.wrapped.address),
|
|
499
|
+
payerIsUser && i === 0,
|
|
500
|
+
];
|
|
501
|
+
if (useV2_1_1)
|
|
502
|
+
v2Params.push(sectionHopSlippage !== null && sectionHopSlippage !== void 0 ? sectionHopSlippage : []);
|
|
503
|
+
planner.addCommand(CommandType.V2_SWAP_EXACT_IN, v2Params, false, options.urVersion);
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
throw new Error('Unexpected Pool Type');
|
|
507
|
+
}
|
|
508
|
+
// perform a token transition (wrap/unwrap if necessary)
|
|
509
|
+
if (!isLastSectionInRoute(i)) {
|
|
510
|
+
if (outputToken.isNative && !nextInputToken.isNative) {
|
|
511
|
+
planner.addCommand(CommandType.WRAP_ETH, [ROUTER_AS_RECIPIENT, CONTRACT_BALANCE]);
|
|
512
|
+
}
|
|
513
|
+
else if (!outputToken.isNative && nextInputToken.isNative) {
|
|
514
|
+
planner.addCommand(CommandType.UNWRAP_WETH, [ROUTER_AS_RECIPIENT, 0]);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
hopOffset += section.length;
|
|
518
|
+
inputToken = nextInputToken;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// if price impact is very high, there's a chance of hitting max/min prices resulting in a partial fill of the swap
|
|
522
|
+
function riskOfPartialFill(trade) {
|
|
523
|
+
return trade.priceImpact.greaterThan(REFUND_ETH_PRICE_IMPACT_THRESHOLD);
|
|
524
|
+
}
|
|
525
|
+
function hasFeeOption(swapOptions) {
|
|
526
|
+
return !!swapOptions.fee || !!swapOptions.flatFee;
|
|
527
|
+
}
|
|
528
|
+
//# sourceMappingURL=uniswap.js.map
|