Skip to content

Commit

Permalink
Update to deploy 7 (#66)
Browse files Browse the repository at this point in the history
* forge install: balancer-v3-monorepo

7682994

* Update foundry package to deploy 7

* Update frontend for deploy 7

* Add balancer logo

* Fix function visibility warnings
  • Loading branch information
MattPereira authored Aug 16, 2024
1 parent 7abeb10 commit b05f3af
Show file tree
Hide file tree
Showing 30 changed files with 319 additions and 807 deletions.
58 changes: 26 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 🏗️ Scaffold Balancer v3
# Scaffold Balancer v3

A full stack prototyping tool for building on top of Balancer v3. Accelerate the process of designing and deploying custom pools and hooks contracts. Concentrate on mastering the core concepts within a swift and responsive environment augmented by a local fork and a frontend pool operations playground.

Expand All @@ -15,26 +15,26 @@ A full stack prototyping tool for building on top of Balancer v3. Accelerate the

### 🪧 Table Of Contents

- [🧑‍💻 Environment Setup](#0-environment-setup-)
- [🌊 Create a Custom Pool](#1-create-a-custom-pool-)
- [🏭 Create a Pool Factory](#2-create-a-pool-factory-)
- [🪝 Create a Pool Hook](#3-create-a-pool-hook-)
- [🚢 Deploy the Contracts](#4-deploy-the-contracts-)
- [🧪 Test the Contracts](#5-test-the-contracts-)
- [🧑‍💻 Environment Setup](#-environment-setup)
- [🌊 Create a Custom Pool](#-create-a-custom-pool)
- [🏭 Create a Pool Factory](#-create-a-pool-factory)
- [🪝 Create a Pool Hook](#-create-a-pool-hook)
- [🚢 Deploy the Contracts](#-deploy-the-contracts)
- [🧪 Test the Contracts](#-test-the-contracts)

## 🧑‍💻 Environment Setup

<!-- TODO: Record Updated Video -->
<!-- [![image](https://github.com/user-attachments/assets/2d0d5c6d-647d-4782-8d7a-9076b39319b9)](https://www.youtube.com/watch?v=2lInvpCt2o4) -->

### 📜 Requirements
### 1. Requirements 📜

- [Node (>= v18.17)](https://nodejs.org/en/download/)
- Yarn ([v1](https://classic.yarnpkg.com/en/docs/install/) or [v2+](https://yarnpkg.com/getting-started/install))
- [Git](https://git-scm.com/downloads)
- [Foundry](https://book.getfoundry.sh/getting-started/installation)

### 🏃 Quickstart
### 2. Quickstart 🏃

1. Clone this repo & install dependencies

Expand Down Expand Up @@ -83,7 +83,7 @@ yarn start
yarn test
```

### 🏗️ Scaffold ETH 2 Tips
### 3. Scaffold ETH 2 Tips 🏗️

SE-2 offers a variety of configuration options for connecting an account, choosing networks, and deploying contracts

Expand Down Expand Up @@ -149,72 +149,66 @@ const scaffoldConfig = {
Your journey begins with planning the custom computation logic for the pool, which defines how an AMM exchanges one asset for another.
### 📖 Review the Docs
### 1. Review the Docs 📖
- [Create a custom AMM with a novel invariant](https://docs-v3.balancer.fi/build-a-custom-amm/build-an-amm/create-custom-amm-with-novel-invariant.html)
### 🔑 Recall the Key Requirements
### 2. Recall the Key Requirements 🔑
- Must inherit from `IBasePool` and `BalancerPoolToken`
- Must implement `onSwap`, `computeInvariant`, and `computeBalance`
- Must implement `getMaximumSwapFeePercentage` and `getMinimumSwapFeePercentage`
### 📝 Write a Custom Pool Contract
### 3. Write a Custom Pool Contract 📝
- To get started, edit the`ConstantSumPool.sol` contract directly or make a copy
## 🏭 Create a Pool Factory
After designing a pool contract, the next step is to prepare a factory contract because Balancer's off-chain infrastructure uses the factory address as a means to identify the type of pool, which is important for integration into the UI, SDK, and external aggregators
### 📖 Review the Docs
### 1. Review the Docs 📖
- [Deploy a Custom AMM Using a Factory](https://docs-v3.balancer.fi/build-a-custom-amm/build-an-amm/deploy-custom-amm-using-factory.html)
### 🔑 Recall the Key Requirements
### 2. Recall the Key Requirements 🔑
- A pool factory contract must inherit from [BasePoolFactory](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/factories/BasePoolFactory.sol)
- Use the internal `_create` function to deploy a new pool
- Use the internal `_registerPoolWithVault` fuction to register a pool immediately after creation
### 📝 Write a Factory Contract
### 3. Write a Factory Contract 📝
- To get started, edit the`ConstantSumFactory.sol` contract directly or make a copy
## 🪝 Create a Pool Hook
Next, consider further extending the functionality of the custom pool contract with a hooks contract. If your custom pool does not need a hooks contract, use the zero address during pool registration
### 📖 Review the Docs
### 1. Review the Docs 📖
- [Extend an Existing Pool Type Using Hooks](https://docs-v3.balancer.fi/build-a-custom-amm/build-an-amm/extend-existing-pool-type-using-hooks.html)
### 🔑 Recall the Key Requirements
### 2. Recall the Key Requirements 🔑
- A hooks contract must inherit from [BasePoolHooks.sol](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/BaseHooks.sol)
- Must implement `getHookFlags` to define which hooks are supported
- Must implement `onRegister` to determine if a pool is allowed to use the hook contract
### 📝 Write a Hook Contract
### 3. Write a Hook Contract 📝
- To get started, edit the `VeBALFeeDiscountHook.sol` contract directly or make a copy
## 🚢 Deploy the Contracts
The deploy scripts are all located in the [foundry/script/](https://github.com/balancer/scaffold-balancer-v3/tree/main/packages/foundry/script) directory. All deploy scripts should be run inside of `Deploy.s.sol` so that the `export` modifier can automate the transfer of deployed contract info to `nextjs/contracts/depoloyedContracts.ts`
### 👯 Follow the Pattern
To add a new deploy script, import it into `Deploy.s.sol`, create a new instance of it, and run it
The deploy scripts are located in the [foundry/script/](https://github.com/balancer/scaffold-balancer-v3/tree/main/packages/foundry/script) directory. All deploy scripts should be run inside of `Deploy.s.sol` so that the `export` modifier can automate the transfer of deployed contract info to `nextjs/contracts/depoloyedContracts.ts`. To add a new deploy script, import it into `Deploy.s.sol`, create a new instance of it, and run it
```
function run() external virtual export {
DeployYourContract deployYourContract = new DeployYourContract();
deployYourContract.run();
}
```
### 🛠️ Examine the Example Deploy Scripts
### 1. Examine the Example Deploy Scripts 🕵️
#### `00_DeployMockTokens.s.sol`
Expand All @@ -233,7 +227,7 @@ function run() external virtual export {
2. Deploys and registers a `ConstantProductPool`
3. Initializes the `ConstantProductPool` using mock tokens
### 📡 Broadcast the Transactions
### 2. Broadcast the Transactions 📡
To run all the deploy scripts
Expand All @@ -245,9 +239,9 @@ yarn deploy
## 🧪 Test the Contracts
The [balancer-v3-monorepo](https://github.com/balancer/balancer-v3-monorepo) provides testing utility contracts like [BaseVaultTest](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/test/foundry/utils/BaseVaultTest.sol). Therefore, the best way to begin writing tests for custom factory, pool, and hook contracts is to utilize the patterns and methods established by the source code.
The [balancer-v3-monorepo](https://github.com/balancer/balancer-v3-monorepo) provides testing utility contracts like [BasePoolTest](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/test/foundry/utils/BasePoolTest.sol) and [BaseVaultTest](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/test/foundry/utils/BaseVaultTest.sol). Therefore, the best way to begin writing tests for custom factories, pools, and hooks contracts is to leverage the examples established by the source code.
### 👨‍🔬 Testing Factories
### 1. Testing Factories 👨‍🔬
The `ConstantSumFactoryTest` roughly mirrors the [WeightedPool8020FactoryTest
](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/pool-weighted/test/foundry/WeightedPool8020Factory.t.sol)
Expand All @@ -256,15 +250,15 @@ The `ConstantSumFactoryTest` roughly mirrors the [WeightedPool8020FactoryTest
yarn test --match-contract ConstantSumFactoryTest
```
### 🏊 Testing Pools
### 2. Testing Pools 🏊
The `ConstantSumPoolTest` roughly mirrors the [WeightedPoolTest](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/pool-weighted/test/foundry/WeightedPool.t.sol)
```
yarn test --match-contract ConstantSumPoolTest
```
### 🎣 Testing Hooks
### 3. Testing Hooks 🎣
The `VeBALFeeDiscountHookTest` mirrors the [VeBALFeeDiscountHookExampleTest](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/pool-hooks/test/foundry/VeBALFeeDiscountHookExample.t.sol)
Expand Down
31 changes: 17 additions & 14 deletions packages/foundry/contracts/hooks/VeBALFeeDiscountHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

pragma solidity ^0.8.24;

import {
BaseHooks,
IVault,
IHooks,
TokenConfig,
LiquidityManagement
} from "@balancer-labs/v3-vault/contracts/BaseHooks.sol";
import { IBasePool } from "@balancer-labs/v3-interfaces/contracts/vault/IBasePool.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { IBasePoolFactory } from "@balancer-labs/v3-interfaces/contracts/vault/IBasePoolFactory.sol";
import { IHooks } from "@balancer-labs/v3-interfaces/contracts/vault/IHooks.sol";
import { IRouterCommon } from "@balancer-labs/v3-interfaces/contracts/vault/IRouterCommon.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";
import {
LiquidityManagement,
TokenConfig,
PoolSwapParams,
HookFlags
} from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol";

import { BaseHooks } from "@balancer-labs/v3-vault/contracts/BaseHooks.sol";

/**
* @title VeBAL Fee Discount Hook
Expand Down Expand Up @@ -43,7 +46,7 @@ contract VeBALFeeDiscountHook is BaseHooks {
address pool,
TokenConfig[] memory,
LiquidityManagement calldata
) external view override returns (bool) {
) public view override returns (bool) {
// Only pools deployed by an allowed factory may register
return factory == _allowedFactory && IBasePoolFactory(factory).isPoolFromFactory(pool);
}
Expand All @@ -52,7 +55,7 @@ contract VeBALFeeDiscountHook is BaseHooks {
* @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) {
function getHookFlags() public pure override returns (HookFlags memory hookFlags) {
// Support the `onComputeDynamicSwapFeePercentage` hook
hookFlags.shouldCallComputeDynamicSwapFee = true;
}
Expand All @@ -64,11 +67,11 @@ contract VeBALFeeDiscountHook is BaseHooks {
* @return success True if the pool wishes to proceed with settlement
* @return dynamicSwapFee Value of the swap fee
*/
function onComputeDynamicSwapFee(
IBasePool.PoolSwapParams calldata params,
function onComputeDynamicSwapFeePercentage(
PoolSwapParams calldata params,
address, // pool
uint256 staticSwapFeePercentage
) external view override returns (bool success, uint256 dynamicSwapFee) {
) public 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);
Expand Down
17 changes: 16 additions & 1 deletion packages/foundry/contracts/pools/ConstantProductPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
pragma solidity ^0.8.24;

import { BalancerPoolToken } from "@balancer-labs/v3-vault/contracts/BalancerPoolToken.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.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";
import { FixedPoint } from "@balancer-labs/v3-solidity-utils/contracts/math/FixedPoint.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";

Expand All @@ -15,6 +16,10 @@ 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_SWAP_FEE_PERCENTAGE = 0.02e18; // 2%

Expand Down Expand Up @@ -65,6 +70,16 @@ contract ConstantProductPool is BalancerPoolToken, IBasePool {
newBalance = ((newInvariant * newInvariant) / poolBalanceOtherToken);
}

/// @return minimumInvariantRatio The minimum invariant ratio for a pool during unbalanced remove liquidity
function getMinimumInvariantRatio() external pure returns (uint256) {
return _MIN_INVARIANT_RATIO;
}

/// @return maximumInvariantRatio The maximum invariant ratio for a pool during unbalanced add liquidity
function getMaximumInvariantRatio() external pure returns (uint256) {
return _MAX_INVARIANT_RATIO;
}

/// @return minimumSwapFeePercentage The minimum swap fee percentage for a pool
function getMinimumSwapFeePercentage() external pure returns (uint256) {
return _MIN_SWAP_FEE_PERCENTAGE;
Expand Down
15 changes: 15 additions & 0 deletions packages/foundry/contracts/pools/ConstantSumPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,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 { PoolSwapParams } from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";

/**
Expand All @@ -11,6 +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 _MIN_SWAP_FEE_PERCENTAGE = 1e12; // 0.00001%
uint256 private constant _MAX_SWAP_FEE_PERCENTAGE = 0.1e18; // 10%

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

/// @return minimumInvariantRatio The minimum invariant ratio for a pool during unbalanced remove liquidity
function getMinimumInvariantRatio() external pure returns (uint256) {
return _MIN_INVARIANT_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
function getMinimumSwapFeePercentage() external pure returns (uint256) {
return _MIN_SWAP_FEE_PERCENTAGE;
Expand Down
2 changes: 1 addition & 1 deletion packages/foundry/lib/balancer-v3-monorepo
2 changes: 1 addition & 1 deletion 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 } from "./PoolHelpers.sol";
import { PoolHelpers, PoolRegistrationConfig, PoolInitializationConfig } from "./PoolHelpers.sol";
import { ScaffoldHelpers, console } from "./ScaffoldHelpers.sol";
import { ConstantSumFactory } from "../contracts/pools/ConstantSumFactory.sol";

Expand Down
2 changes: 1 addition & 1 deletion 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 } from "./PoolHelpers.sol";
import { PoolHelpers, PoolRegistrationConfig, PoolInitializationConfig } from "./PoolHelpers.sol";
import { ScaffoldHelpers, console } from "./ScaffoldHelpers.sol";
import { VeBALFeeDiscountHook } from "../contracts/hooks/VeBALFeeDiscountHook.sol";
import { ConstantProductFactory } from "../contracts/pools/ConstantProductFactory.sol";
Expand Down
46 changes: 23 additions & 23 deletions packages/foundry/script/PoolHelpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,9 @@ import { IRouter } from "@balancer-labs/v3-interfaces/contracts/vault/IRouter.so
* @notice Helpful types, interface instances, and functions for deploying pools on Balancer v3
*/
contract PoolHelpers {
struct PoolRegistrationConfig {
string name;
string symbol;
bytes32 salt;
TokenConfig[] tokenConfig;
uint256 swapFeePercentage;
bool protocolFeeExempt;
PoolRoleAccounts roleAccounts;
address poolHooksContract;
LiquidityManagement liquidityManagement;
}

struct PoolInitializationConfig {
IERC20[] tokens;
uint256[] exactAmountsIn;
uint256 minBptAmountOut;
bool wethIsEth;
bytes userData;
}

// BalancerV3 Sepolia addresses (6th testnet release)
IVault internal vault = IVault(0x92B5c1CB2999c45804A60d6529D77DeEF00fb839);
IRouter internal router = IRouter(0xa12Da7dfD0792a10a5b05B575545Bd685798Ce35);
// BalancerV3 Sepolia addresses (7th testnet release)
IVault internal vault = IVault(0x7966FE92C59295EcE7FB5D9EfDB271967BFe2fbA);
IRouter internal router = IRouter(0xDd10aDF05379D7C0Ee4bC9c72ecc5C01c40E25b8);
IPermit2 internal permit2 = IPermit2(0x000000000022D473030F116dDEE9F6B43aC78BA3);

/**
Expand Down Expand Up @@ -82,3 +62,23 @@ contract PoolHelpers {
}
}
}

struct PoolRegistrationConfig {
string name;
string symbol;
bytes32 salt;
TokenConfig[] tokenConfig;
uint256 swapFeePercentage;
bool protocolFeeExempt;
PoolRoleAccounts roleAccounts;
address poolHooksContract;
LiquidityManagement liquidityManagement;
}

struct PoolInitializationConfig {
IERC20[] tokens;
uint256[] exactAmountsIn;
uint256 minBptAmountOut;
bool wethIsEth;
bytes userData;
}
Loading

0 comments on commit b05f3af

Please sign in to comment.