@kittycorn-labs/universal-router-sdk 1.4.42 → 2.0.0

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