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

Update to deploy 7 #66

Merged
merged 5 commits into from
Aug 16, 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
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
Loading