This repo contains smart contract implementations of all known Linear Pool factories on Balancer. Contributions are welcome!
IMPORTANT: Before developing your own factory, ensure that your yield-bearing token is truly incompatible with existing solutions. In most cases, unique factories are not required to support forks of existing integrations (e.g., Aave v2). Furthermore, yield vaults implementing the ERC-4626 standard can leverage that factory and will not require bespoke solutions.
For more information about Boosted Pools and Linear Pools, please consult the official documentation.
Each implementation should include the components below.
The Linear Pool contract defines how the exchange rate is calculated. This is the most critical component because it determines the price of each swap within the Pool.
NOTE: All external calls within the
_getWrappedTokenRatefunction should include try/catch blocks and utilize theExternalCallLibto prevent manipulation by nefarious tokens.
At a minimum, the unit tests for each Linear Pool should verify the following assumptions:
- There is strict relationship between a
mainTokenandwrappedTokenis respected (e.g., cDAI is not paired with USDC). - Asset Managers are set correctly.
- The token rate is calculated correctly and is normalized to 1e18 regardless of
mainTokenorwrappedTokendecimals. - Malicious queries (that manipulate the token rate) are reverted.
The Linear Pool Factory contract is responsible for creating new Linear Pools.
At a minimum, the unit tests for each Linear Pool Factory should verify the following assumptions:
- A Pool can be created and its constituent tokens are registered in the Vault.
- The Factory version and Pool version are set correctly.
- An Asset Manager is created and configured for each Pool.
The Linear Pool Rebalancer contract is a helper to enable efficient, capital-free arbitrage and maintain the target balance of mainToken within the Pool.
It doesn't have unit tests. The logic of the Rebalancer is instead verified using fork tests, since it is very sensitive to the wrappedToken implementation.
In order to implement a Linear Pool for a given yield protocol, we need to understand the following features of that protocol:
- Can the exchange rate between the
mainTokenandwrappedTokenbe queried directly, and is the return value up to date?- If not, how can we calculate the exchange rate?
- How is
mainTokendeposited to the protocol? - How is
wrappedTokenredeemed, ormainTokenwithdrawn, from the protocol? - How many decimals does the
wrappedTokenhave? Is it at all related to themainTokendecimals, or is it fixed?
These questions will define which interfaces need to be declared. Some protocols expose all of this via the token contract itself, whereas others perform some or all operations via a central protocol vault.
In order to properly unit test Linear Pool contracts, mocked token contracts need to be implemented. These can be found inside the __mocks__ directory.
Fork tests are also essential because they act on real token contracts rather than mocks. The Rebalancer, especially, requires precision in order to function correctly, so it is safest to verify it on a real protocol token.
Fork tests can be found inside pkg/fork-tests, and their implementation is described in the next section.
-
Clone this repository to your machine. Ensure that you have
nodejsinstalled and that your version is at least 14.x (preferably 16.x). Runyarn && yarn buildto begin, and troubleshoot any errors you encounter before moving forward. -
Create a copy of
pkg/linear-pools/contracts/erc4626-linear-pool, and change the name to match your protocol's name (e.g.,pkg/linear-pools/contracts/<YOUR_PROTOCOL>-linear-pool) -
Change the names of all files accordingly, e.g.,
ERC4626LinearPool.sol,ERC4626LinearPoolFactory.sol,ERC4626LinearPoolRebalancer.sol, and all corresponding test files. -
Within each file, change the names of variables and classes to suit your protocol's name.
-
Inside
<YOUR_PROTOCOL>LinearPool.sol, adapt the_getWrappedTokenRatefunction to your protocol. Make sure to wrap any external calls in try/catch blocks and utilize theExternalCallLib.- NOTE: During this step, you'll probably need to define an interface for the token/vault of the protocol, especially the function pertaining to the exchange rate.
-
Inside
<YOUR_PROTOCOL>LinearPoolRebalancer.sol, define the_wrapTokens(deposit),_unwrapTokens(redeem), and_getRequiredTokensToWrap(given an amount ofwrappedToken, how manymainTokendo I need?) functions.- IMPORTANT:
_getRequiredTokensToWrapalso uses the token rate, so make sure that_getWrappedTokenRateand_getRequiredTokensToWrapuse the same source to fetch the token rate. - IMPORTANT: During this step, the interface created in Step 4 will need to be expanded to include withdraw/deposit functions.
- IMPORTANT:
-
Edit the
setupsection within your Linear Pool test file to make sure you're deploying and testing the correct Linear Pool. Do not delete any tests from the copied file, since many tests apply to all kinds of Linear Pools and protocols.- NOTE:
setupdeploys a mocked version of the token, so you'll also need to implement a mock. If your protocol uses a central vault contract as well, check theAaveLinearPooltests for examples.
- NOTE:
-
Run
yarn testand make sure the Linear Pool tests pass. -
Edit the Linear Pool Factory test file (especially
beforeEach('deploy factory & tokens')) to adapt to your protocol. You don't need to change the Protocol ID right now. -
Run
yarn testand make sure the Linear Pool Rebalancer tests pass. -
To begin fork testing, navigate to
pkg/fork-tests/tests, duplicate the ERC-4626 test folder, and change the name to your protocol. Make sure the number in the folder matches the YYYYMMDD pattern. -
Delete the
outputdirectory and the contents ofbuild-info. -
Adapt
index.ts,input.ts, andreadme.mdto your protocol name. -
Open
pkg/linear-poolsand runyarn hardhat compile. Once the contracts are compiled, open theartifacts/build-infodirectory and copy the json inside this file topkg/fork-tests/tests/YYYYMMDD-<YOUR-PROTOCOL>-linear-pool/build-info. -
Inside your test folder, open
test.fork.tsand adapt it to your protocol. Make sure that you're using a recent block number, token addresses are correctly defined, and your chosen token holder has a large balance at that block number. -
Go to
pkg/fork-testsand runyarn test. Make sure your tests are passing.