-
Notifications
You must be signed in to change notification settings - Fork 9
/
ConstantProduct2.sol
101 lines (91 loc) · 3.31 KB
/
ConstantProduct2.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import {IBeanstalkWellFunction} from "src/interfaces/IBeanstalkWellFunction.sol";
import {ProportionalLPToken2} from "src/functions/ProportionalLPToken2.sol";
import {LibMath} from "src/libraries/LibMath.sol";
/**
* @title ConstantProduct2
* @author Publius
* @notice Gas efficient Constant Product pricing function for Wells with 2 tokens.
* @dev Constant Product Wells with 2 tokens use the formula:
* `b_0 * b_1 = s^2`
*
* Where:
* `s` is the supply of LP tokens
* `b_i` is the reserve at index `i`
*/
contract ConstantProduct2 is ProportionalLPToken2, IBeanstalkWellFunction {
using LibMath for uint256;
uint256 constant EXP_PRECISION = 1e12;
/**
* @dev `s = (b_0 * b_1)^(1/2)`
*
* When does this function overflow?
* ---------------------------------
*
* Let N be the length of the reserves array, and P be the precision multiplier
* defined in `EXP_PRECISION`.
*
* Assuming all tokens in reserves are at their maximum value simultaneously,
* this function will overflow when:
*
* (10^X)^N * P >= MAX_UINT256 (~10^77)
* 10^(X*N) >= 10^77/P
* (X*N)*ln(10) >= 77*ln(10) - ln(P)
*
* ∴ X >= (1/N) * (77 - ln(P)/ln(10))
*
* ConstantProduct2 sets the constraints `N = 2` and `EXP_PRECISION = 1e12`,
* resulting in an upper bound of X = 32.5.
*
* In other words, {calcLpTokenSupply} overflows if all reserves are simultaneously
* >= 10^32.5, or about 100 trillion if tokens are measured to 18 decimal precision.
*/
function calcLpTokenSupply(
uint256[] calldata reserves,
bytes calldata
) external pure override returns (uint256 lpTokenSupply) {
lpTokenSupply = (reserves[0] * reserves[1] * EXP_PRECISION).sqrt();
}
/// @dev `b_j = s^2 / b_{i | i != j}`
/// @dev rounds up
function calcReserve(
uint256[] calldata reserves,
uint256 j,
uint256 lpTokenSupply,
bytes calldata
) external pure override returns (uint256 reserve) {
// Note: potential optimization is to use unchecked math here
reserve = lpTokenSupply ** 2;
reserve = LibMath.roundUpDiv(reserve, reserves[j == 1 ? 0 : 1] * EXP_PRECISION);
}
function name() external pure override returns (string memory) {
return "Constant Product 2";
}
function symbol() external pure override returns (string memory) {
return "CP2";
}
/// @dev `b_j = (b_0 * b_1 * r_j / r_i)^(1/2)`
/// Note: Always rounds down
function calcReserveAtRatioSwap(
uint256[] calldata reserves,
uint256 j,
uint256[] calldata ratios,
bytes calldata
) external pure override returns (uint256 reserve) {
uint256 i = j == 1 ? 0 : 1;
// use 512 muldiv for last mul to avoid overflow
reserve = (reserves[i] * reserves[j]).mulDiv(ratios[j], ratios[i]).sqrt();
}
/// @dev `b_j = b_i * r_j / r_i`
/// Note: Always rounds down
function calcReserveAtRatioLiquidity(
uint256[] calldata reserves,
uint256 j,
uint256[] calldata ratios,
bytes calldata
) external pure override returns (uint256 reserve) {
uint256 i = j == 1 ? 0 : 1;
reserve = reserves[i] * ratios[j] / ratios[i];
}
}