@kittycorn-labs/universal-router-sdk 1.4.43 → 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.
Files changed (244) hide show
  1. package/README.md +312 -0
  2. package/dist/cjs/src/abis/UniversalRouter.json +740 -0
  3. package/dist/{entities → cjs/src/entities}/Command.d.ts +12 -12
  4. package/dist/cjs/src/entities/Command.js +9 -0
  5. package/dist/cjs/src/entities/Command.js.map +1 -0
  6. package/dist/cjs/src/entities/actions/across.d.ts +21 -0
  7. package/dist/cjs/src/entities/actions/across.js +7 -0
  8. package/dist/cjs/src/entities/actions/across.js.map +1 -0
  9. package/dist/{entities → cjs/src/entities}/actions/index.d.ts +3 -2
  10. package/dist/cjs/src/entities/actions/index.js +7 -0
  11. package/dist/cjs/src/entities/actions/index.js.map +1 -0
  12. package/dist/cjs/src/entities/actions/uniswap.d.ts +94 -0
  13. package/dist/cjs/src/entities/actions/uniswap.js +740 -0
  14. package/dist/cjs/src/entities/actions/uniswap.js.map +1 -0
  15. package/dist/{entities → cjs/src/entities}/actions/unwrapWETH.d.ts +12 -12
  16. package/dist/cjs/src/entities/actions/unwrapWETH.js +33 -0
  17. package/dist/cjs/src/entities/actions/unwrapWETH.js.map +1 -0
  18. package/dist/{entities → cjs/src/entities}/index.d.ts +2 -2
  19. package/dist/cjs/src/entities/index.js +6 -0
  20. package/dist/cjs/src/entities/index.js.map +1 -0
  21. package/dist/cjs/src/index.d.ts +19 -0
  22. package/dist/cjs/src/index.js +43 -0
  23. package/dist/cjs/src/index.js.map +1 -0
  24. package/dist/cjs/src/kittycorn.d.ts +9 -0
  25. package/dist/cjs/src/kittycorn.js +26 -0
  26. package/dist/cjs/src/kittycorn.js.map +1 -0
  27. package/dist/cjs/src/swapRouter.d.ts +123 -0
  28. package/dist/cjs/src/swapRouter.js +445 -0
  29. package/dist/cjs/src/swapRouter.js.map +1 -0
  30. package/dist/cjs/src/types/encodeSwaps.d.ts +197 -0
  31. package/dist/cjs/src/types/encodeSwaps.js +3 -0
  32. package/dist/cjs/src/types/encodeSwaps.js.map +1 -0
  33. package/dist/{utils → cjs/src/utils}/commandParser.d.ts +36 -34
  34. package/dist/cjs/src/utils/commandParser.js +154 -0
  35. package/dist/cjs/src/utils/commandParser.js.map +1 -0
  36. package/dist/cjs/src/utils/computeEncodeSwapsAmounts.d.ts +8 -0
  37. package/dist/cjs/src/utils/computeEncodeSwapsAmounts.js +46 -0
  38. package/dist/cjs/src/utils/computeEncodeSwapsAmounts.js.map +1 -0
  39. package/dist/{utils → cjs/src/utils}/constants.d.ts +46 -30
  40. package/dist/cjs/src/utils/constants.js +777 -0
  41. package/dist/cjs/src/utils/constants.js.map +1 -0
  42. package/dist/cjs/src/utils/directTransfers.d.ts +15 -0
  43. package/dist/cjs/src/utils/directTransfers.js +381 -0
  44. package/dist/cjs/src/utils/directTransfers.js.map +1 -0
  45. package/dist/cjs/src/utils/eip712.d.ts +17 -0
  46. package/dist/cjs/src/utils/eip712.js +44 -0
  47. package/dist/cjs/src/utils/eip712.js.map +1 -0
  48. package/dist/cjs/src/utils/encodeSwapStep.d.ts +4 -0
  49. package/dist/cjs/src/utils/encodeSwapStep.js +62 -0
  50. package/dist/cjs/src/utils/encodeSwapStep.js.map +1 -0
  51. package/dist/cjs/src/utils/encodeV4Action.d.ts +7 -0
  52. package/dist/cjs/src/utils/encodeV4Action.js +84 -0
  53. package/dist/cjs/src/utils/encodeV4Action.js.map +1 -0
  54. package/dist/{utils → cjs/src/utils}/getCurrencyAddress.d.ts +2 -2
  55. package/dist/cjs/src/utils/getCurrencyAddress.js +9 -0
  56. package/dist/cjs/src/utils/getCurrencyAddress.js.map +1 -0
  57. package/dist/{utils → cjs/src/utils}/inputTokens.d.ts +23 -23
  58. package/dist/cjs/src/utils/inputTokens.js +58 -0
  59. package/dist/cjs/src/utils/inputTokens.js.map +1 -0
  60. package/dist/cjs/src/utils/normalizeEncodeSwapsSpec.d.ts +6 -0
  61. package/dist/cjs/src/utils/normalizeEncodeSwapsSpec.js +34 -0
  62. package/dist/cjs/src/utils/normalizeEncodeSwapsSpec.js.map +1 -0
  63. package/dist/{utils → cjs/src/utils}/numbers.d.ts +7 -6
  64. package/dist/cjs/src/utils/numbers.js +27 -0
  65. package/dist/cjs/src/utils/numbers.js.map +1 -0
  66. package/dist/{utils → cjs/src/utils}/pathCurrency.d.ts +3 -3
  67. package/dist/cjs/src/utils/pathCurrency.js +27 -0
  68. package/dist/cjs/src/utils/pathCurrency.js.map +1 -0
  69. package/dist/cjs/src/utils/portionFees.d.ts +14 -0
  70. package/dist/cjs/src/utils/portionFees.js +40 -0
  71. package/dist/cjs/src/utils/portionFees.js.map +1 -0
  72. package/dist/cjs/src/utils/routerBalanceSteps.d.ts +31 -0
  73. package/dist/cjs/src/utils/routerBalanceSteps.js +293 -0
  74. package/dist/cjs/src/utils/routerBalanceSteps.js.map +1 -0
  75. package/dist/{utils → cjs/src/utils}/routerCommands.d.ts +77 -64
  76. package/dist/cjs/src/utils/routerCommands.js +331 -0
  77. package/dist/cjs/src/utils/routerCommands.js.map +1 -0
  78. package/dist/{utils → cjs/src/utils}/routerTradeAdapter.d.ts +73 -73
  79. package/dist/cjs/src/utils/routerTradeAdapter.js +139 -0
  80. package/dist/cjs/src/utils/routerTradeAdapter.js.map +1 -0
  81. package/dist/cjs/src/utils/toV4URVersion.d.ts +3 -0
  82. package/dist/cjs/src/utils/toV4URVersion.js +17 -0
  83. package/dist/cjs/src/utils/toV4URVersion.js.map +1 -0
  84. package/dist/cjs/src/utils/validateEncodeSwaps.d.ts +2 -0
  85. package/dist/cjs/src/utils/validateEncodeSwaps.js +344 -0
  86. package/dist/cjs/src/utils/validateEncodeSwaps.js.map +1 -0
  87. package/dist/esm/package.json +1 -0
  88. package/dist/esm/src/abis/UniversalRouter.json +740 -0
  89. package/dist/esm/src/entities/Command.d.ts +12 -0
  90. package/dist/esm/src/entities/Command.js +6 -0
  91. package/dist/esm/src/entities/Command.js.map +1 -0
  92. package/dist/esm/src/entities/actions/across.d.ts +21 -0
  93. package/dist/esm/src/entities/actions/across.js +3 -0
  94. package/dist/esm/src/entities/actions/across.js.map +1 -0
  95. package/dist/esm/src/entities/actions/index.d.ts +3 -0
  96. package/dist/esm/src/entities/actions/index.js +4 -0
  97. package/dist/esm/src/entities/actions/index.js.map +1 -0
  98. package/dist/esm/src/entities/actions/uniswap.d.ts +94 -0
  99. package/dist/esm/src/entities/actions/uniswap.js +735 -0
  100. package/dist/esm/src/entities/actions/uniswap.js.map +1 -0
  101. package/dist/esm/src/entities/actions/unwrapWETH.d.ts +12 -0
  102. package/dist/esm/src/entities/actions/unwrapWETH.js +28 -0
  103. package/dist/esm/src/entities/actions/unwrapWETH.js.map +1 -0
  104. package/dist/esm/src/entities/index.d.ts +2 -0
  105. package/dist/esm/src/entities/index.js +3 -0
  106. package/dist/esm/src/entities/index.js.map +1 -0
  107. package/dist/esm/src/index.d.ts +19 -0
  108. package/dist/esm/src/index.js +15 -0
  109. package/dist/esm/src/index.js.map +1 -0
  110. package/dist/esm/src/kittycorn.d.ts +9 -0
  111. package/dist/esm/src/kittycorn.js +22 -0
  112. package/dist/esm/src/kittycorn.js.map +1 -0
  113. package/dist/esm/src/swapRouter.d.ts +123 -0
  114. package/dist/esm/src/swapRouter.js +440 -0
  115. package/dist/esm/src/swapRouter.js.map +1 -0
  116. package/dist/esm/src/types/encodeSwaps.d.ts +197 -0
  117. package/dist/esm/src/types/encodeSwaps.js +2 -0
  118. package/dist/esm/src/types/encodeSwaps.js.map +1 -0
  119. package/dist/esm/src/utils/commandParser.d.ts +36 -0
  120. package/dist/esm/src/utils/commandParser.js +146 -0
  121. package/dist/esm/src/utils/commandParser.js.map +1 -0
  122. package/dist/esm/src/utils/computeEncodeSwapsAmounts.d.ts +8 -0
  123. package/dist/esm/src/utils/computeEncodeSwapsAmounts.js +41 -0
  124. package/dist/esm/src/utils/computeEncodeSwapsAmounts.js.map +1 -0
  125. package/dist/esm/src/utils/constants.d.ts +46 -0
  126. package/dist/esm/src/utils/constants.js +768 -0
  127. package/dist/esm/src/utils/constants.js.map +1 -0
  128. package/dist/esm/src/utils/directTransfers.d.ts +15 -0
  129. package/dist/esm/src/utils/directTransfers.js +369 -0
  130. package/dist/esm/src/utils/directTransfers.js.map +1 -0
  131. package/dist/esm/src/utils/eip712.d.ts +17 -0
  132. package/dist/esm/src/utils/eip712.js +39 -0
  133. package/dist/esm/src/utils/eip712.js.map +1 -0
  134. package/dist/esm/src/utils/encodeSwapStep.d.ts +4 -0
  135. package/dist/esm/src/utils/encodeSwapStep.js +58 -0
  136. package/dist/esm/src/utils/encodeSwapStep.js.map +1 -0
  137. package/dist/esm/src/utils/encodeV4Action.d.ts +7 -0
  138. package/dist/esm/src/utils/encodeV4Action.js +80 -0
  139. package/dist/esm/src/utils/encodeV4Action.js.map +1 -0
  140. package/dist/esm/src/utils/getCurrencyAddress.d.ts +2 -0
  141. package/dist/esm/src/utils/getCurrencyAddress.js +5 -0
  142. package/dist/esm/src/utils/getCurrencyAddress.js.map +1 -0
  143. package/dist/esm/src/utils/inputTokens.d.ts +23 -0
  144. package/dist/esm/src/utils/inputTokens.js +51 -0
  145. package/dist/esm/src/utils/inputTokens.js.map +1 -0
  146. package/dist/esm/src/utils/normalizeEncodeSwapsSpec.d.ts +6 -0
  147. package/dist/esm/src/utils/normalizeEncodeSwapsSpec.js +27 -0
  148. package/dist/esm/src/utils/normalizeEncodeSwapsSpec.js.map +1 -0
  149. package/dist/esm/src/utils/numbers.d.ts +7 -0
  150. package/dist/esm/src/utils/numbers.js +19 -0
  151. package/dist/esm/src/utils/numbers.js.map +1 -0
  152. package/dist/esm/src/utils/pathCurrency.d.ts +3 -0
  153. package/dist/esm/src/utils/pathCurrency.js +23 -0
  154. package/dist/esm/src/utils/pathCurrency.js.map +1 -0
  155. package/dist/esm/src/utils/portionFees.d.ts +14 -0
  156. package/dist/esm/src/utils/portionFees.js +35 -0
  157. package/dist/esm/src/utils/portionFees.js.map +1 -0
  158. package/dist/esm/src/utils/routerBalanceSteps.d.ts +31 -0
  159. package/dist/esm/src/utils/routerBalanceSteps.js +282 -0
  160. package/dist/esm/src/utils/routerBalanceSteps.js.map +1 -0
  161. package/dist/esm/src/utils/routerCommands.d.ts +77 -0
  162. package/dist/esm/src/utils/routerCommands.js +326 -0
  163. package/dist/esm/src/utils/routerCommands.js.map +1 -0
  164. package/dist/esm/src/utils/routerTradeAdapter.d.ts +73 -0
  165. package/dist/esm/src/utils/routerTradeAdapter.js +134 -0
  166. package/dist/esm/src/utils/routerTradeAdapter.js.map +1 -0
  167. package/dist/esm/src/utils/toV4URVersion.d.ts +3 -0
  168. package/dist/esm/src/utils/toV4URVersion.js +13 -0
  169. package/dist/esm/src/utils/toV4URVersion.js.map +1 -0
  170. package/dist/esm/src/utils/validateEncodeSwaps.d.ts +2 -0
  171. package/dist/esm/src/utils/validateEncodeSwaps.js +339 -0
  172. package/dist/esm/src/utils/validateEncodeSwaps.js.map +1 -0
  173. package/dist/types/src/entities/Command.d.ts +13 -0
  174. package/dist/types/src/entities/Command.d.ts.map +1 -0
  175. package/dist/types/src/entities/actions/across.d.ts +22 -0
  176. package/dist/types/src/entities/actions/across.d.ts.map +1 -0
  177. package/dist/types/src/entities/actions/index.d.ts +4 -0
  178. package/dist/types/src/entities/actions/index.d.ts.map +1 -0
  179. package/dist/types/src/entities/actions/uniswap.d.ts +95 -0
  180. package/dist/types/src/entities/actions/uniswap.d.ts.map +1 -0
  181. package/dist/types/src/entities/actions/unwrapWETH.d.ts +13 -0
  182. package/dist/types/src/entities/actions/unwrapWETH.d.ts.map +1 -0
  183. package/dist/types/src/entities/index.d.ts +3 -0
  184. package/dist/types/src/entities/index.d.ts.map +1 -0
  185. package/dist/types/src/index.d.ts +20 -0
  186. package/dist/types/src/index.d.ts.map +1 -0
  187. package/dist/types/src/kittycorn.d.ts +10 -0
  188. package/dist/types/src/kittycorn.d.ts.map +1 -0
  189. package/dist/types/src/swapRouter.d.ts +124 -0
  190. package/dist/types/src/swapRouter.d.ts.map +1 -0
  191. package/dist/types/src/types/encodeSwaps.d.ts +198 -0
  192. package/dist/types/src/types/encodeSwaps.d.ts.map +1 -0
  193. package/dist/types/src/utils/commandParser.d.ts +37 -0
  194. package/dist/types/src/utils/commandParser.d.ts.map +1 -0
  195. package/dist/types/src/utils/computeEncodeSwapsAmounts.d.ts +9 -0
  196. package/dist/types/src/utils/computeEncodeSwapsAmounts.d.ts.map +1 -0
  197. package/dist/types/src/utils/constants.d.ts +47 -0
  198. package/dist/types/src/utils/constants.d.ts.map +1 -0
  199. package/dist/types/src/utils/directTransfers.d.ts +16 -0
  200. package/dist/types/src/utils/directTransfers.d.ts.map +1 -0
  201. package/dist/types/src/utils/eip712.d.ts +18 -0
  202. package/dist/types/src/utils/eip712.d.ts.map +1 -0
  203. package/dist/types/src/utils/encodeSwapStep.d.ts +5 -0
  204. package/dist/types/src/utils/encodeSwapStep.d.ts.map +1 -0
  205. package/dist/types/src/utils/encodeV4Action.d.ts +8 -0
  206. package/dist/types/src/utils/encodeV4Action.d.ts.map +1 -0
  207. package/dist/types/src/utils/getCurrencyAddress.d.ts +3 -0
  208. package/dist/types/src/utils/getCurrencyAddress.d.ts.map +1 -0
  209. package/dist/types/src/utils/inputTokens.d.ts +24 -0
  210. package/dist/types/src/utils/inputTokens.d.ts.map +1 -0
  211. package/dist/types/src/utils/normalizeEncodeSwapsSpec.d.ts +7 -0
  212. package/dist/types/src/utils/normalizeEncodeSwapsSpec.d.ts.map +1 -0
  213. package/dist/types/src/utils/numbers.d.ts +8 -0
  214. package/dist/types/src/utils/numbers.d.ts.map +1 -0
  215. package/dist/types/src/utils/pathCurrency.d.ts +4 -0
  216. package/dist/types/src/utils/pathCurrency.d.ts.map +1 -0
  217. package/dist/types/src/utils/portionFees.d.ts +15 -0
  218. package/dist/types/src/utils/portionFees.d.ts.map +1 -0
  219. package/dist/types/src/utils/routerBalanceSteps.d.ts +32 -0
  220. package/dist/types/src/utils/routerBalanceSteps.d.ts.map +1 -0
  221. package/dist/types/src/utils/routerCommands.d.ts +78 -0
  222. package/dist/types/src/utils/routerCommands.d.ts.map +1 -0
  223. package/dist/types/src/utils/routerTradeAdapter.d.ts +74 -0
  224. package/dist/types/src/utils/routerTradeAdapter.d.ts.map +1 -0
  225. package/dist/types/src/utils/toV4URVersion.d.ts +4 -0
  226. package/dist/types/src/utils/toV4URVersion.d.ts.map +1 -0
  227. package/dist/types/src/utils/validateEncodeSwaps.d.ts +3 -0
  228. package/dist/types/src/utils/validateEncodeSwaps.d.ts.map +1 -0
  229. package/package.json +33 -52
  230. package/dist/entities/actions/uniswap.d.ts +0 -30
  231. package/dist/index.d.ts +0 -10
  232. package/dist/index.js +0 -8
  233. package/dist/swapRouter.d.ts +0 -37
  234. package/dist/test/forge/writeInterop.d.ts +0 -2
  235. package/dist/test/utils/addresses.d.ts +0 -5
  236. package/dist/test/utils/hexToDecimalString.d.ts +0 -2
  237. package/dist/test/utils/permit2.d.ts +0 -7
  238. package/dist/test/utils/uniswapData.d.ts +0 -25
  239. package/dist/universal-router-sdk.cjs.development.js +0 -1500
  240. package/dist/universal-router-sdk.cjs.development.js.map +0 -1
  241. package/dist/universal-router-sdk.cjs.production.min.js +0 -2
  242. package/dist/universal-router-sdk.cjs.production.min.js.map +0 -1
  243. package/dist/universal-router-sdk.esm.js +0 -1493
  244. 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