Skip to content

Commit

Permalink
Update deploy scripts to be more consistent and readable
Browse files Browse the repository at this point in the history
  • Loading branch information
MattPereira committed Jul 25, 2024
1 parent d1db3c5 commit 0bca6e3
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 97 deletions.
54 changes: 35 additions & 19 deletions packages/foundry/contracts/hooks/VeBALFeeDiscountHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
TokenConfig,
LiquidityManagement
} from "@balancer-labs/v3-vault/contracts/BaseHooks.sol";
import { IBasePoolFactory } from "@balancer-labs/v3-interfaces/contracts/vault/IBasePoolFactory.sol";
import { IBasePool } from "@balancer-labs/v3-interfaces/contracts/vault/IBasePool.sol";
import { IBasePoolFactory } from "@balancer-labs/v3-interfaces/contracts/vault/IBasePoolFactory.sol";
import { IRouterCommon } from "@balancer-labs/v3-interfaces/contracts/vault/IRouterCommon.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

Expand All @@ -19,54 +19,70 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
* @notice Applies a 50% discount to the swap fee for users holding veBAL tokens
*/
contract VeBALFeeDiscountHook is BaseHooks {
// only pools from the allowedFactory are able to register and use this hook
address private immutable _allowedFactory;
// only calls from a trusted routers are allowed to call this hook, because the hook relies on the getSender
// implementation to work properly
address private immutable _trustedRouter;
IERC20 private immutable _veBAL;

constructor(IVault vault, address allowedFactory, address veBAL, address trustedRouter) BaseHooks(vault) {
constructor(IVault vault, address allowedFactory, address trustedRouter, IERC20 veBAL) BaseHooks(vault) {
_allowedFactory = allowedFactory;
_trustedRouter = trustedRouter;
_veBAL = IERC20(veBAL);
}

/// @inheritdoc IHooks
function getHookFlags() external pure override returns (IHooks.HookFlags memory hookFlags) {
hookFlags.shouldCallComputeDynamicSwapFee = true;
_veBAL = veBAL;
}

/// @inheritdoc IHooks
/**
* @notice Hook executed when pool is registered
* @dev Return true if registration was successful
* @dev Return false to revert the registration of the pool
* @dev Vault address can be accessed with msg.sender
* @param factory Address of the pool factory
* @param pool Address of the pool
* @return success True if the hook allowed the registration, false otherwise
*/
function onRegister(
address factory,
address pool,
TokenConfig[] memory,
LiquidityManagement calldata
) external view override returns (bool) {
// This hook implements a restrictive approach, where we check if the factory is an allowed factory and if
// the pool was created by the allowed factory. Since we only use onComputeDynamicSwapFee, this might be an
// overkill in real applications because the pool math doesn't play a role in the discount calculation.
// Only pools deployed by an allowed factory may register
return factory == _allowedFactory && IBasePoolFactory(factory).isPoolFromFactory(pool);
}

/**
* @notice Returns flags informing which hooks are implemented in the contract.
* @return hookFlags Flags indicating which hooks the contract supports
*/
function getHookFlags() external pure override returns (IHooks.HookFlags memory hookFlags) {
// Support the `onComputeDynamicSwapFeePercentage` hook
hookFlags.shouldCallComputeDynamicSwapFee = true;
}

/**
* @notice Called before `onBeforeSwap` if the pool has dynamic fees.
* @param params Swap parameters (see IBasePool.PoolSwapParams for struct definition)
* @param staticSwapFeePercentage Value of the static swap fee, for reference
* @return success True if the pool wishes to proceed with settlement
* @return dynamicSwapFee Value of the swap fee
*/
function onComputeDynamicSwapFee(
IBasePool.PoolSwapParams calldata params,
address,
address, // pool
uint256 staticSwapFeePercentage
) external view override returns (bool, uint256) {
// If the router is not trusted, does not apply the veBAL discount because getSender() may be manipulated by a malicious router.
) external view override returns (bool success, uint256 dynamicSwapFee) {
// If the router is not trusted, do not apply a fee discount
if (params.router != _trustedRouter) {
return (true, staticSwapFeePercentage);
}

// Find the user's address
address user = IRouterCommon(params.router).getSender();

// If user has veBAL, apply a 50% discount to the current fee (divides fees by 2)
// If the user owns veBAL, apply a 50% discount to the swap fee
if (_veBAL.balanceOf(user) > 0) {
return (true, staticSwapFeePercentage / 2);
}

// Otherwise, do not apply the discount
return (true, staticSwapFeePercentage);
}
}
15 changes: 9 additions & 6 deletions packages/foundry/script/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";

import { ScaffoldHelpers } from "./ScaffoldHelpers.sol";
import { DeployMockTokens } from "./DeployMockTokens.s.sol";
import { DeployConstantSum } from "./DeployConstantSum.s.sol";
import { DeployConstantProduct } from "./DeployConstantProduct.s.sol";
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";

/**
* @title Deploy Script
* @notice Import all deploy scripts here so that scaffold can exportDeployments()
* @dev Run this script with `yarn deploy`
*/
contract DeployScript is DeployMockTokens, DeployConstantSum, DeployConstantProduct {
contract DeployScript is ScaffoldHelpers {
function run() external virtual {
// Deploy mock tokens to be used for pools and hooks contracts
(IERC20 mockToken1, IERC20 mockToken2, IERC20 mockVeBAL) = deployMockTokens();
DeployMockTokens deployMockTokens = new DeployMockTokens();
(IERC20 mockToken1, IERC20 mockToken2, IERC20 mockVeBAL) = deployMockTokens.run();

// Deploy a constant sum factory and a pool
deployConstantSum(mockToken1, mockToken2);
DeployConstantSum deployConstantSum = new DeployConstantSum();
deployConstantSum.run(mockToken1, mockToken2);

// Deploy a constant product factory, a hooks contract, and a pool
deployConstantProduct(mockToken1, mockToken2, mockVeBAL);
DeployConstantProduct deployConstantProduct = new DeployConstantProduct();
deployConstantProduct.run(mockToken1, mockToken2, mockVeBAL);

/**
* This function generates the file containing the contracts Abi definitions that are carried from /foundry to /nextjs.
Expand Down
31 changes: 18 additions & 13 deletions packages/foundry/script/DeployConstantProduct.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import { ConstantProductFactory } from "../contracts/pools/ConstantProductFactor
* @notice Deploys a factory and hooks contract and then deploys, registers, and initializes a constant product pool
*/
contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {
function deployConstantProduct(IERC20 token1, IERC20 token2, IERC20 veBAL) internal {
function run(IERC20 token1, IERC20 token2, IERC20 veBAL) external virtual {
// Set the deployment configurations
uint32 pauseWindowDuration = 365 days;
RegistrationConfig memory regConfig = getPoolRegistrationConfig(token1, token2);
InitializationConfig memory initConfig = getPoolInitializationConfig(token1, token2);
PoolRegistrationConfig memory regConfig = getPoolRegistrationConfig(token1, token2);
PoolInitializationConfig memory initConfig = getPoolInitializationConfig(token1, token2);

// Start creating the transactions
uint256 deployerPrivateKey = getDeployerPrivateKey();
Expand All @@ -40,8 +40,8 @@ contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {
VeBALFeeDiscountHook poolHooksContract = new VeBALFeeDiscountHook(
IVault(vault),
address(factory),
address(veBAL),
address(router)
address(router),
IERC20(veBAL)
);
console.log("VeBALFeeDiscountHook deployed at address: %s", address(poolHooksContract));

Expand All @@ -59,8 +59,14 @@ contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {
);
console.log("Constant Product Pool deployed at: %s", pool);

// Approve Permit2 contract to spend tokens on behalf of deployer
approveSpenderOnToken(address(permit2), initConfig.tokens);

// Approve Router contract to spend tokens using Permit2
approveSpenderOnPermit2(address(router), initConfig.tokens);

// Seed the pool with initial liquidity
initializePool(
router.initialize(
pool,
initConfig.tokens,
initConfig.exactAmountsIn,
Expand All @@ -74,23 +80,22 @@ contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {

/**
* @dev Set all of the configurations for deploying and registering a pool here
*
* TokenConfig encapsulates the data required for the Vault to support a token of the given type.
* @notice TokenConfig encapsulates the data required for the Vault to support a token of the given type.
* 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(
IERC20 token1,
IERC20 token2
) internal view returns (RegistrationConfig memory regConfig) {
) internal view returns (PoolRegistrationConfig 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.001e18; // 0.1%
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[] 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)
token: token1,
tokenType: TokenType.STANDARD, // STANDARD or WITH_RATE
Expand All @@ -116,7 +121,7 @@ contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {
enableDonation: false
});

regConfig = RegistrationConfig({
config = PoolRegistrationConfig({
name: name,
symbol: symbol,
salt: salt,
Expand All @@ -136,7 +141,7 @@ contract DeployConstantProduct is PoolHelpers, ScaffoldHelpers {
function getPoolInitializationConfig(
IERC20 token1,
IERC20 token2
) internal pure returns (InitializationConfig memory poolInitConfig) {
) internal pure returns (PoolInitializationConfig memory config) {
IERC20[] memory tokens = new IERC20[](2); // Array of tokens to be used in the pool
tokens[0] = token1;
tokens[1] = token2;
Expand All @@ -147,7 +152,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

poolInitConfig = InitializationConfig({
config = PoolInitializationConfig({
tokens: InputHelpers.sortTokens(tokens),
exactAmountsIn: exactAmountsIn,
minBptAmountOut: minBptAmountOut,
Expand Down
29 changes: 17 additions & 12 deletions packages/foundry/script/DeployConstantSum.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
import { IRateProvider } from "@balancer-labs/v3-interfaces/contracts/vault/IRateProvider.sol";
import { InputHelpers } from "@balancer-labs/v3-solidity-utils/contracts/helpers/InputHelpers.sol";
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";

import { PoolHelpers } from "./PoolHelpers.sol";
import { ScaffoldHelpers, console } from "./ScaffoldHelpers.sol";
Expand All @@ -21,18 +20,18 @@ import { ConstantSumFactory } from "../contracts/pools/ConstantSumFactory.sol";
* @notice Deploys, registers, and initializes a Constant Sum Pool
*/
contract DeployConstantSum is PoolHelpers, ScaffoldHelpers {
function deployConstantSum(IERC20 token1, IERC20 token2) internal {
function run(IERC20 token1, IERC20 token2) external virtual {
// Set the deployment configurations
uint32 pauseWindowDuration = 365 days;
RegistrationConfig memory regConfig = getRegistrationConfig(token1, token2);
InitializationConfig memory initConfig = getInitializationConfig(token1, token2);
PoolRegistrationConfig memory regConfig = getPoolRegistrationConfig(token1, token2);
PoolInitializationConfig memory initConfig = getPoolInitializationConfig(token1, token2);

// Start creating the transactions
uint256 deployerPrivateKey = getDeployerPrivateKey();
vm.startBroadcast(deployerPrivateKey);

// Deploy a constant sum factory contract
ConstantSumFactory factory = new ConstantSumFactory(IVault(vault), pauseWindowDuration);
ConstantSumFactory factory = new ConstantSumFactory(vault, pauseWindowDuration);
console.log("Constant Sum Factory deployed at: %s", address(factory));

// Deploy a pool and register it with the vault
Expand All @@ -49,8 +48,14 @@ contract DeployConstantSum is PoolHelpers, ScaffoldHelpers {
);
console.log("Constant Sum Pool deployed at: %s", pool);

// Approve Permit2 contract to spend tokens on behalf of deployer
approveSpenderOnToken(address(permit2), initConfig.tokens);

// Approve Router contract to spend tokens using Permit2
approveSpenderOnPermit2(address(router), initConfig.tokens);

// Seed the pool with initial liquidity
initializePool(
router.initialize(
pool,
initConfig.tokens,
initConfig.exactAmountsIn,
Expand All @@ -68,10 +73,10 @@ 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 getRegistrationConfig(
function getPoolRegistrationConfig(
IERC20 token1,
IERC20 token2
) internal view returns (RegistrationConfig memory regConfig) {
) internal view returns (PoolRegistrationConfig 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
Expand Down Expand Up @@ -105,7 +110,7 @@ contract DeployConstantSum is PoolHelpers, ScaffoldHelpers {
enableDonation: false
});

regConfig = RegistrationConfig({
config = PoolRegistrationConfig({
name: name,
symbol: symbol,
salt: salt,
Expand All @@ -122,10 +127,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 getInitializationConfig(
function getPoolInitializationConfig(
IERC20 token1,
IERC20 token2
) internal pure returns (InitializationConfig memory poolInitConfig) {
) internal pure returns (PoolInitializationConfig memory config) {
IERC20[] memory tokens = new IERC20[](2); // Array of tokens to be used in the pool
tokens[0] = token1;
tokens[1] = token2;
Expand All @@ -136,7 +141,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

poolInitConfig = InitializationConfig({
config = PoolInitializationConfig({
tokens: InputHelpers.sortTokens(tokens),
exactAmountsIn: exactAmountsIn,
minBptAmountOut: minBptAmountOut,
Expand Down
2 changes: 1 addition & 1 deletion packages/foundry/script/DeployMockTokens.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { MockVeBAL } from "../contracts/mocks/MockVeBAL.sol";
* @notice Deploys mock tokens for use with pools and hooks
*/
contract DeployMockTokens is ScaffoldHelpers {
function deployMockTokens() internal returns (IERC20 mockToken1, IERC20 mockToken2, IERC20 mockVeBAL) {
function run() external virtual returns (IERC20 mockToken1, IERC20 mockToken2, IERC20 mockVeBAL) {
uint256 deployerPrivateKey = getDeployerPrivateKey();

vm.startBroadcast(deployerPrivateKey);
Expand Down
Loading

0 comments on commit 0bca6e3

Please sign in to comment.