Skip to content

Commit

Permalink
Merge pull request #135 from BeanstalkFarms/stableswapWF-2
Browse files Browse the repository at this point in the history
Stable2 + WellUpgradeable
  • Loading branch information
nickkatsios authored Sep 6, 2024
2 parents 0c90c49 + c5507c5 commit 582a98e
Showing 39 changed files with 5,564 additions and 81 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -2,6 +2,12 @@
cache/
out/

# Python virtual environments
env/
venv/

.vscode

# Ignores development broadcast logs
/broadcast

3 changes: 1 addition & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ out = 'out'
libs = ['lib', 'node_modules']
fuzz = { runs = 256 }
optimizer = true
optimizer_runs = 800
optimizer_runs = 200
remappings = [
'@openzeppelin/=node_modules/@openzeppelin/',
]
@@ -27,7 +27,6 @@ ignore = ["src/libraries/LibClone.sol", "src/utils/Clone.sol", "src/libraries/AB
int_types = "long"
line_length = 120
multiline_func_header = "params_first"
number_underscore = "thousands"
override_spacing = false
quote_style = "double"
tab_width = 4
14 changes: 14 additions & 0 deletions mocks/wells/MockWellUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {WellUpgradeable} from "src/WellUpgradeable.sol";

// this needs to be here for upgrade checks
/// @custom:oz-upgrades-from WellUpgradeable
contract MockWellUpgradeable is WellUpgradeable {

function getVersion(uint256 i) external pure returns (uint256) {
return i;
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@beanstalk/wells",
"version": "1.1.0",
"version": "1.2.0",
"description": "A [{Well}](/src/Well.sol) is a constant function AMM that allows the provisioning of liquidity into a single pooled on-chain liquidity position.",
"main": "index.js",
"directories": {
19 changes: 16 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
eth-abi==3.0.1
pandas
numpy
cytoolz==0.12.3
eth-hash==0.7.0
eth-typing==3.5.2
eth-utils==2.3.1
eth_abi==5.1.0
numpy==2.0.1
pandas==2.2.2
parsimonious==0.10.0
python-dateutil==2.9.0.post0
pytz==2024.1
regex==2024.5.15
setuptools==71.1.0
six==1.16.0
toolz==0.12.1
typing_extensions==4.12.2
tzdata==2024.1
24 changes: 24 additions & 0 deletions script/helpers/WellDeployer.sol
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@ import {LibContractInfo} from "src/libraries/LibContractInfo.sol";
import {LibWellConstructor} from "src/libraries/LibWellConstructor.sol";
import {Well, Call, IERC20} from "src/Well.sol";
import {Aquifer} from "src/Aquifer.sol";
import {WellUpgradeable} from "src/WellUpgradeable.sol";
import {LibWellUpgradeableConstructor} from "src/libraries/LibWellUpgradeableConstructor.sol";

abstract contract WellDeployer {
/**
@@ -28,4 +30,26 @@ abstract contract WellDeployer {
LibWellConstructor.encodeWellDeploymentData(_aquifer, _tokens, _wellFunction, _pumps);
_well = Well(Aquifer(_aquifer).boreWell(_wellImplementation, immutableData, initData, _salt));
}

/**
* @notice Encode the Well's immutable data, and deploys the well. Modified for upgradeable wells.
* @param _aquifer The address of the Aquifer which will deploy this Well.
* @param _wellImplementation The address of the Well implementation.
* @param _tokens A list of ERC20 tokens supported by the Well.
* @param _wellFunction A single Call struct representing a call to the Well Function.
* @param _pumps An array of Call structs representings calls to Pumps.
* @param _salt The salt to deploy the Well with (`bytes32(0)` for none). See {LibClone}.
*/
function encodeAndBoreWellUpgradeable(
address _aquifer,
address _wellImplementation,
IERC20[] memory _tokens,
Call memory _wellFunction,
Call[] memory _pumps,
bytes32 _salt
) internal returns (WellUpgradeable _well) {
(bytes memory immutableData, bytes memory initData) =
LibWellUpgradeableConstructor.encodeWellDeploymentData(_aquifer, _tokens, _wellFunction, _pumps);
_well = WellUpgradeable(Aquifer(_aquifer).boreWell(_wellImplementation, immutableData, initData, _salt));
}
}
99 changes: 99 additions & 0 deletions script/simulations/stableswap/StableswapCalcRatiosLiqSim.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import "forge-std/Script.sol";
import {console} from "forge-std/console.sol";
import {Stable2} from "src/functions/Stable2.sol";
import {Stable2LUT1} from "src/functions/StableLUT/Stable2LUT1.sol";

/**
* Stable2 well function simulation and precalculations used
* to produce the token ratios for the lookup table needed for the initial
* `calcReserveAtRatioLiquidity` estimates.
*/
contract StableswapCalcRatiosLiqSim is Script {
function run() external {
Stable2LUT1 stable2LUT1 = new Stable2LUT1();
Stable2 stable2 = new Stable2(address(stable2LUT1));
console.log("stable2.getAParameter(): %d", stable2LUT1.getAParameter());
// initial reserves
uint256 init_reserve_x = 1_000_000e18;
uint256 init_reserve_y = 1_000_000e18;
uint256[] memory reserves = new uint256[](2);
reserves[0] = init_reserve_x;
reserves[1] = init_reserve_y;
uint256 reserve_y = init_reserve_y;
bytes memory data = abi.encode(18, 18);
uint256 price;

// for n times (1...n) :
// 1) modify reserve x_n-1 by some percentage (this changes the pool liquidity)
// 3) calc price_n using calcRate(...)

// csv header
console.log("Price (P),Reserve (x),Reserve (y)");

// calcReserveAtRatioLiquidity
for (uint256 i; i < 20; i++) {
// update reserves
reserve_y = reserve_y * 88 / 100;
reserves[1] = reserve_y;
// mark price
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, init_reserve_x, reserve_y);
}

// reset reserves
reserve_y = init_reserve_y;

// calcReserveAtRatioLiquidity
for (uint256 i; i < 20; i++) {
// update reserves
reserve_y = reserve_y * 98 / 100;
reserves[1] = reserve_y;
// mark price
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, init_reserve_x, reserve_y);
}

// reset reserves
reserve_y = init_reserve_y;

// calcReserveAtRatioLiquidity
for (uint256 i; i < 20; i++) {
// update reserves
reserve_y = reserve_y * 102 / 100;
reserves[1] = reserve_y;
// mark price
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, init_reserve_x, reserve_y);
}

// reset reserves
reserve_y = init_reserve_y;

// calcReserveAtRatioLiquidity
for (uint256 i; i < 20; i++) {
// update reserves
reserve_y = reserve_y * 112 / 100;
reserves[1] = reserve_y;
// mark price
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, init_reserve_x, reserve_y);
}

// Extreme prices

// extreme low
reserve_y = init_reserve_y * 1 / 28;
reserves[1] = reserve_y;
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, init_reserve_x, reserve_y);

// extreme high
reserve_y = init_reserve_y * 2000;
reserves[1] = reserve_y;
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, init_reserve_x, reserve_y);
}
}
119 changes: 119 additions & 0 deletions script/simulations/stableswap/StableswapCalcRatiosSwapSim.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import "forge-std/Script.sol";
import {console} from "forge-std/console.sol";
import {Stable2} from "src/functions/Stable2.sol";
import {Stable2LUT1} from "src/functions/StableLUT/Stable2LUT1.sol";

/**
* Stable2 well function simulation and precalculations used
* to produce the token ratios for the lookup table needed for the initial
* `calcReserveAtRatioSwap` estimates.
*/
contract StableswapCalcRatiosSwapSim is Script {
function run() external {
Stable2LUT1 stable2LUT1 = new Stable2LUT1();
Stable2 stable2 = new Stable2(address(stable2LUT1));
console.log("stable2.getAParameter(): %d", stable2LUT1.getAParameter());
// initial reserves
uint256 init_reserve_x = 1_000_000e18;
uint256 init_reserve_y = 1_000_000e18;
uint256[] memory reserves = new uint256[](2);
reserves[0] = init_reserve_x;
reserves[1] = init_reserve_y;
bytes memory data = abi.encode(18, 18);
// calculateLP token supply (this remains unchanged)
uint256 lpTokenSupply = stable2.calcLpTokenSupply(reserves, data);
console.log("lp_token_supply: %d", lpTokenSupply);
uint256 reserve_x = init_reserve_x;
uint256 price;

// for n times (1...n) :
// 1) increment x_n-1 by some amount to get x_n
// 2) calc y_n using calcReserves(...)
// 3) calc price_n using calcRate(...)

// csv header
console.log("Price (P),Reserve (x),Reserve (y)");

for (uint256 i; i < 20; i++) {
// update reserve x
reserve_x = reserve_x * 92 / 100;
reserves[0] = reserve_x;
// get y_n --> corresponding reserve y for a given liquidity level
uint256 reserve_y = stable2.calcReserve(reserves, 1, lpTokenSupply, data);
// update reserve y
reserves[1] = reserve_y;
// mark price
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, reserve_x, reserve_y);
}

// reset reserves
reserve_x = init_reserve_x;

for (uint256 i; i < 40; i++) {
// update reserve x
reserve_x = reserve_x * 99 / 100;
reserves[0] = reserve_x;
// get y_n --> corresponding reserve y for a given liquidity level
uint256 reserve_y = stable2.calcReserve(reserves, 1, lpTokenSupply, data);
// update reserve y
reserves[1] = reserve_y;
// mark price
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, reserve_x, reserve_y);
}

// reset reserves
reserve_x = init_reserve_x;

for (uint256 i; i < 40; i++) {
// update reserve x
reserve_x = reserve_x * 101 / 100;
reserves[0] = reserve_x;
// get y_n --> corresponding reserve y for a given liquidity level
uint256 reserve_y = stable2.calcReserve(reserves, 1, lpTokenSupply, data);
// update reserve y
reserves[1] = reserve_y;
// mark price
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, reserve_x, reserve_y);
}

// reset reserves
reserve_x = init_reserve_x;

for (uint256 i; i < 18; i++) {
// update reserve x
reserve_x = reserve_x * 105 / 100;
reserves[0] = reserve_x;
// get y_n --> corresponding reserve y for a given liquidity level
uint256 reserve_y = stable2.calcReserve(reserves, 1, lpTokenSupply, data);
// update reserve y
reserves[1] = reserve_y;
// mark price
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, reserve_x, reserve_y);
}

// Extreme prices

// extreme low
reserve_x = init_reserve_x * 3;
reserves[0] = reserve_x;
uint256 reserve_y = stable2.calcReserve(reserves, 1, lpTokenSupply, data);
reserves[1] = reserve_y;
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, reserve_x, reserve_y);

// extreme high
reserve_x = init_reserve_x * 1 / 190;
reserves[0] = reserve_x;
reserve_y = stable2.calcReserve(reserves, 1, lpTokenSupply, data);
reserves[1] = reserve_y;
price = stable2.calcRate(reserves, 0, 1, data);
console.log("%d,%d,%d", price, reserve_x, reserve_y);
}
}
Loading

0 comments on commit 582a98e

Please sign in to comment.