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.
Balancer SDK | Scaffold ETH 2 | Balancer v3 Monorepo |
---|
- Basic understanding of Solidity and Foundry
- Basic understanding of liquidity pools and AMMs
- Basic understanding of Balancer v3
- Environment Setup π§βπ»
- Create a Custom Pool π
- Create a Pool Factory π
- Create a Pool Hook πͺ
- Deploy the Contracts π’
- Test the Contracts π§ͺ
- Node (>= v18.17)
- Yarn (v1 or v2+)
- Git
- Foundry
- Clone this repo & install dependencies
git clone https://github.com/balancer/scaffold-balancer-v3.git
cd scaffold-balancer-v3
yarn install
- Set the necessary environment variables in a
packages/foundry/.env
file 1
DEPLOYER_PRIVATE_KEY=0x...
SEPOLIA_RPC_URL=...
- Start a local anvil fork of the Sepolia testnet
yarn fork
- Deploy the mock tokens, pool factories, pool hooks, and custom pools contracts 2
yarn deploy
- Start the nextjs frontend
yarn start
- Explore the frontend
- Navigate to http://localhost:3000 to see the home page
- Visit the Pools Page to search by address or select using the pool buttons
- Vist the Debug Page to see the mock tokens, factory, and hooks contracts
- Run the Foundry tests
yarn test
SE-2 offers a variety of configuration options for connecting an account, choosing networks, and deploying contracts
π₯ Burner Wallet
If you do not have an active wallet extension connected to your web browser, then scaffold eth will automatically connect to a "burner wallet" that is randomly generated on the frontend and saved to the browser's local storage. When using the burner wallet, transactions will be instantly signed, which is convenient for quick iterative development.
To force the use of burner wallet, disable your browsers wallet extensions and refresh the page. Note that the burner wallet comes with 0 ETH to pay for gas so you will need to click the faucet button in top right corner. Also the mock tokens for the pool are minted to your deployer account set in .env
so you will want to navigate to the "Debug Contracts" page to mint your burner wallet some mock tokens to use with the pool.
π Browser Extension Wallet
- To use your preferred browser extension wallet, ensure that the account you are using matches the PK you previously provided in the
foundry/.env
file - You may need to add a local development network with rpc url
http://127.0.0.1:8545/
and chain id31337
. Also, you may need to reset the nonce data for your wallet exension if it gets out of sync.
π Debug Contracts Page
The Debug Contracts Page can be useful for viewing and interacting with all of the externally avaiable read and write functions of a contract. The page will automatically hot reload with contracts that are deployed via the 01_DeployConstantSumFactory.s.sol
script. We use this handy setup to mint mockERC20
tokens to any connected wallet
π Changing The Frontend Network Connection
- The network the frontend points at is set via
targetNetworks
in thescaffold.config.ts
file usingchains
from viem. - By default, the frontend runs on a local node at
http://127.0.0.1:8545
const scaffoldConfig = {
targetNetworks: [chains.foundry],
π΄ Changing The Forked Network
- By default, the
yarn fork
command points at sepolia, but any of the network aliases from the[rpc_endpoints]
offoundry.toml
can be used to modify the"fork"
alias in thepackages/foundry/package.json
file
"fork": "anvil --fork-url ${0:-sepolia} --chain-id 31337 --config-out localhost.json",
- To point the frontend at a different forked network, change the
targetFork
inscaffold.config.ts
const scaffoldConfig = {
// The networks the frontend can connect to
targetNetworks: [chains.foundry],
// If using chains.foundry as your targetNetwork, you must specify a network to fork
targetFork: chains.sepolia,
Your journey begins with planning the custom computation logic for the pool, which defines how an AMM exchanges one asset for another.
- Must inherit from
IBasePool
andBalancerPoolToken
- Must implement
onSwap
,computeInvariant
, andcomputeBalance
- Must implement
getMaximumSwapFeePercentage
andgetMinimumSwapFeePercentage
- To get started, edit the
ConstantSumPool.sol
contract directly or make a copy
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
- A pool factory contract must inherit from BasePoolFactory
- Use the internal
_create
function to deploy a new pool - Use the internal
_registerPoolWithVault
fuction to register a pool immediately after creation
- To get started, edit the
ConstantSumFactory.sol
contract directly or make a copy
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
- A hooks contract must inherit from BasePoolHooks.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
- To get started, edit the
VeBALFeeDiscountHook.sol
contract directly or make a copy
The deploy scripts are all located in the foundry/script/ directory and are prefixed with a number based on the order the order they're intended to be run. The mock tokens, factories, and hooks contracts must be deployed before the pools. On the frontend, the Pools page will automatically add a button above the search bar for any pools deployed using the latest factory contract
Deploys mock tokens, factory contracts, and hooks contracts to be used by pools
- Set the
pauseWindowDuration
for the factory contracts - Set the mock token names, symbols, and supply
- Set any hooks contracts constructor args
Deploys, registers, and initializes a Constant Sum Pool
- Set the pool registration config in the
getRegistrationConfig()
function - Set the pool initialization config in the
getInitializationConfig()
function
Deploys, registers, and initializes a Constant Product Pool
- Set the pool registration config in the
getRegistrationConfig()
function - Set the pool initialization config in the
getInitializationConfig()
function
To run all the deploy scripts
yarn deploy
To run only the DeploySetup
script
yarn deploy:setup
To run only the DeployConstantSumPool
script
yarn deploy:sum
To run only the DeployConstantProductPool
script
yarn deploy:product
π To deploy to the live sepolia testnet, add the --network sepolia
flag
π To modify the yarn commands, edit the "scripts" section of the /foundry/package.json
Sample tests for the ConstantSumPool
and ConstantSumFactory
are provided as examples to help you get started writing your own tests.
The ConstantSumFactoryTest
roughly mirrors the WeightedPool8020FactoryTest
yarn test --match-contract ConstantSumFactoryTest
The ConstantSumPoolTest
roughly mirrors the WeightedPoolTest
yarn test --match-contract ConstantSumPoolTest
- Coming soonβ’οΈ after update to 6th testnet deployment of v3
Footnotes
-
The
DEPLOYER_PRIVATE_KEY
must start with0x
and must possess enough Sepolia ETH to deploy the contracts. TheSEPOLIA_RPC_URL
facilitates running a local fork and sending transactions to sepolia testnet β© -
The
DEPLOYER_PRIVATE_KEY
wallet receives the mock tokens and resulting BPT from pool initialization β©