Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for weighted pools #70

Merged
merged 4 commits into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/foundry/contracts/pools/ConstantProductPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
contract ConstantProductPool is BalancerPoolToken, IBasePool {
using FixedPoint for uint256;

// Invariant growth limit: non-proportional add cannot cause the invariant to increase by more than this ratio.
uint256 private constant _MAX_INVARIANT_RATIO = 300e16; // 300%
// Invariant shrink limit: non-proportional remove cannot cause the invariant to decrease by less than this ratio.
uint256 private constant _MIN_INVARIANT_RATIO = 70e16; // 70%
uint256 private constant _MIN_SWAP_FEE_PERCENTAGE = 0.001e18; // 0.1%
uint256 private constant _MAX_INVARIANT_RATIO = 300e16; // 300%
uint256 private constant _MIN_SWAP_FEE_PERCENTAGE = 1e12; // 0.00001%
uint256 private constant _MAX_SWAP_FEE_PERCENTAGE = 0.10e18; // 10%

constructor(IVault vault, string memory name, string memory symbol) BalancerPoolToken(vault, name, symbol) {}
Expand Down Expand Up @@ -70,11 +68,13 @@ contract ConstantProductPool is BalancerPoolToken, IBasePool {
newBalance = ((newInvariant * newInvariant) / poolBalanceOtherToken);
}

// Invariant shrink limit: non-proportional remove cannot cause the invariant to decrease by less than this ratio.
/// @return minimumInvariantRatio The minimum invariant ratio for a pool during unbalanced remove liquidity
function getMinimumInvariantRatio() external pure returns (uint256) {
return _MIN_INVARIANT_RATIO;
}

// Invariant growth limit: non-proportional add cannot cause the invariant to increase by more than this ratio.
/// @return maximumInvariantRatio The maximum invariant ratio for a pool during unbalanced add liquidity
function getMaximumInvariantRatio() external pure returns (uint256) {
return _MAX_INVARIANT_RATIO;
Expand Down
14 changes: 7 additions & 7 deletions packages/foundry/contracts/pools/ConstantSumPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.24;

import { BalancerPoolToken } from "@balancer-labs/v3-vault/contracts/BalancerPoolToken.sol";
import { IBasePool, ISwapFeePercentageBounds } from "@balancer-labs/v3-interfaces/contracts/vault/IBasePool.sol";
import { IBasePool } from "@balancer-labs/v3-interfaces/contracts/vault/IBasePool.sol";
import { PoolSwapParams } from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";

Expand All @@ -12,12 +12,10 @@ import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol"
* https://docs-v3.balancer.fi/build-a-custom-amm/build-an-amm/create-custom-amm-with-novel-invariant.html
*/
contract ConstantSumPool is IBasePool, BalancerPoolToken {
// Invariant growth limit: non-proportional add cannot cause the invariant to increase by more than this ratio.
uint256 private constant _MAX_INVARIANT_RATIO = 300e16; // 300%
// Invariant shrink limit: non-proportional remove cannot cause the invariant to decrease by less than this ratio.
uint256 private constant _MIN_INVARIANT_RATIO = 70e16; // 70%
uint256 private constant _MAX_INVARIANT_RATIO = 300e16; // 300%
uint256 private constant _MIN_SWAP_FEE_PERCENTAGE = 1e12; // 0.00001%
uint256 private constant _MAX_SWAP_FEE_PERCENTAGE = 0.1e18; // 10%
uint256 private constant _MAX_SWAP_FEE_PERCENTAGE = 0.10e18; // 10%

constructor(IVault vault, string memory name, string memory symbol) BalancerPoolToken(vault, name, symbol) {}

Expand Down Expand Up @@ -57,22 +55,24 @@ contract ConstantSumPool is IBasePool, BalancerPoolToken {
newBalance = (balancesLiveScaled18[tokenInIndex] + invariant * (invariantRatio)) - invariant;
}

// Invariant shrink limit: non-proportional remove cannot cause the invariant to decrease by less than this ratio.
/// @return minimumInvariantRatio The minimum invariant ratio for a pool during unbalanced remove liquidity
function getMinimumInvariantRatio() external pure returns (uint256) {
return _MIN_INVARIANT_RATIO;
}

// Invariant growth limit: non-proportional add cannot cause the invariant to increase by more than this ratio.
/// @return maximumInvariantRatio The maximum invariant ratio for a pool during unbalanced add liquidity
function getMaximumInvariantRatio() external pure returns (uint256) {
return _MAX_INVARIANT_RATIO;
}

/// @inheritdoc ISwapFeePercentageBounds
/// @return minimumSwapFeePercentage The minimum swap fee percentage for a pool
function getMinimumSwapFeePercentage() external pure returns (uint256) {
return _MIN_SWAP_FEE_PERCENTAGE;
}

/// @inheritdoc ISwapFeePercentageBounds
/// @return maximumSwapFeePercentage The maximum swap fee percentage for a pool
function getMaximumSwapFeePercentage() external pure returns (uint256) {
return _MAX_SWAP_FEE_PERCENTAGE;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/foundry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"account": "node script/ListAccount.js",
"chain": "anvil --config-out localhost.json",
"compile": "forge compile",
"deploy": "forge build --build-info --build-info-path out/build-info/ && forge script script/Deploy.s.sol --rpc-url ${1:-default_network} --broadcast --slow && node scripts-js/generateTsAbis.js",
"deploy": "forge build --build-info --build-info-path out/build-info/ && forge script script/Deploy.s.sol --rpc-url ${1:-default_network} --broadcast && node scripts-js/generateTsAbis.js",
"flatten": "forge flatten",
"fork": "anvil --fork-url ${0:-sepolia} --chain-id 31337 --config-out localhost.json",
"format": "npx prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol' 'test/**/*.sol' 'script/*.sol' 'utils/*.sol'",
Expand Down
1 change: 1 addition & 0 deletions packages/foundry/remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@balancer-labs/v3-solidity-utils/=lib/balancer-v3-monorepo/pkg/solidity-utils/
@balancer-labs/v3-pool-utils/=lib/balancer-v3-monorepo/pkg/pool-utils/
@balancer-labs/v3-interfaces/=lib/balancer-v3-monorepo/pkg/interfaces/
@balancer-labs/v3-pool-weighted/=lib/balancer-v3-monorepo/pkg/pool-weighted/
@balancer-labs/v3-vault/=lib/balancer-v3-monorepo/pkg/vault/
permit2/=lib/permit2/
forge-gas-snapshot/=node_modules/forge-gas-snapshot/src/
Expand Down
47 changes: 22 additions & 25 deletions packages/foundry/script/01_DeployConstantSum.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { IRateProvider } from "@balancer-labs/v3-interfaces/contracts/vault/IRat
import { InputHelpers } from "@balancer-labs/v3-solidity-utils/contracts/helpers/InputHelpers.sol";
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";

import { PoolHelpers, PoolRegistrationConfig, PoolInitializationConfig } from "./PoolHelpers.sol";
import { PoolHelpers, CustomPoolConfig, InitializationConfig } from "./PoolHelpers.sol";
import { ScaffoldHelpers, console } from "./ScaffoldHelpers.sol";
import { ConstantSumFactory } from "../contracts/pools/ConstantSumFactory.sol";

Expand All @@ -23,8 +23,8 @@ contract DeployConstantSum is PoolHelpers, ScaffoldHelpers {
function run(address token1, address token2) external {
// Set the deployment configurations
uint32 pauseWindowDuration = 365 days;
PoolRegistrationConfig memory regConfig = getPoolRegistrationConfig(token1, token2);
PoolInitializationConfig memory initConfig = getPoolInitializationConfig(token1, token2);
CustomPoolConfig memory poolConfig = getPoolConfig(token1, token2);
InitializationConfig memory initConfig = getInitializationConfig(token1, token2);

// Start creating the transactions
uint256 deployerPrivateKey = getDeployerPrivateKey();
Expand All @@ -36,15 +36,15 @@ contract DeployConstantSum is PoolHelpers, ScaffoldHelpers {

// Deploy a pool and register it with the vault
address pool = factory.create(
regConfig.name,
regConfig.symbol,
regConfig.salt,
regConfig.tokenConfig,
regConfig.swapFeePercentage,
regConfig.protocolFeeExempt,
regConfig.roleAccounts,
regConfig.poolHooksContract,
regConfig.liquidityManagement
poolConfig.name,
poolConfig.symbol,
poolConfig.salt,
poolConfig.tokenConfigs,
poolConfig.swapFeePercentage,
poolConfig.protocolFeeExempt,
poolConfig.roleAccounts,
poolConfig.poolHooksContract,
poolConfig.liquidityManagement
);
console.log("Constant Sum Pool deployed at: %s", pool);

Expand Down Expand Up @@ -73,25 +73,22 @@ contract DeployConstantSum is PoolHelpers, ScaffoldHelpers {
* For STANDARD tokens, the rate provider address must be 0, and paysYieldFees must be false.
* All WITH_RATE tokens need a rate provider, and may or may not be yield-bearing.
*/
function getPoolRegistrationConfig(
address token1,
address token2
) internal view returns (PoolRegistrationConfig memory config) {
function getPoolConfig(address token1, address token2) internal view returns (CustomPoolConfig memory config) {
string memory name = "Constant Sum Pool"; // name for the pool
string memory symbol = "CSP"; // symbol for the BPT
bytes32 salt = keccak256(abi.encode(block.number)); // salt for the pool deployment via factory
uint256 swapFeePercentage = 0.001e18; // 0.1%
uint256 swapFeePercentage = 0.01e18; // 1%
bool protocolFeeExempt = true;
address poolHooksContract = address(0); // zero address if no hooks contract is needed

TokenConfig[] memory tokenConfig = new TokenConfig[](2); // An array of descriptors for the tokens the pool will manage.
tokenConfig[0] = TokenConfig({ // Make sure to have proper token order (alphanumeric)
TokenConfig[] memory tokenConfigs = new TokenConfig[](2); // An array of descriptors for the tokens the pool will manage.
tokenConfigs[0] = TokenConfig({ // Make sure to have proper token order (alphanumeric)
token: IERC20(token1),
tokenType: TokenType.STANDARD, // STANDARD or WITH_RATE
rateProvider: IRateProvider(address(0)), // The rate provider for a token (see further documentation above)
paysYieldFees: false // Flag indicating whether yield fees should be charged on this token
});
tokenConfig[1] = TokenConfig({ // Make sure to have proper token order (alphanumeric)
tokenConfigs[1] = TokenConfig({ // Make sure to have proper token order (alphanumeric)
token: IERC20(token2),
tokenType: TokenType.STANDARD, // STANDARD or WITH_RATE
rateProvider: IRateProvider(address(0)), // The rate provider for a token (see further documentation above)
Expand All @@ -110,11 +107,11 @@ contract DeployConstantSum is PoolHelpers, ScaffoldHelpers {
enableDonation: false
});

config = PoolRegistrationConfig({
config = CustomPoolConfig({
name: name,
symbol: symbol,
salt: salt,
tokenConfig: sortTokenConfig(tokenConfig),
tokenConfigs: sortTokenConfig(tokenConfigs),
swapFeePercentage: swapFeePercentage,
protocolFeeExempt: protocolFeeExempt,
roleAccounts: roleAccounts,
Expand All @@ -127,10 +124,10 @@ contract DeployConstantSum is PoolHelpers, ScaffoldHelpers {
* @dev Set the pool initialization configurations here
* @notice this is where the amounts of tokens to be initially added to the pool are set
*/
function getPoolInitializationConfig(
function getInitializationConfig(
address token1,
address token2
) internal pure returns (PoolInitializationConfig memory config) {
) internal pure returns (InitializationConfig memory config) {
IERC20[] memory tokens = new IERC20[](2); // Array of tokens to be used in the pool
tokens[0] = IERC20(token1);
tokens[1] = IERC20(token2);
Expand All @@ -141,7 +138,7 @@ contract DeployConstantSum is PoolHelpers, ScaffoldHelpers {
bool wethIsEth = false; // If true, incoming ETH will be wrapped to WETH; otherwise the Vault will pull WETH tokens
bytes memory userData = bytes(""); // Additional (optional) data required for adding initial liquidity

config = PoolInitializationConfig({
config = InitializationConfig({
tokens: InputHelpers.sortTokens(tokens),
exactAmountsIn: exactAmountsIn,
minBptAmountOut: minBptAmountOut,
Expand Down
45 changes: 21 additions & 24 deletions packages/foundry/script/02_DeployConstantProduct.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { IRateProvider } from "@balancer-labs/v3-interfaces/contracts/vault/IRat
import { InputHelpers } from "@balancer-labs/v3-solidity-utils/contracts/helpers/InputHelpers.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";

import { PoolHelpers, PoolRegistrationConfig, PoolInitializationConfig } from "./PoolHelpers.sol";
import { PoolHelpers, CustomPoolConfig, InitializationConfig } from "./PoolHelpers.sol";
import { ScaffoldHelpers, console } from "./ScaffoldHelpers.sol";
import { VeBALFeeDiscountHook } from "../contracts/hooks/VeBALFeeDiscountHook.sol";
import { ConstantProductFactory } from "../contracts/pools/ConstantProductFactory.sol";
Expand All @@ -25,8 +25,8 @@ contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {
function run(address token1, address token2, address veBAL) external {
// Set the deployment configurations
uint32 pauseWindowDuration = 365 days;
PoolRegistrationConfig memory regConfig = getPoolRegistrationConfig(token1, token2);
PoolInitializationConfig memory initConfig = getPoolInitializationConfig(token1, token2);
CustomPoolConfig memory poolConfig = getPoolConfig(token1, token2);
InitializationConfig memory initConfig = getInitializationConfig(token1, token2);

// Start creating the transactions
uint256 deployerPrivateKey = getDeployerPrivateKey();
Expand All @@ -47,15 +47,15 @@ contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {

// Deploy a pool and register it with the vault
address pool = factory.create(
regConfig.name,
regConfig.symbol,
regConfig.salt,
regConfig.tokenConfig,
regConfig.swapFeePercentage,
regConfig.protocolFeeExempt,
regConfig.roleAccounts,
poolConfig.name,
poolConfig.symbol,
poolConfig.salt,
poolConfig.tokenConfigs,
poolConfig.swapFeePercentage,
poolConfig.protocolFeeExempt,
poolConfig.roleAccounts,
address(poolHooksContract),
regConfig.liquidityManagement
poolConfig.liquidityManagement
);
console.log("Constant Product Pool deployed at: %s", pool);

Expand Down Expand Up @@ -84,25 +84,22 @@ contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {
* For STANDARD tokens, the rate provider address must be 0, and paysYieldFees must be false.
* All WITH_RATE tokens need a rate provider, and may or may not be yield-bearing.
*/
function getPoolRegistrationConfig(
address token1,
address token2
) internal view returns (PoolRegistrationConfig memory config) {
function getPoolConfig(address token1, address token2) internal view returns (CustomPoolConfig memory config) {
string memory name = "Constant Product Pool"; // name for the pool
string memory symbol = "CPP"; // symbol for the BPT
bytes32 salt = keccak256(abi.encode(block.number)); // salt for the pool deployment via factory
uint256 swapFeePercentage = 0.05e18; // 5%
uint256 swapFeePercentage = 0.02e18; // 2%
bool protocolFeeExempt = false;
address poolHooksContract = address(0); // zero address if no hooks contract is needed

TokenConfig[] memory tokenConfig = new TokenConfig[](2); // An array of descriptors for the tokens the pool will manage
tokenConfig[0] = TokenConfig({ // Make sure to have proper token order (alphanumeric)
TokenConfig[] memory tokenConfigs = new TokenConfig[](2); // An array of descriptors for the tokens the pool will manage
tokenConfigs[0] = TokenConfig({ // Make sure to have proper token order (alphanumeric)
token: IERC20(token1),
tokenType: TokenType.STANDARD, // STANDARD or WITH_RATE
rateProvider: IRateProvider(address(0)), // The rate provider for a token (see further documentation above)
paysYieldFees: false // Flag indicating whether yield fees should be charged on this token
});
tokenConfig[1] = TokenConfig({ // Make sure to have proper token order (alphanumeric)
tokenConfigs[1] = TokenConfig({ // Make sure to have proper token order (alphanumeric)
token: IERC20(token2),
tokenType: TokenType.STANDARD, // STANDARD or WITH_RATE
rateProvider: IRateProvider(address(0)), // The rate provider for a token (see further documentation above)
Expand All @@ -121,11 +118,11 @@ contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {
enableDonation: false
});

config = PoolRegistrationConfig({
config = CustomPoolConfig({
name: name,
symbol: symbol,
salt: salt,
tokenConfig: sortTokenConfig(tokenConfig),
tokenConfigs: sortTokenConfig(tokenConfigs),
swapFeePercentage: swapFeePercentage,
protocolFeeExempt: protocolFeeExempt,
roleAccounts: roleAccounts,
Expand All @@ -138,10 +135,10 @@ contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {
* @dev Set the pool initialization configurations here
* @notice This is where the amounts of tokens to seed the pool with initial liquidity are set
*/
function getPoolInitializationConfig(
function getInitializationConfig(
address token1,
address token2
) internal pure returns (PoolInitializationConfig memory config) {
) internal pure returns (InitializationConfig memory config) {
IERC20[] memory tokens = new IERC20[](2); // Array of tokens to be used in the pool
tokens[0] = IERC20(token1);
tokens[1] = IERC20(token2);
Expand All @@ -152,7 +149,7 @@ contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {
bool wethIsEth = false; // If true, incoming ETH will be wrapped to WETH; otherwise the Vault will pull WETH tokens
bytes memory userData = bytes(""); // Additional (optional) data required for adding initial liquidity

config = PoolInitializationConfig({
config = InitializationConfig({
tokens: InputHelpers.sortTokens(tokens),
exactAmountsIn: exactAmountsIn,
minBptAmountOut: minBptAmountOut,
Expand Down
Loading
Loading