@bananapus/router-terminal-v6 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/SKILLS.md +101 -0
- package/foundry.toml +22 -0
- package/package.json +30 -0
- package/script/Deploy.s.sol +130 -0
- package/script/helpers/RouterTerminalDeploymentLib.sol +76 -0
- package/slither-ci.config.json +10 -0
- package/src/JBRouterTerminal.sol +1355 -0
- package/src/JBRouterTerminalRegistry.sol +466 -0
- package/src/interfaces/IJBRouterTerminal.sol +45 -0
- package/src/interfaces/IJBRouterTerminalRegistry.sol +62 -0
- package/src/interfaces/IWETH9.sol +13 -0
- package/src/libraries/JBSwapLib.sol +159 -0
- package/src/structs/PoolInfo.sol +14 -0
- package/test/RouterTerminal.t.sol +865 -0
- package/test/RouterTerminalRegistry.t.sol +333 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
import {TickMath} from "@uniswap/v3-core/contracts/libraries/TickMath.sol";
|
|
5
|
+
import {mulDiv} from "@prb/math/src/Common.sol";
|
|
6
|
+
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
|
|
7
|
+
|
|
8
|
+
/// @custom:benediction DEVS BENEDICAT ET PROTEGAT CONTRACTVS MEAM
|
|
9
|
+
/// @notice Shared library for slippage tolerance and price limit calculations.
|
|
10
|
+
/// @dev V3-compatible port of the V4 JBSwapLib. Uses continuous sigmoid formula instead of
|
|
11
|
+
/// stepped if/else brackets for smoother slippage tolerance across all swap sizes.
|
|
12
|
+
library JBSwapLib {
|
|
13
|
+
/// @notice The denominator used for slippage tolerance basis points.
|
|
14
|
+
uint256 internal constant SLIPPAGE_DENOMINATOR = 10_000;
|
|
15
|
+
|
|
16
|
+
/// @notice The maximum slippage ceiling (88%).
|
|
17
|
+
uint256 internal constant MAX_SLIPPAGE = 8800;
|
|
18
|
+
|
|
19
|
+
/// @notice The precision multiplier for impact calculations.
|
|
20
|
+
/// @dev Using 1e18 instead of 1e5 (10 * SLIPPAGE_DENOMINATOR) gives 13 extra orders of magnitude,
|
|
21
|
+
/// preventing small-swap-in-deep-pool impacts from rounding to zero.
|
|
22
|
+
uint256 internal constant IMPACT_PRECISION = 1e18;
|
|
23
|
+
|
|
24
|
+
/// @notice The K parameter for the sigmoid curve, scaled to match IMPACT_PRECISION.
|
|
25
|
+
/// @dev Preserves the same sigmoid shape as the original K=5000 with amplifier=1e5:
|
|
26
|
+
/// K_new / IMPACT_PRECISION = K_old / (10 * SLIPPAGE_DENOMINATOR)
|
|
27
|
+
/// → K_new = 5000 * 1e18 / 1e5 = 5e16
|
|
28
|
+
uint256 internal constant SIGMOID_K = 5e16;
|
|
29
|
+
|
|
30
|
+
//*********************************************************************//
|
|
31
|
+
// -------------------- Slippage Tolerance -------------------------- //
|
|
32
|
+
//*********************************************************************//
|
|
33
|
+
|
|
34
|
+
/// @notice Compute a continuous sigmoid slippage tolerance based on swap impact and pool fee.
|
|
35
|
+
/// @dev tolerance = minSlippage + (maxSlippage - minSlippage) * impact / (impact + K)
|
|
36
|
+
/// When impact is 0 (negligible swap in deep pool), returns minSlippage.
|
|
37
|
+
/// @param impact The estimated price impact from calculateImpact (scaled by IMPACT_PRECISION).
|
|
38
|
+
/// @param poolFeeBps The pool fee in basis points (e.g., 30 for 0.3%).
|
|
39
|
+
/// @return tolerance The slippage tolerance in basis points of SLIPPAGE_DENOMINATOR.
|
|
40
|
+
function getSlippageTolerance(uint256 impact, uint256 poolFeeBps) internal pure returns (uint256) {
|
|
41
|
+
// If pool fee alone meets/exceeds the ceiling, return the ceiling.
|
|
42
|
+
if (poolFeeBps >= MAX_SLIPPAGE) return MAX_SLIPPAGE;
|
|
43
|
+
|
|
44
|
+
// Minimum slippage: at least pool fee + 1% buffer, with a floor of 2%.
|
|
45
|
+
uint256 minSlippage = poolFeeBps + 100;
|
|
46
|
+
if (minSlippage < 200) minSlippage = 200;
|
|
47
|
+
if (minSlippage >= MAX_SLIPPAGE) return MAX_SLIPPAGE;
|
|
48
|
+
|
|
49
|
+
// When impact is 0, sigmoid returns minSlippage directly.
|
|
50
|
+
if (impact == 0) return minSlippage;
|
|
51
|
+
|
|
52
|
+
// For extreme impact values, cap to prevent overflow in (impact + K).
|
|
53
|
+
if (impact > type(uint256).max - SIGMOID_K) return MAX_SLIPPAGE;
|
|
54
|
+
|
|
55
|
+
// Sigmoid: minSlippage + (maxSlippage - minSlippage) * impact / (impact + K)
|
|
56
|
+
uint256 range = MAX_SLIPPAGE - minSlippage;
|
|
57
|
+
uint256 tolerance = minSlippage + mulDiv(range, impact, impact + SIGMOID_K);
|
|
58
|
+
|
|
59
|
+
return tolerance;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//*********************************************************************//
|
|
63
|
+
// -------------------- Impact Calculation -------------------------- //
|
|
64
|
+
//*********************************************************************//
|
|
65
|
+
|
|
66
|
+
/// @notice Estimate the price impact of a swap, scaled by IMPACT_PRECISION.
|
|
67
|
+
/// @dev Uses 1e18 precision to capture sub-basis-point impacts for small swaps in deep pools.
|
|
68
|
+
/// @param amountIn The amount of tokens being swapped in.
|
|
69
|
+
/// @param liquidity The pool's in-range liquidity.
|
|
70
|
+
/// @param sqrtP The sqrt price in Q96 format.
|
|
71
|
+
/// @param zeroForOne Whether the swap is token0 → token1.
|
|
72
|
+
/// @return impact The estimated price impact scaled by IMPACT_PRECISION.
|
|
73
|
+
function calculateImpact(
|
|
74
|
+
uint256 amountIn,
|
|
75
|
+
uint128 liquidity,
|
|
76
|
+
uint160 sqrtP,
|
|
77
|
+
bool zeroForOne
|
|
78
|
+
)
|
|
79
|
+
internal
|
|
80
|
+
pure
|
|
81
|
+
returns (uint256 impact)
|
|
82
|
+
{
|
|
83
|
+
if (liquidity == 0 || sqrtP == 0) return 0;
|
|
84
|
+
|
|
85
|
+
uint256 base = mulDiv(amountIn, IMPACT_PRECISION, uint256(liquidity));
|
|
86
|
+
|
|
87
|
+
impact = zeroForOne
|
|
88
|
+
? mulDiv(base, uint256(sqrtP), uint256(1) << 96)
|
|
89
|
+
: mulDiv(base, uint256(1) << 96, uint256(sqrtP));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//*********************************************************************//
|
|
93
|
+
// -------------------- Price Limit -------------------------------- //
|
|
94
|
+
//*********************************************************************//
|
|
95
|
+
|
|
96
|
+
/// @notice Compute a sqrtPriceLimitX96 from input/output amounts so the swap stops
|
|
97
|
+
/// if the execution price would be worse than the minimum acceptable rate.
|
|
98
|
+
/// @dev When `minimumAmountOut == 0`, returns extreme values (no limit).
|
|
99
|
+
/// @param amountIn The amount of tokens being swapped in.
|
|
100
|
+
/// @param minimumAmountOut The minimum acceptable output.
|
|
101
|
+
/// @param zeroForOne True when selling token0 for token1 (price decreases).
|
|
102
|
+
/// @return sqrtPriceLimit The V3-compatible sqrtPriceLimitX96.
|
|
103
|
+
function sqrtPriceLimitFromAmounts(
|
|
104
|
+
uint256 amountIn,
|
|
105
|
+
uint256 minimumAmountOut,
|
|
106
|
+
bool zeroForOne
|
|
107
|
+
)
|
|
108
|
+
internal
|
|
109
|
+
pure
|
|
110
|
+
returns (uint160 sqrtPriceLimit)
|
|
111
|
+
{
|
|
112
|
+
if (minimumAmountOut == 0 || amountIn == 0) {
|
|
113
|
+
return zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
uint256 num;
|
|
117
|
+
uint256 den;
|
|
118
|
+
if (zeroForOne) {
|
|
119
|
+
num = minimumAmountOut;
|
|
120
|
+
den = amountIn;
|
|
121
|
+
} else {
|
|
122
|
+
num = amountIn;
|
|
123
|
+
den = minimumAmountOut;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
uint256 sqrtResult;
|
|
127
|
+
|
|
128
|
+
if (num / den >= (uint256(1) << 128)) {
|
|
129
|
+
// Ratio too large for any valid sqrtPriceX96 — fall back to no limit.
|
|
130
|
+
return zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1;
|
|
131
|
+
} else if (num / den >= (uint256(1) << 64)) {
|
|
132
|
+
// Extended range: use ratioX128 to avoid mulDiv overflow, then shift.
|
|
133
|
+
uint256 ratioX128 = mulDiv(num, uint256(1) << 128, den);
|
|
134
|
+
sqrtResult = Math.sqrt(ratioX128) * (uint256(1) << 32);
|
|
135
|
+
} else {
|
|
136
|
+
// Normal range: full precision via ratioX192.
|
|
137
|
+
uint256 ratioX192 = mulDiv(num, uint256(1) << 192, den);
|
|
138
|
+
sqrtResult = Math.sqrt(ratioX192);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (zeroForOne) {
|
|
142
|
+
if (sqrtResult <= uint256(TickMath.MIN_SQRT_RATIO)) {
|
|
143
|
+
return TickMath.MIN_SQRT_RATIO + 1;
|
|
144
|
+
}
|
|
145
|
+
if (sqrtResult >= uint256(TickMath.MAX_SQRT_RATIO)) {
|
|
146
|
+
return TickMath.MAX_SQRT_RATIO - 1;
|
|
147
|
+
}
|
|
148
|
+
return uint160(sqrtResult);
|
|
149
|
+
} else {
|
|
150
|
+
if (sqrtResult >= uint256(TickMath.MAX_SQRT_RATIO)) {
|
|
151
|
+
return TickMath.MAX_SQRT_RATIO - 1;
|
|
152
|
+
}
|
|
153
|
+
if (sqrtResult <= uint256(TickMath.MIN_SQRT_RATIO)) {
|
|
154
|
+
return TickMath.MIN_SQRT_RATIO + 1;
|
|
155
|
+
}
|
|
156
|
+
return uint160(sqrtResult);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
|
|
5
|
+
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
6
|
+
|
|
7
|
+
/// @custom:member isV4 Whether this pool is on Uniswap V4 (true) or V3 (false).
|
|
8
|
+
/// @custom:member v3Pool The V3 pool reference. Valid when `isV4` is false.
|
|
9
|
+
/// @custom:member v4Key The V4 pool key. Valid when `isV4` is true.
|
|
10
|
+
struct PoolInfo {
|
|
11
|
+
bool isV4;
|
|
12
|
+
IUniswapV3Pool v3Pool;
|
|
13
|
+
PoolKey v4Key;
|
|
14
|
+
}
|