From eff9b3fb6973932351029101d07acfb26e780081 Mon Sep 17 00:00:00 2001 From: Jeff Bennett Date: Wed, 2 Oct 2024 16:39:03 -0400 Subject: [PATCH 1/8] refactor: adjust router.initialize links --- .../build-an-amm/deploy-custom-amm-using-factory.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/build-a-custom-amm/build-an-amm/deploy-custom-amm-using-factory.md b/docs/build-a-custom-amm/build-an-amm/deploy-custom-amm-using-factory.md index 1ae92d82..6d34a3ea 100644 --- a/docs/build-a-custom-amm/build-an-amm/deploy-custom-amm-using-factory.md +++ b/docs/build-a-custom-amm/build-an-amm/deploy-custom-amm-using-factory.md @@ -15,7 +15,7 @@ To fully set up a new custom pool so that normal liquidity operations and swaps 2. Deploy the pool contract using the factory's `_create` function 3. Register the pool using the factory's `_registerPoolWithVault` function 4. Use [Permit2](https://github.com/Uniswap/permit2) to approve the Router to spend the tokens that will be used to initialize the pool -5. Call [`router.initialize()`](https://github.com/balancer/balancer-v3-monorepo/blob/e9bd6b0b154f2bd083a5049267b7a417c5a2c984/pkg/interfaces/contracts/vault/IRouter.sol#L39-L56) to seed the pool with initial liquidity +5. Call [`router.initialize()`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IRouter.sol#L46-L53) to seed the pool with initial liquidity ::: tip @@ -155,6 +155,6 @@ After a custom pool has been deployed and registered, the next step is to add in 1. Ensure the [Permit2](https://github.com/Uniswap/permit2) contract has been granted sufficient allowance to spend tokens on behalf of the `msg.sender` 2. Transfer sufficient allowance to the [Router](https://docs-v3.balancer.fi/concepts/router/overview.html) with [`Permit2.approve`](https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/AllowanceTransfer.sol#L25-L30) -3. Call [`router.initialize()`](https://github.com/balancer/balancer-v3-monorepo/blob/e9bd6b0b154f2bd083a5049267b7a417c5a2c984/pkg/interfaces/contracts/vault/IRouter.sol#L39-L56) +3. Call [`router.initialize()`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IRouter.sol#L46-L53) After a pool has been initialized, normal liquidity operations and swaps are instantly enabled. From 4b1785226fb10c94b795d00ce8660e41005a2479 Mon Sep 17 00:00:00 2001 From: Jeff Bennett Date: Wed, 2 Oct 2024 16:44:23 -0400 Subject: [PATCH 2/8] docs: spelling --- .../build-an-amm/extend-existing-pool-type-using-hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build-a-custom-amm/build-an-amm/extend-existing-pool-type-using-hooks.md b/docs/build-a-custom-amm/build-an-amm/extend-existing-pool-type-using-hooks.md index 2e279f6b..a02589c9 100644 --- a/docs/build-a-custom-amm/build-an-amm/extend-existing-pool-type-using-hooks.md +++ b/docs/build-a-custom-amm/build-an-amm/extend-existing-pool-type-using-hooks.md @@ -125,7 +125,7 @@ function onRegister( The `onRegister` function enables developers to implement custom validation logic to ensure the registration is valid. When a new pool is registered, a hook address can be provided to "link" the pool and the hook. At this stage, the `onRegister` function is invoked by the Vault, and it must return true for the registration to be successful. If the validation fails, the function should return false, preventing the registration from being completed. -In this example we validate that the `factory` param forwarded from the Vault matches the `allowedFactory` set during the hook deployment, and that the pool was deployed by that factory. If successful, it emits an event for tracking by offchain processes. +In this example we validate that the `factory` param forwarded from the Vault matches the `allowedFactory` set during the hook deployment, and that the pool was deployed by that factory. If successful, it emits an event for tracking by off-chain processes. ### Implementing the Swap Fee Logic From ef426d74e10892fefb125857325da264ff3f7f42 Mon Sep 17 00:00:00 2001 From: Jeff Bennett Date: Thu, 3 Oct 2024 18:00:14 -0400 Subject: [PATCH 3/8] docs: build-an-amm --- .../build-an-amm/create-custom-amm-with-novel-invariant.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build-a-custom-amm/build-an-amm/create-custom-amm-with-novel-invariant.md b/docs/build-a-custom-amm/build-an-amm/create-custom-amm-with-novel-invariant.md index 8c1e0af5..8bc186c3 100644 --- a/docs/build-a-custom-amm/build-an-amm/create-custom-amm-with-novel-invariant.md +++ b/docs/build-a-custom-amm/build-an-amm/create-custom-amm-with-novel-invariant.md @@ -23,7 +23,7 @@ To expedite the development process, Balancer provides two contracts to inherit Both `IBasePool` and `BalancerPoolToken` are used across all core Balancer pools, even those implemented by Balancer Labs (ie: [WeightedPool](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/pool-weighted/contracts/WeightedPool.sol#L18)). -Below, we present a naive implementation of a two token `ConstantProductPool` & `ConstantSumPool` utilizing (X * Y = K) & (X + Y = K) as a reference for walking through the required functions necessary to implement a custom AMM on Balancer protocol: +Below, we present a naive implementation of a two token `ConstantProductPool` and `ConstantSumPool` utilizing (X * Y = K) and (X + Y = K) as references for walking through the required functions necessary to implement a custom AMM on Balancer protocol: ::: code-tabs#shell @tab Constant Product Pool From 821873563e7a04f5d2d0e5ba02672e902de20b41 Mon Sep 17 00:00:00 2001 From: Jeff Bennett Date: Thu, 3 Oct 2024 18:01:47 -0400 Subject: [PATCH 4/8] docs: core-concepts --- docs/concepts/core-concepts/architecture.md | 20 +++++++++---------- .../core-concepts/balancer-pool-tokens.md | 13 +++++++++--- docs/concepts/core-concepts/hooks.md | 12 +++++------ .../core-concepts/pool-creator-fee.md | 12 +++++------ .../core-concepts/pool-role-accounts.md | 2 +- 5 files changed, 33 insertions(+), 26 deletions(-) diff --git a/docs/concepts/core-concepts/architecture.md b/docs/concepts/core-concepts/architecture.md index 572bf90e..b029cab8 100644 --- a/docs/concepts/core-concepts/architecture.md +++ b/docs/concepts/core-concepts/architecture.md @@ -9,11 +9,11 @@ order: 1 The Balancer protocol architecture comprises three primary components, each strategically designed to enhance flexibility and minimize the intricacies involved in constructing pools. By distributing responsibilities across these components, Balancer simplifies pool development, empowering builders to focus on innovation rather than grappling with complex code. -- Router: Serves as the user's gateway to the protocol, offering straightforward interfaces for executing operations. +- Router: Serves as the user's gateway to the protocol, offering straightforward interfaces for executing operations. (This includes the basic Router, BatchRouter, and CompositeLiquidityRouter.) - Vault: Centralizes liquidity operations and manages accounting, streamlining the handling of token balances across multiple pools. - Pool: Exposes precise pool mathematics through invariant calculations, enabling developers to harness powerful functionalities without delving into intricate details. -This design philosophy ensures that building pools on Balancer is intuitive and efficient, with the vault shouldering the burden of complexity, allowing pool creators to unleash their creativity without worrying about accounting details. +This design philosophy ensures that building pools on Balancer is intuitive and efficient, with the vault shouldering the burden of complexity, allowing pool creators to unleash their creativity without worrying about accounting details. The Vault is part of the core protocol, but users can build custom pools and routers. ## Simplified transaction flow @@ -37,9 +37,9 @@ The Vault verifies that the Router has correctly settled its accrued debts and c ![Detailed Router Vault interaction](/images/architecture-detailed.png) -1. The Balancer Router is the main interface for interacting with Balancer, providing a user-friendly way to access functions and simplify interactions with the Vault. Any smart contract can serve as a Router, tailored to the specific use case. +1. The Balancer Router is the main interface for interacting with Balancer, providing a user-friendly way to access functions and simplify interactions with the Vault. Any smart contract can serve as a Router, tailored to the specific use case. For instance, hooks and pools can "act" as routers and make calls on the Vault. It's also possible to create custom routers to combine Vault operations in novel ways (e.g., interact with a custom protocol). -2. The Router calls the Vault's `unlock` method and opens up the vault to record debts & credits based on liquidity or swap operations. This allows operations on the Vault to be combined atomically and still ensure correct accounting. +2. The Router calls the Vault's `unlock` method and opens up the vault to record debts and credits based on liquidity or swap operations. This allows operations on the Vault to be combined atomically and still ensure correct accounting. 3. With the Vault unlocked, the Vault calls back into the Router. This is where arbitrary logic can be executed, including any combination of calls to Vault primitives, interacting with external protocols, etc. For example, in the case of a swap action, the Vault calls the Router's specific action hook implementation, such as `swapSingleTokenHook`, and passes the initial function payload from step 1 back to the Router to continue the regular transaction flow. @@ -53,13 +53,13 @@ The inputs from step 1 are passed to the Vault's core functions, such as swap. These functions calculate the tokens that need to be deposited into or withdrawn from the Vault. Proportional liquidity management operations are an exception to this rule: the math is solved at the Vault level in this case. -6. The outcomes of these calculations are attributed as either debt or credit, which must be settled at a later stage. Additionally, the required amount of Balancer Pool Tokens (BPT) is minted or burned accordingly in the case of liquidity management operations. +6. The outcomes of these calculations are categorized as either debts or credits, which must be settled at a later stage. Additionally, in the case of liquidity management operations, the required amount of Balancer Pool Tokens (BPT) is minted or burned. -7. After debt & credit has been recorded for and shared with the Router by the Vault, the execution flow is passed to the Router. This allows the Router to be aware of the amounts owed (both by the sender to the Vault, and vice-versa). -It is important to mention that the Router contract has the ability to retrieve the current debt and credit owed to the Vault at any point during the execution by calling a specific function on the Vault. +7. After debts and credits have been recorded and shared with the Router by the Vault, the execution flow is passed to the Router. This allows the Router to be aware of the amounts owed (both by the sender to the Vault, and vice-versa). +It is important to mention that the Router contract has the ability to retrieve the current debts and credits associated with the Vault at any point during the execution by calling a specific function on the Vault. -8. The Router is responsible for settling the remaining debt and credit, which must be done for the transaction to succeed. If ETH or WETH is to be used in the transaction, the Router wraps or unwraps Ether right before settling WETH debts. +8. The Router is responsible for settling the remaining debts and credits, which must be done for the transaction to succeed. If ETH or WETH is to be used in the transaction, the Router wraps or unwraps Ether right before settling WETH debts. -9. When the router is done with settlement, the Vault verifies that all credit and debt accrued during the operations has been settled as the final step. Once the verification is complete, the Vault is locked again. If all debt has been correctly settled, the transaction will succeed; otherwise, it will be reverted. +9. When the router is done with settlement, the Vault verifies that all credits and debts accrued during the operations have been settled as the final step. Once the verification is complete, the Vault is locked again. If all debt has been correctly settled, the transaction will succeed; otherwise, it will be reverted. -On top of the basic workflow, pools can be extended with standalone hooks contracts that can be leveraged at different stages of the pool's lifecycle. These hooks contracts can be called either before or after a pool operation (i.e. step 5), depending on how the pool is configured during deployment. By utilizing hooks, developers can customize and enhance the functionality of pools, enabling the integration of features like oracles or time-weighted average market maker capabilities. See [hooks](./hooks.md) article for more detailed information about it. +On top of the basic workflow, pools can be extended with standalone hooks contracts that can be leveraged at different stages of the pool's lifecycle. These hooks contracts can be called either before or after a pool operation (i.e. step 5), depending on how the pool is configured during deployment. By utilizing hooks, developers can customize and enhance the functionality of pools, enabling the integration of features like oracles or time-weighted average market maker capabilities. See [hooks](./hooks.md) article for more detailed information. diff --git a/docs/concepts/core-concepts/balancer-pool-tokens.md b/docs/concepts/core-concepts/balancer-pool-tokens.md index 8d13cac7..f57e107c 100644 --- a/docs/concepts/core-concepts/balancer-pool-tokens.md +++ b/docs/concepts/core-concepts/balancer-pool-tokens.md @@ -15,7 +15,7 @@ The BalancerPoolToken contract adheres to the ERC20 token standard by incorporat Here's how the [`BalancerPoolToken`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/BalancerPoolToken.sol) contract achieves this: -Inheritance: The [`BalancerPoolToken`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/BalancerPoolToken.sol) contract also inherits from `IERC20`, `IERC20Metadata` and `IERC20Permit`. This means it has all the methods and properties required by the ERC20 standard. +Inheritance: The [`BalancerPoolToken`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/BalancerPoolToken.sol) contract also inherits from `IERC20`, `IERC20Metadata`, `IERC20Permit`, `IRateProvider`, `EIP712`, `Nonces`, `ERC165` and our local `VaultGuard`. This means it has all the methods and properties required by the ERC20 standard. - Delegation: The BalancerPoolToken contract doesn't manage the token state itself. Instead, it delegates this responsibility to the Vault contract. For example, the totalSupply, balanceOf, transfer, allowance, approve, and transferFrom methods all call the corresponding methods on the Vault contract. @@ -23,9 +23,16 @@ Inheritance: The [`BalancerPoolToken`](https://github.com/balancer/balancer-v3-m - ERC20Permit: The BalancerPoolToken contract also implements the ERC20 Permit extension, which allows approvals to be made via signatures. This is done in the permit method, which again delegates the approval to the Vault contract. +- IRateProvider: Each pool can serve as a rate provider, with a natural "BPT Rate" = pool value (= invariant) / totalSupply. However, great care must be taken when using pool tokens this way, as rates may be unpredictable or manipulable. In general, since the rate calculation does not include a "rounding hint" (as does the base invariant calculation), if there is any significant or non-linear error in the invariant, the computed value of the invariant might go down when it should go up. Weighted Pool invariants have this property, so calling `getRate` on a Weighted Pool is unsafe, and reverts unconditionally. + +- EIP712, Nonces: These have to do with supporting permit2 and signatures, so that explicit token approvals can be avoided in many cases. + +- ERC165: This is not used in core pool types, but allows interface detection. See the [Ethereum docs](https://eips.ethereum.org/EIPS/eip-165). + +- VaultGuard: This simply stores a reference to the Vault and defines the `onlyVault` modifier for functions that can only be called by the Vault. + By doing this, the BalancerPoolToken contract ensures that Balancer Pool Tokens (BPTs) are fully ERC20 compliant, while also allowing the Vault contract to have full control over BPT accounting. This design ensures atomic updates to critical pool state and supports composability, which is crucial for integration with other DeFi protocols. ## Composability -As BPTs adhere to the ERC20 standard, they can seamlessly integrate as pool tokens in other pools. For instance, the BPT of a boosted pool comprising DAI, USDC, and USDT can be paired with tokens from new projects. This composability ensures the maintenance of deep and capital-efficient stable liquidity, while simultaneously creating efficient swap paths for the project token. - +As BPTs adhere to the ERC20 standard, they can seamlessly integrate as pool tokens in other pools. For instance, the BPT of an ERC4626 pool comprising wrapped versions of DAI, USDC, and USDT can be paired with tokens from new projects. This composability ensures the maintenance of deep and capital-efficient stable liquidity, while simultaneously creating efficient swap paths for the project token. diff --git a/docs/concepts/core-concepts/hooks.md b/docs/concepts/core-concepts/hooks.md index 17396cdc..762839c8 100644 --- a/docs/concepts/core-concepts/hooks.md +++ b/docs/concepts/core-concepts/hooks.md @@ -59,13 +59,13 @@ struct HookFlags { bool shouldCallAfterRemoveLiquidity; } ``` -This decision is final and cannot be changed for a pool once it is registered, as each pool's hook configuration is stored in the Vault and set at pool registration time. During pool registration, the Vault calls into the Hooks contract and [retrieves](https://github.com/balancer/balancer-v3-monorepo/blob/49553c0546121f7725e0b024b240d6e722f02538/pkg/vault/contracts/VaultExtension.sol#L198) the `HookFlags`. +This decision is final and cannot be changed for a pool once it is registered, as each pool's hook configuration is stored in the Vault and set at pool registration time. During pool registration, the Vault calls into the Hooks contract and [retrieves](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol#L569-L573) the `HookFlags`. -:::info Hooks & reentrancy -It is possible to reenter the Vault as part of a hook execution, as only the internal functions for each operation are reentrancy protected (e.g., `_swap`, `_addLiquidity` & `_removeLiquidity`). +:::info Hooks and reentrancy +It is possible to reenter the Vault as part of a hook execution, as only the internal functions for each operation are reentrancy protected (e.g., `_swap`, `_addLiquidity` and `_removeLiquidity`). ::: -## How Pools & Hooks Are Connected +## How Pools and Hooks Are Connected When a new pool is registered a hook contract address can be passed to "link" the pool and the hook (use the zero address if there is no hook). This configuration is immutable and cannot change after the pool is registered. @@ -82,7 +82,7 @@ function registerPool( ``` ::: info -If you want your Hooks contract to be used, you must implement `onRegister` as the Vault calls it during the [pool registration](https://github.com/balancer/balancer-v3-monorepo/blob/49553c0546121f7725e0b024b240d6e722f02538/pkg/vault/contracts/VaultExtension.sol#L184). The intention of `onRegister` is for the developer to verify that the pool should be allowed to use the hooks contract. +If you want your Hooks contract to be used, you must implement `onRegister` as the Vault calls it during the [pool registration](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol#L144-L166). The intention of `onRegister` is for the developer to verify that the pool should be allowed to use the hooks contract. ::: Afterwards the pool is linked to the hook via the `_hooksContracts` mapping, shown below. @@ -95,7 +95,7 @@ mapping(address pool => IHooks hooksContract) internal _hooksContracts; ## Adjusted amounts - using hooks to change `amountCalculated`. -Remember that pool liquidity operations like `swap`, `addLiquidity` and `removeLiquidity` signal to the Vault the entries on the credit & debt tab. These entries can either be calculated as part of custom pool implementations or pools in combination with hooks. Both have the capability to determine the amount of credit & debt the vault adds to the tab. +Remember that pool liquidity operations like `swap`, `addLiquidity` and `removeLiquidity` signal to the Vault the entries on the credit and debt tab. These entries can either be calculated as part of custom pool implementations or pools in combination with hooks. Both have the capability to determine the amount of credits and debts the vault adds to the tab. The reason hooks also have this capability is to change `amountCalculated` for existing pool types from established factories. This allows for more fine-grained pool tuning capabilities in `after` hooks. ![Vault-Pool-Hooks relation](/images/hook-delta.png) diff --git a/docs/concepts/core-concepts/pool-creator-fee.md b/docs/concepts/core-concepts/pool-creator-fee.md index 093c7403..4c8da41c 100644 --- a/docs/concepts/core-concepts/pool-creator-fee.md +++ b/docs/concepts/core-concepts/pool-creator-fee.md @@ -4,7 +4,7 @@ title: Pool Creator Fee --- ## Pool Creator Fee -Introducing the Pool Creator Fee—a groundbreaking feature within the Balancer Protocol that revolutionizes the way developers engage with liquidity pools. With this innovative concept, developers of specific pools have the opportunity to earn a share of the swap fee & yield fee as revenue, incentivizing the creation of successful and thriving pools. In the following section, we delve into the details of this exciting feature, exploring how it works and its implications for pool creators. +Introducing the Pool Creator Fee—a groundbreaking feature within the Balancer Protocol that revolutionizes the way developers engage with liquidity pools. With this innovative concept, developers of specific pools have the opportunity to earn a share of the swap fee and yield fee as revenue, incentivizing the creation of successful and thriving pools. In the following section, we delve into the details of this exciting feature, exploring how it works and its implications for pool creators. ## Implementation @@ -32,11 +32,11 @@ The accrued creator fees can only be claimed by the `poolCreator`, as the functi * @param pool The pool on which fees were collected * @param recipient Address to send the tokens */ -function withdrawPoolCreatorFees(address pool, address recipient) external; +function withdrawPoolCreatorFees(address pool, address recipient) external onlyPoolCreator(pool); ``` This collects the entire amount of accrued creator fees. It is not possible to claim creator swap or yield fees separately. -Note that there is also a permissionless version, without a recipient, that sends the fees to the registered pool creator. +Note that there is also a permissionless version, without a recipient, that sends the fees to the registered pool creator. If this address is a contract, that means anyone can send tokens to that contract at any time, so you must ensure they can be withdrawn! ```solidity /** @@ -62,8 +62,8 @@ function getPoolCreatorFeeAmounts(address pool) external view returns (uint256[] ### Setting The Fee Appropriately -Developers must carefully consider their decisions regarding the creator fee, as increasing this fee reduces the portion of swap & yield fees allocated to Liquidity Providers. While higher creator fees may increase revenue for the creator, they can also diminish incentives for Liquidity Providers to participate, potentially resulting in reduced liquidity and overall fee generation within the pool. +Developers must carefully consider their decisions regarding the creator fee, as increasing this fee reduces the portion of swap and yield fees allocated to Liquidity Providers. While higher creator fees may increase revenue for the creator, they can also diminish incentives for Liquidity Providers to participate, potentially resulting in reduced liquidity and overall fee generation within the pool. -The pool creator can set the fees by calling the [`ProtocolFeeController`'s](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/ProtocolFeeController.sol) `setPoolCreatorYieldFeePercentage` & `setPoolCreatorSwapFeePercentage`. +The pool creator can set the fees by calling the [`ProtocolFeeController`'s](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/ProtocolFeeController.sol) `setPoolCreatorYieldFeePercentage` and `setPoolCreatorSwapFeePercentage`. -The maximum `poolCreatorFeePercentage` for both types is 100% (`100e18`). Note that this percentage is net protocol fees, which are paid first. As described above, a 100% fee would leave nothing for LPs. \ No newline at end of file +The maximum `poolCreatorFeePercentage` for both types is 100% (`1e18`). Note that this percentage is net protocol fees, which are paid first. As described above, a 100% fee would leave nothing for LPs. And in practice, depending on the configuration of other fees, it might revert (e.g., exact out calculations divide by "1-fee"). \ No newline at end of file diff --git a/docs/concepts/core-concepts/pool-role-accounts.md b/docs/concepts/core-concepts/pool-role-accounts.md index dc550a8f..8d2d2ebf 100644 --- a/docs/concepts/core-concepts/pool-role-accounts.md +++ b/docs/concepts/core-concepts/pool-role-accounts.md @@ -5,7 +5,7 @@ title: Pool Role Accounts # Pool Role Permissions -During pool registration `PoolRoleAccounts` are immutably set. These addresses have permission to change certain pool settings: +During pool registration `PoolRoleAccounts` are set immutably. These addresses have permission to change certain pool settings: ```solidity struct PoolRoleAccounts { From 09fc3350d108105003a1efd2d98009ff06f321d7 Mon Sep 17 00:00:00 2001 From: Jeff Bennett Date: Thu, 3 Oct 2024 18:04:42 -0400 Subject: [PATCH 5/8] docs: concepts --- .../boosted-pool.md | 2 +- .../stable-pool/stable-math.md | 4 +-- .../weighted-pool/weighted-math.md | 2 +- .../weighted-pool/weighted-pool.md | 3 ++ docs/concepts/router/overview.md | 7 ++++- docs/concepts/router/queries.md | 18 ++++++++--- docs/concepts/router/technical.md | 1 - docs/concepts/vault/README.md | 3 +- .../vault/add-remove-liquidity-types.md | 2 +- docs/concepts/vault/erc20-multi-token.md | 4 +-- .../liquidity-invariant-approximation.md | 4 +-- docs/concepts/vault/swap-fee.md | 10 +++--- docs/concepts/vault/token-scaling.md | 31 ++++++++++--------- docs/concepts/vault/token-types.md | 6 ++-- docs/concepts/vault/transient-accounting.md | 23 +++++++------- docs/concepts/vault/yield-fee.md | 4 +-- 16 files changed, 70 insertions(+), 54 deletions(-) diff --git a/docs/concepts/explore-available-balancer-pools/boosted-pool.md b/docs/concepts/explore-available-balancer-pools/boosted-pool.md index cb4d0836..9350af76 100644 --- a/docs/concepts/explore-available-balancer-pools/boosted-pool.md +++ b/docs/concepts/explore-available-balancer-pools/boosted-pool.md @@ -18,7 +18,7 @@ Balancer v3 boosted pools allow for 100% of an LP position to be considered boos #### Gas efficient swaps between boosted pool's base assets Boosted pools leverage the Vault's [liquidity buffers](/concepts/vault/buffer.html#erc4626-liquidity-buffers) concept to facilitate gas-efficient swaps between boosted pools. This allows LPs to maintain 100% boosted pool positions and still earn swap fees from base-asset to base-asset trades. -#### Boosted pools are "plug & play" for ERC4626 Vaults +#### Boosted pools are "plug and play" for ERC4626 Vaults Any token that complies with the ERC4626 standard can easily become an asset within a boosted pool and can be swapped on Balancer gas efficiently while keeping swap prices competitive due to [liquidity buffers](/concepts/vault/buffer.html#erc4626-liquidity-buffers). #### Great way for a DAO to facilitate liquidity for their token product diff --git a/docs/concepts/explore-available-balancer-pools/stable-pool/stable-math.md b/docs/concepts/explore-available-balancer-pools/stable-pool/stable-math.md index f3f1ef5c..8b77b681 100644 --- a/docs/concepts/explore-available-balancer-pools/stable-pool/stable-math.md +++ b/docs/concepts/explore-available-balancer-pools/stable-pool/stable-math.md @@ -12,7 +12,7 @@ Stable Math is designed to allow for swaps between any assets that have the same ### TypeScript -Developers can use the TypeScript math implementations used by the Smart Order router +Developers can use the TypeScript math implementations used by the Smart Order router (equivalent v2 reference). - [stableMath.ts](https://github.com/balancer/balancer-sor/blob/john/v2-package-linear/src/pools/stablePool/stableMath.ts) - [metaStableMath.ts](https://github.com/balancer/balancer-sor/blob/john/v2-package-linear/src/pools/metaStablePool/metaStableMath.ts) @@ -34,7 +34,7 @@ Where: ## Swap Equations -Similar to determining the invariant, determining (out/in) amount given (in/out) amounts is also done iteratively. Both [outGivenIn](https://github.com/georgeroman/balancer-v2-pools/blob/db415173277bfa86d9aa6b0c1fbd15481c7a2398/src/pools/stable/math.ts#L88) and [inGivenOut](https://github.com/georgeroman/balancer-v2-pools/blob/db415173277bfa86d9aa6b0c1fbd15481c7a2398/src/pools/stable/math.ts#L138) use the same function, [getTokenBalanceGivenInvariantAndAllOtherBalances](https://github.com/georgeroman/balancer-v2-pools/blob/db415173277bfa86d9aa6b0c1fbd15481c7a2398/src/pools/stable/math.ts#L502). +Similar to determining the invariant, determining (out/in) amount given (in/out) amounts is also done iteratively. Both [outGivenIn](https://github.com/georgeroman/balancer-v2-pools/blob/db415173277bfa86d9aa6b0c1fbd15481c7a2398/src/pools/stable/math.ts#L88) and [inGivenOut](https://github.com/georgeroman/balancer-v2-pools/blob/db415173277bfa86d9aa6b0c1fbd15481c7a2398/src/pools/stable/math.ts#L138) use the same function, [getTokenBalanceGivenInvariantAndAllOtherBalances](https://github.com/georgeroman/balancer-v2-pools/blob/db415173277bfa86d9aa6b0c1fbd15481c7a2398/src/pools/stable/math.ts#L502). Note that these are v2 references; we don't use TS in this way in v3, and `computeBalance` is equivalent to `getTokenBalanceGivenInvariantAndAllOtherBalances` in v2. Otherwise, they are mathematically equivalent. ### outGivenIn diff --git a/docs/concepts/explore-available-balancer-pools/weighted-pool/weighted-math.md b/docs/concepts/explore-available-balancer-pools/weighted-pool/weighted-math.md index 1e277542..ce02fda5 100644 --- a/docs/concepts/explore-available-balancer-pools/weighted-pool/weighted-math.md +++ b/docs/concepts/explore-available-balancer-pools/weighted-pool/weighted-math.md @@ -16,7 +16,7 @@ For more formulas and derivations of the below formulas, please refer to the [Ba ### TypeScript -Developers can use the TypeScript math implementations used by the Smart Order router +Developers can use the TypeScript math implementations used by the Smart Order router (equivalent v2 reference). - [weightedMath.ts](https://github.com/balancer/balancer-sor/blob/john/v2-package-linear/src/pools/weightedPool/weightedMath.ts) diff --git a/docs/concepts/explore-available-balancer-pools/weighted-pool/weighted-pool.md b/docs/concepts/explore-available-balancer-pools/weighted-pool/weighted-pool.md index 5bb55763..9fa09e8c 100644 --- a/docs/concepts/explore-available-balancer-pools/weighted-pool/weighted-pool.md +++ b/docs/concepts/explore-available-balancer-pools/weighted-pool/weighted-pool.md @@ -20,8 +20,11 @@ Weighted Pools have additional security constraints based on Weighted Math. (The - Weights must sum to 100% - Swaps amounts cannot exceed 30% of the token balance - The invariant cannot decrease below 70% or increase beyond 300% on liquidity operations +- The swap fee must be between 0.001% and 10%. (Note that the lower limit is higher than in v2.) ::: +Note that the swap fee and invariant limits are defined in `WeightedPool` through implementing the `ISwapFeePercentageBounds` and `IUnbalancedLiquidityInvariantRatioBounds` interfaces, which are included in `IBasePool`. + ::: chart Weighted Pool ```json diff --git a/docs/concepts/router/overview.md b/docs/concepts/router/overview.md index aabc6156..1b960519 100644 --- a/docs/concepts/router/overview.md +++ b/docs/concepts/router/overview.md @@ -31,6 +31,11 @@ Balancer has developed, audited and deployed Router contracts with the goal of p - [API](../../developer-reference/contracts/batch-router-api.md) - [Code](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/BatchRouter.sol) -Additionally both Routers expose [Query Functions](./queries.md) providing the ability to query the result of an operation using the latest onchain state. +### Composite Liquidity Router +- ERC4626 buffer and nested swaps +- [API](../../developer-reference/contracts/composite-liquidity-router-api.md) +- [Code](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/CompositeLiquidityRouter.sol) + +Additionally, all Routers expose [Query Functions](./queries.md) providing the ability to query the result of an operation using the latest onchain state. Latest deployment of the Routers can be found in the [deployments section](/developer-reference/contracts/deployment-addresses/mainnet.html). diff --git a/docs/concepts/router/queries.md b/docs/concepts/router/queries.md index db9532e3..03d0f656 100644 --- a/docs/concepts/router/queries.md +++ b/docs/concepts/router/queries.md @@ -7,6 +7,7 @@ Queries provide the ability to simulate an operation and find its result without ## Router queries The detailed Router API description can be found in the [Router API section](/concepts/router/onchain-api/router-api.html). +- `queryAddLiquidityProportional` - `queryAddLiquidityUnbalanced` - `queryAddLiquiditySingleTokenExactOut` - `queryAddLiquidityCustom` @@ -14,12 +15,9 @@ The detailed Router API description can be found in the [Router API section](/co - `queryRemoveLiquiditySingleTokenExactIn` - `queryRemoveLiquiditySingleTokenExactOut` - `queryRemoveLiquidityCustom` -- `queryRemoveLiquidityCustom` - `queryRemoveLiquidityRecovery` - `querySwapSingleTokenExactIn` - `querySwapSingleTokenExactOut` -- `querySwapExactIn` -- `querySwapExactOut` ## Batch Router queries The detailed Router API description can be found in the [Batch Router API section](/concepts/router/onchain-api/batch-router-api.html). @@ -27,9 +25,19 @@ The detailed Router API description can be found in the [Batch Router API sectio - `querySwapExactIn` - `querySwapExactOut` +## Composite Liquidity Router queries + +The detailed Router API description can be found in the [Composite Liquidity Router API section](/concepts/router/onchain-api/composite-liquidity-router-api.html). + +- `queryAddLiquidityUnbalancedToERC4626Pool` +- `queryAddLiquidityProportionalToERC4626Pool` +- `queryRemoveLiquidityProportionalFromERC4626Pool` +- `queryAddLiquidityUnbalancedNestedPool` +- `queryRemoveLiquidityProportionalNestedPool` + ## Complex queries -The Router and Batch Router are primarily used as entrypoints for standard queries. However, Balancer's design allows for a more flexible querying mechanism. Any Vault operation that includes a `onlyWhenUnlocked` modifier can be queried natively. +The Router contracts are primarily used as entrypoints for standard queries. However, Balancer's design allows for a more flexible querying mechanism. Any Vault operation that includes a `onlyWhenUnlocked` modifier can be queried natively. ### Quote This is facilitated through a `quote` mechanism. The concept of Transient Accounting enables the querying of complex Vault operations. To execute a query, the Router invokes the `quote` function on the Vault. @@ -38,4 +46,4 @@ The Vault requires that any invocation of `quote` be executed as a staticcall (e ### quoteAndRevert -Since `quote` changes the Vault state some query combinations are not possible. For example, if you wanted to quote `querySwapExactIn` for POOL_A but also `querySwapExactOut` for POOL_A in its initial state, you would have to use `quoteAndRevert`. In this variant, the call always reverts and returns the result in the revert data (similar to the v2 mechanism). \ No newline at end of file +Since `quote` changes the Vault state some query combinations are not possible. For example, if you wanted to quote `querySwapExactIn` for POOL_A but also `querySwapExactOut` for POOL_A in its initial state, you would have to use `quoteAndRevert`. In this variant, the call always reverts and returns the result in the revert data (similar to the v2 mechanism). diff --git a/docs/concepts/router/technical.md b/docs/concepts/router/technical.md index 91ade002..9381395e 100644 --- a/docs/concepts/router/technical.md +++ b/docs/concepts/router/technical.md @@ -16,4 +16,3 @@ Every user interaction going through the Router follows the same pattern of exec 1. The Router calls `unlock` on the Vault, allowing access to protected state-changing functions that perform token [accounting](../vault/transient-accounting.md) by triggering the `transient` modifier. You can think of this step as the Router opening a tab with the Vault, after which any debts or credits from subsequent Vault operations accrue to that tab. 2. The Router executes a hook function (ie: `swapSingleTokenHook`) which calls the Vault's primitives (ie: `swap`). These operations incur debt or supply credit to the currently open tab. 3. To finalize the user operation, all outstanding debts and credits accrued during `swap` need to be settled. If the tab is not settled, the transaction will revert. This settlement step (i.e., when `unlock` returns and executes the rest of the `transient` modifier) closes out the tab opened with the Vault in step 1. - diff --git a/docs/concepts/vault/README.md b/docs/concepts/vault/README.md index 3c098fdb..f52e01dd 100644 --- a/docs/concepts/vault/README.md +++ b/docs/concepts/vault/README.md @@ -13,8 +13,7 @@ The Vault is the core of the Balancer protocol; it is a smart contract that hold First introduced in Balancer v2, the vault architecture separates token accounting from pool logic, allowing for simplified pool contracts that focus on the implementation of their swap, add liquidity and remove liquidity logic. -This architecture brings different pool designs under the same umbrella; the Vault is agnostic to pool math and can accommodate any system that satisfies a few requirements. Anyone who comes up with a novel idea can develop a custom pool -plugged directly into Balancer's existing liquidity instead of needing to build their own Decentralized Exchange. +This architecture brings different pool designs under the same umbrella; the Vault is agnostic to pool math and can accommodate any system that satisfies a few requirements. Anyone who comes up with a novel idea can develop a custom pool plugged directly into Balancer's existing liquidity instead of needing to build their own Decentralized Exchange. In v3, the vault more formally defines the requirements of a pool contract, shifting core design patterns out of the pool and into the vault. The result is pool contracts that are compact and much easier to reason about. diff --git a/docs/concepts/vault/add-remove-liquidity-types.md b/docs/concepts/vault/add-remove-liquidity-types.md index 7851a3b1..4b7e6709 100644 --- a/docs/concepts/vault/add-remove-liquidity-types.md +++ b/docs/concepts/vault/add-remove-liquidity-types.md @@ -8,7 +8,7 @@ title: Add/Remove liquidity types Balancer protocol leverages the [Liquidity invariant approximation](/concepts/vault/liquidity-invariant-approximation.html) to provide a generalized solution for add and remove liquidity operations. This enables the Vault to implement complex `unbalanced` and `singleAsset` liquidity operations that all custom AMMs built on Balancer support by default. -The Vault's [`addLiquidity`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IVaultMain.sol#L60-L72) and [`removeLiquidity`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IVaultMain.sol#L78-L91) functions accept a `kind` argument that identifies the type of operation to be performed. As each `kind` has slightly different requirements, the argument impacts +The Vault's [`addLiquidity`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IVaultMain.sol#L93-L95) and [`removeLiquidity`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IVaultMain.sol#L112-L114) functions accept a `kind` argument that identifies the type of operation to be performed. As each `kind` has slightly different requirements, the argument impacts how the other function arguments are interpreted. If you're an integrator looking to implement add or remove liquidity for an existing pool, see the [Router Onchain API](/concepts/router/overview.html). diff --git a/docs/concepts/vault/erc20-multi-token.md b/docs/concepts/vault/erc20-multi-token.md index c1d2a6ec..a86b14aa 100644 --- a/docs/concepts/vault/erc20-multi-token.md +++ b/docs/concepts/vault/erc20-multi-token.md @@ -48,8 +48,8 @@ function _approve(address token, address owner, address spender, uint256 amount) ## Where is the public interface? You'll notice that [ERC20MultiToken.sol](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/token/ERC20MultiToken.sol) contains only internal functions. -You can find the public interface defined in [IVaultExtension.sol](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IVaultExtension.sol#L160-L223) and implemented in [VaultExtension.sol](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol). -To ensure that the state changing public interface is always delegate-called by the vault, each function has the [onlyVaultDelegateCall](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol#L75) modifier. +You can find the public interface defined in [IVaultExtension.sol](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IVaultExtension.sol#L231-L290) and implemented in [VaultExtension.sol](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol#L589-L630). +To ensure that the state changing public interface is always delegate-called by the vault, each function has the [onlyVaultDelegateCall](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol#L69-L72) modifier. ```solidity function approve(address owner, address spender, uint256 amount) external onlyVaultDelegateCall returns (bool) { diff --git a/docs/concepts/vault/liquidity-invariant-approximation.md b/docs/concepts/vault/liquidity-invariant-approximation.md index 8f039856..f93b9bb5 100644 --- a/docs/concepts/vault/liquidity-invariant-approximation.md +++ b/docs/concepts/vault/liquidity-invariant-approximation.md @@ -28,9 +28,9 @@ This methodology and evaluation criteria apply to all disproportionate liquidity ::: info Custom pool implication As a custom pool developer, you are faced with the choice of allowing these combinations in your pool. If you chose to do so, you must verify -the liquidity-invariant approximation approach in your tests to ensure fair & even settlement across various operations. +the liquidity-invariant approximation approach in your tests to ensure fair and even settlement across various operations. ::: ## Verification -For reference, see the [`LiquidityApproximation.t.sol`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/test/foundry/LiquidityApproximation.t.sol#L682) test file where the required assertions are verified in `assertLLiquidityOperationNoSwapFee()` and `assertLiquidityOperation()`. +For reference, see the [`LiquidityApproximation.t.sol`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/test/foundry/LiquidityApproximation.t.sol) test file where the required assertions are verified in `assertLiquidityOperationNoSwapFee()` and `assertLiquidityOperation()`. diff --git a/docs/concepts/vault/swap-fee.md b/docs/concepts/vault/swap-fee.md index 09669c1a..98e2cd38 100644 --- a/docs/concepts/vault/swap-fee.md +++ b/docs/concepts/vault/swap-fee.md @@ -3,7 +3,7 @@ title: Swap Fee order: 8 --- # Swap fee -A swap fee is charged for each swap, as well as on the non-proportional amounts in add/remove liquidity operations. When a pool is registered, the initial swap fee is passed as a parameter and stored as part of [the pool's configuration](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L21). The swap fee is always charged on the calculated amount (i.e., on `amountOut` for EXACT_IN, and `amountIn` for EXACT_OUT). +A swap fee is charged for each swap, as well as on the non-proportional amounts in add/remove liquidity operations. When a pool is registered, the initial swap fee is passed as a parameter and stored as part of [the pool's configuration](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L28-L39). The swap fee is always charged on the calculated amount (i.e., on `amountOut` for EXACT_IN, and `amountIn` for EXACT_OUT). :::info Let's imagine a liquidity pool that maintains an equal balance of DAI and USDC, known as a 50/50 pool. A user decides to add liquidity to this pool, but does so in an unbalanced manner: they contribute 15 DAI and 10 USDC. @@ -20,7 +20,7 @@ Users who have been granted authorization have the capability to set a fixed swa If users prefer not to have the swap fee of a pool controlled by Balancer governance (through the `Authorizer`), they can opt out by providing a non-zero address for the `swapManager`. This address could be a multi-sig or custom contract. -Unlike in v2, v3 does not impose limits on the swap fee percentage at the Vault level. Rather, these limits are set at the pool level (0.0001% - 10% for standard Balancer Weighted and Stable pools.) +Unlike in v2, v3 does not impose limits on the swap fee percentage at the Vault level. Rather, these limits are set at the pool level (0.001% - 10% for standard Balancer Weighted, and 0.0001% - 10% for Stable pools). ## Swap fees by pool type. Different types of pools can have varying minimum and maximum swap fees. These variations are determined by the mathematical security properties and specific product requirements. Maximum and minimum swap fees are set on a per pool basis implemented via the `ISwapFeePercentageBounds` interface, which is inherited by `IBasePool`: @@ -31,12 +31,12 @@ Different types of pools can have varying minimum and maximum swap fees. These v function getMaximumSwapFeePercentage() external view returns (uint256); ``` -This means that all new pool types (assuming they implement `IBasePool`) will need to think about what the swap fee range should be, according to the pool type's math and other constraints, then override and set the values accordingly. +This means that all new pool types (assuming they implement `IBasePool`) will need to think about what the swap fee range should be, according to the pool type's math and other constraints, then override and set the values accordingly. (Note that invariant limits must also be defined for new pool types, by implementing `IUnbalancedLiquidityInvariantRatioBounds`.) ## Dynamic swap fee Liquidity pools can be set up to use dynamic swap fees. When registering a pool with a dynamic swap fee, `shouldCallComputeDynamicSwapFee` should be true in the HooksConfig. -Instead of getting the swap fee from the [pool's configuration](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L33), the Vault uses the [`onComputeDynamicSwapFeePercentage()`](/developer-reference/contracts/hooks-api.html#oncomputedynamicswapfeepercentage) hook to fetch the dynamic swap fee from the pool. This function returns the swap fee percentage to be used for the current swap. It's important to note that even when a pool is set to use dynamic swap fees, it still maintains a static swap fee, which is not directly used (though it is sent to the dynamic fee hook for reference) +Instead of getting the swap fee from the [pool's configuration](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L28-L39), the Vault uses the [`onComputeDynamicSwapFeePercentage()`](/developer-reference/contracts/hooks-api.html#oncomputedynamicswapfeepercentage) hook to fetch the dynamic swap fee from the pool. This function returns the swap fee percentage to be used for the current swap. It's important to note that even when a pool is set to use dynamic swap fees, it still maintains a static swap fee, which is not directly used (though it is sent to the dynamic fee hook for reference). :::info The capability to compute dynamic swap fee percentages opens up new and creative ways to calculate fees. For example, the fees can be adjusted depending on the swap direction, or configured to maintain a token's pegged value. @@ -46,4 +46,4 @@ In addition to these, dynamic swap fees can also be used to: - Adjust fees based on market conditions: higher fees can be charged during periods of high volatility to discourage frequent trading and maintain stability. - Implement tiered fee structures: different fees can be charged based on the size of the swap, with larger swaps incurring higher fees. - Encourage certain types of trading behavior: lower fees can be set for trades that contribute to the pool's liquidity or stability. -::: \ No newline at end of file +::: diff --git a/docs/concepts/vault/token-scaling.md b/docs/concepts/vault/token-scaling.md index 7e966d6a..4554b776 100644 --- a/docs/concepts/vault/token-scaling.md +++ b/docs/concepts/vault/token-scaling.md @@ -16,28 +16,31 @@ amount of complexity from the pool and allowing it to focus primarily on its inv All token balances and input values are scaled to 18 decimal places prior to being sent to the [Pool](/concepts/explore-available-balancer-pools/). Once scaled, these numbers are referred to internally as `scaled18`. ### Pool registration -During pool registration, the vault stores the `tokenDecimalDiffs` for each token in the pool in the `PoolConfig` bits. Refer to the full implementation [here](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol#L239). +During pool registration, the vault stores the `tokenDecimalDiffs` for each token in the pool in the `PoolConfig` bits. Refer to the full implementation [here](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol#L230). ```solidity tokenDecimalDiffs[i] = uint8(18) - IERC20Metadata(address(token)).decimals(); ``` -A token with 6 decimals (USDC) would have a `tokenDecimalDiff = 18 - 6 = 12`, and a token with 18 decimals (WETH) would have a `tokenDecimalDiff = 18 - 18 = 0`. +A token with 6 decimals (USDC) would have a `tokenDecimalDiff = 18 - 6 = 12`, and a token with 18 decimals (WETH) would have a `tokenDecimalDiff = 18 - 18 = 0`. Note that tokens with more than 18 decimals would revert here with an arithmetic error. ### Scaling factors -The `tokenDecimalDiffs` are then used to calculate the decimal `scalingFactors` for each token. This implementation can be found in the [PoolConfigLib](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/lib/PoolConfigLib.sol#L214-L230). +The `tokenDecimalDiffs` are then used to calculate the decimal `scalingFactors` for each token. This implementation can be found in the [PoolConfigLib](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/lib/PoolConfigLib.sol#L240-L259). ```solidity function getDecimalScalingFactors( - PoolConfig memory config, + PoolConfigBits memory config, uint256 numTokens ) internal pure returns (uint256[] memory) { uint256[] memory scalingFactors = new uint256[](numTokens); - bytes32 tokenDecimalDiffs = bytes32(uint256(config.tokenDecimalDiffs)); + bytes32 tokenDecimalDiffs = bytes32(uint256(config.getTokenDecimalDiffs())); - for (uint256 i = 0; i < numTokens; i++) { - uint256 decimalDiff = tokenDecimalDiffs.decodeUint(i * _DECIMAL_DIFF_BITLENGTH, _DECIMAL_DIFF_BITLENGTH); + for (uint256 i = 0; i < numTokens; ++i) { + uint256 decimalDiff = tokenDecimalDiffs.decodeUint( + i * PoolConfigConst.DECIMAL_DIFF_BITLENGTH, + PoolConfigConst.DECIMAL_DIFF_BITLENGTH + ); // This is equivalent to `10**(18+decimalsDifference)` but this form optimizes for 18 decimal tokens. scalingFactors[i] = FixedPoint.ONE * 10 ** decimalDiff; @@ -50,7 +53,7 @@ function getDecimalScalingFactors( ### References To review the scaling implementations, refer to [ScalingHelpers.sol](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/solidity-utils/contracts/helpers/ScalingHelpers.sol). -You can review the logic flow of [swap](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/Vault.sol#L165), [addLiquidity](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/Vault.sol#L487) and [removeLiquidity](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/Vault.sol#L709) +You can review the logic flow of [swap](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/Vault.sol#L181-L275), [addLiquidity](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/Vault.sol#L489-L572) and [removeLiquidity](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/Vault.sol#L761-L841) to better understand how the vault manages token scaling. ## Rate scaling @@ -73,16 +76,16 @@ A token's rate is defined as an 18-decimal fixed point number. It represents the ### Creating a pool with tokens that have rates -On pool [register](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IVaultExtension.sol#L77) a [TokenConfig](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L127) is provided for each of the pool's tokens. -To define a token with a rate, specify the token type as `TokenType.WITH_RATE`. Additionally, you must provide a `rateProvider` address that implements the [`IRateProvider`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IRateProvider.sol) interface. Refer to [Token types](/concepts/vault/token-types.html) for a detailed explanation on each token type. +On pool [register](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IVaultExtension.sol#L97-L106) a [TokenConfig](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L142-L147) is provided for each of the pool's tokens. +To define a token with a rate, specify the token type as `TokenType.WITH_RATE`. Additionally, you must provide a `rateProvider` address that implements the [`IRateProvider`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/solidity-utils/helpers/IRateProvider.sol) interface. Refer to [Token types](/concepts/vault/token-types.html) for a detailed explanation on each token type. ### Rate scaling usage Rate scaling is used on every `swap`, `addLiquidity` and `removeLiquidity` operation. If the token was registered as `TokenType.WITH_RATE`, an external call to the Rate Provider is made via `getRate`. If the `TokenType.STANDARD` was selected, the rate is always `1e18`. These rates are used to upscale the `amountGiven` in the Vault primitives. :::info -1. With a swap, the known token amount is given in native decimals as [`amountGivenRaw`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L183) -2. [`AmountGivenRaw` is upscaled](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/Vault.sol#L303-L320) -3. [`AmountGivenScaled18`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/Vault.sol#L358) is forwarded to the pool. -4. Rates are [undone](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/Vault.sol#L378) before calculating and returning either `amountIn` or `amountOut`. +1. With a swap, the known token amount is given in native decimals as [`amountGivenRaw`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L223) +2. `AmountGivenRaw` is upscaled +3. `AmountGivenScaled18` is forwarded to the pool. +4. Rates are undone before calculating and returning either `amountIn` or `amountOut`. ::: You can read more on the [Rate Providers page](/concepts/core-concepts/rate-providers.html). diff --git a/docs/concepts/vault/token-types.md b/docs/concepts/vault/token-types.md index 9d180c29..669d6c0c 100644 --- a/docs/concepts/vault/token-types.md +++ b/docs/concepts/vault/token-types.md @@ -16,7 +16,7 @@ Tokens should be defined as `WITH_RATE` when they have an externally available e See [Rate scaling](./token-scaling.md#rate-scaling) For an in-depth explanation on how Balancer manages tokens with rates. -When [registering](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol#L138) a token as `WITH_RATE`, your [`TokenConfig`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L84-L89) should resemble the following: +When [registering](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol#L144-L166) a token as `WITH_RATE`, your [`TokenConfig`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L84-L89) should resemble the following: ```solidity TokenConfig({ token: 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0, @@ -26,14 +26,14 @@ TokenConfig({ }) ``` -::: info What does paysYieldFees mean? +::: info What does `paysYieldFees` mean? paysYieldFees means that a portion of the yield a specific token accrues is used to fund Balancer DAO operations. Similar to how swap fees accrue to the Balancer treasury. ::: ## All other tokens (`STANDARD`) Any token that is not `WITH_RATE` should be set as `STANDARD`. -When [registering](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol#L138) a token as `STANDARD`, your [`TokenConfig`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L84-L89) should resemble the following: +When [registering](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/VaultExtension.sol#L144-L166) a token as `STANDARD`, your [`TokenConfig`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/VaultTypes.sol#L84-L89) should resemble the following: ```solidity TokenConfig({ token: 0xba100000625a3754423978a60c9317c58a424e3D, diff --git a/docs/concepts/vault/transient-accounting.md b/docs/concepts/vault/transient-accounting.md index 761d5d61..3519a973 100644 --- a/docs/concepts/vault/transient-accounting.md +++ b/docs/concepts/vault/transient-accounting.md @@ -16,6 +16,7 @@ Upon activation of the transient state, the vault is unlocked and permissions to - `addLiquidity`: Adds one or more tokens to a liquidity pool. - `removeLiquidity`: Removes one or more tokens from a liquidity pool. - `erc4626BufferWrapOrUnwrap`: Wraps/unwraps tokens based on provided parameters. +- `initializeBuffer`: Initializes an ERC4626 buffer. This sets the underlying asset token, and is necessary to enable use of the buffer. - `addLiquidityToBuffer`: Adds liquidity to an ERC4626 buffer. - `removeLiquidityFromBuffer`: Removes liquidity from an ERC4626 buffer. - `initialize`: Initialize a liquidity pool. @@ -76,21 +77,19 @@ function _accountDelta(IERC20 token, int256 delta) internal { // Calculate the new delta after accounting for the change. int256 next = current + delta; - unchecked { - // If the resultant delta becomes zero after this operation, - // decrease the count of non-zero deltas. - if (next == 0) { - _nonZeroDeltaCount().tDecrement(); - } - // If there was no previous delta (i.e., it was zero) and now we have one, - // increase the count of non-zero deltas. - else if (current == 0) { - _nonZeroDeltaCount().tIncrement(); - } + // If the resultant delta becomes zero after this operation, + // decrease the count of non-zero deltas. + if (next == 0) { + _nonZeroDeltaCount().tDecrement(); + } + // If there was no previous delta (i.e., it was zero) and now we have one, + // increase the count of non-zero deltas. + else if (current == 0) { + _nonZeroDeltaCount().tIncrement(); } // Update the delta for this token. _tokenDeltas().tSet(token, next); } ``` -This transient accounting approach starts tracking token balances at the beginning of an operation (when opening a tab) and stops at the end (when closing the tab), providing full flexibility for any token amount changes in between. This eliminates the need for additional balance management elsewhere, allowing for a clear separation between token accounting and execution logic. Before closing the temporary state, the only requirement is to ensure that `_nonZeroDeltaCount()` equals 0. \ No newline at end of file +This transient accounting approach starts tracking token balances at the beginning of an operation (when opening a tab) and stops at the end (when closing the tab), providing full flexibility for any token amount changes in between. This eliminates the need for additional balance management elsewhere, allowing for a clear separation between token accounting and execution logic. Before closing the temporary state, the only requirement is to ensure that `_nonZeroDeltaCount()` equals 0. diff --git a/docs/concepts/vault/yield-fee.md b/docs/concepts/vault/yield-fee.md index 8af16bf7..369dcd5e 100644 --- a/docs/concepts/vault/yield-fee.md +++ b/docs/concepts/vault/yield-fee.md @@ -17,10 +17,10 @@ Yield fees are charged on every state changing Vault interaction if: - the token is configured as a [WITH_RATE](token-types.md#tokens-with-external-rates-with_rate) type with `paysYieldFees` set to `true` - yield has accrued since the last fee computation -Yield fees are computed by taking the difference between `currentLiveBalance` and `lastLiveBalance` and multiplying it by the `aggregateYieldFeePercentage`. The `aggregateYieldFeePercentage` is set by Balancer governance, and is defined in the [`ProtocolFeeController`](https://github.com/balancer/balancer-v3-monorepo/blob/10079235a0fec9cf52c53cf6f231b615fa297ab2/pkg/vault/contracts/ProtocolFeeController.sol#L61). Note the use of live balances, which are described [here](./token-scaling.md#live-balances). +Yield fees are computed by taking the difference between `currentLiveBalance` and `lastLiveBalance` and multiplying it by the `aggregateYieldFeePercentage`. The `aggregateYieldFeePercentage` is set by Balancer governance, and is defined in the [`ProtocolFeeController`](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/vault/contracts/ProtocolFeeController.sol). Note the use of live balances, which are described [here](./token-scaling.md#live-balances). :::info What does `aggregate` mean? -Collected fees are allocated between up to three recipients: the Balancer protocol, the pool creator (if defined on registration), and the LPs. The `ProtocolFeeController` is used to set the percentages for swap and yield fees for both the protocol and pool creators. In general, the protocol fee is collected first, and the remaining balance is split between the pool creator and LPs. See [this interface](https://github.com/balancer/balancer-v3-monorepo/blob/e81e91cf850e888675d4f28116df5a29318fde98/pkg/interfaces/contracts/vault/IProtocolFeeController.sol#L140) for an example calculation. +Collected fees are allocated between up to three recipients: the Balancer protocol, the pool creator (if defined on registration), and the LPs. The `ProtocolFeeController` is used to set the percentages for swap and yield fees for both the protocol and pool creators. In general, the protocol fee is collected first, and the remaining balance is split between the pool creator and LPs. See [this interface](https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IProtocolFeeController.sol#L187-L194) for an example calculation. For performance reasons, the Vault stores only the aggregate percentage (i.e., the total fee, to be allocated later by the controller), computes only one "cut," and emits no events on the critical path. Collected aggregate fees can then be permissionlessly transferred to the `ProtocolFeeController`, where they are split between the protocol and pool creator, and available for permissioned withdrawal. The controller emits events that allow off-chain processes to monitor swap and yield fees per pool, albeit not in "real time" on every operation. From 0cbea9896abd50998329326287c70073f609a6fa Mon Sep 17 00:00:00 2001 From: Jeff Bennett Date: Thu, 3 Oct 2024 18:05:00 -0400 Subject: [PATCH 6/8] docs: data-and-analytics --- .../data-and-analytics/balancer-api/balancer-api.md | 4 ++-- .../data-and-analytics/balancer-api/pool-details-with-apr.md | 2 +- .../data-and-analytics/balancer-api/user-pool-join-exits.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/data-and-analytics/data-and-analytics/balancer-api/balancer-api.md b/docs/data-and-analytics/data-and-analytics/balancer-api/balancer-api.md index 93a9831b..848de4ab 100644 --- a/docs/data-and-analytics/data-and-analytics/balancer-api/balancer-api.md +++ b/docs/data-and-analytics/data-and-analytics/balancer-api/balancer-api.md @@ -25,13 +25,13 @@ Further documentation is available on the self documented [api server](https://a # Examples -* [Pools - Get a pool's details including APRs](./pool-details-with-apr.md) +* [Pools - Get a v2 pool's details including APRs](./pool-details-with-apr.md) * [Pools - Pools with TVL greater than $10k](./pools-with-tvl.md) * [Pools - Top 10 pools ordered by TVL](./pools-top-ordered-tvl.md) * [Pools - Get swap events for a pool](./pool-swap-events.md) * [Swap - Query the Smart Order Router (SOR)](./swap-query-sor.md) * [User - Get pool balances for a user](./user-pool-balance.md) -* [User - Get pool join & exits events for a user](./user-pool-join-exits.md) +* [User - Get v2 pool join & exits events for a user](./user-pool-join-exits.md) diff --git a/docs/data-and-analytics/data-and-analytics/balancer-api/pool-details-with-apr.md b/docs/data-and-analytics/data-and-analytics/balancer-api/pool-details-with-apr.md index b8289254..98bc62cf 100644 --- a/docs/data-and-analytics/data-and-analytics/balancer-api/pool-details-with-apr.md +++ b/docs/data-and-analytics/data-and-analytics/balancer-api/pool-details-with-apr.md @@ -2,7 +2,7 @@ title: Pools - Get details including APRs --- -# Get a pool's details including APRs +# Get a v2 pool's details including APRs ```graphql { poolGetPool(id: "0x7f2b3b7fbd3226c5be438cde49a519f442ca2eda00020000000000000000067d", chain:MAINNET) { diff --git a/docs/data-and-analytics/data-and-analytics/balancer-api/user-pool-join-exits.md b/docs/data-and-analytics/data-and-analytics/balancer-api/user-pool-join-exits.md index 70210dfd..34f359b0 100644 --- a/docs/data-and-analytics/data-and-analytics/balancer-api/user-pool-join-exits.md +++ b/docs/data-and-analytics/data-and-analytics/balancer-api/user-pool-join-exits.md @@ -1,8 +1,8 @@ --- -title: User - Get joins & exit events for a pool +title: User - Get joins and exit events for a pool --- -# Get pool join & exits events for a user +# Get v2 pool join and exits events for a user ```graphql { From 2f67121174767ecba6157ad1a20f8d1ecba93f9c Mon Sep 17 00:00:00 2001 From: Jeff Bennett Date: Thu, 3 Oct 2024 18:05:26 -0400 Subject: [PATCH 7/8] docs: developer-reference --- docs/developer-reference/authorizer/README.md | 6 +- .../contracts/error-codes.md | 73 +++++++++-------- .../contracts/hooks-api.md | 26 +++++-- .../contracts/protocol-fee-controller-api.md | 45 ++++------- .../contracts/router-api.md | 15 ++-- .../contracts/vault-api.md | 78 ++++++++++--------- docs/developer-reference/sdk/API.md | 8 +- docs/developer-reference/sdk/README.md | 2 +- 8 files changed, 134 insertions(+), 119 deletions(-) diff --git a/docs/developer-reference/authorizer/README.md b/docs/developer-reference/authorizer/README.md index 48c1f915..292ffef9 100644 --- a/docs/developer-reference/authorizer/README.md +++ b/docs/developer-reference/authorizer/README.md @@ -4,7 +4,11 @@ This section includes all of the permissions currently setup in the Authorizer. - [Mainnet](mainnet.md) - [Arbitrum](arbitrum.md) +- [Avalanche](avalanche.md) - [Optimism](optimism.md) - [Polygon](polygon.md) - [Gnosis](gnosis.md) -- [Görli](goerli.md) \ No newline at end of file +- [Base](base.md) +- [zkEVM](zkevm.md) +- [Görli](goerli.md) +- [Sepolia](sepolia.md) \ No newline at end of file diff --git a/docs/developer-reference/contracts/error-codes.md b/docs/developer-reference/contracts/error-codes.md index 35bd8361..a90e7586 100644 --- a/docs/developer-reference/contracts/error-codes.md +++ b/docs/developer-reference/contracts/error-codes.md @@ -18,7 +18,7 @@ Balancer uses custom errors which provide a convenient and gas-efficient way to | ------- | ----------------------------------------------- | | Disabled| Cannot create a pool after the factory was disabled | -## IERC20Multitoken +## IERC20MultitokenErrors | Error | Comment | | ---------------- | ----------------------------------------------- | @@ -32,16 +32,16 @@ Balancer uses custom errors which provide a convenient and gas-efficient way to | PoolAlreadyInitialized(address) | A pool has already been initialized. `initialize` may only be called once | | PoolNotRegistered(address) | A pool has not been registered | | PoolNotInitialized(address) | A referenced pool has not been initialized | -| HookRegistrationFailed(address, address, address) | A hook contract rejected a pool on registration | -| TokenAlreadyRegistered(IERC20) | A token was already registered (i.e., it is a duplicate in the pool) | +| HookRegistrationFailed(address,address,address) | A hook contract rejected a pool on registration | +| TokenAlreadyRegistered(IERC20) | A token was already registered (i.e.,it is a duplicate in the pool) | | MinTokens() | The token count is below the minimum allowed | | MaxTokens() | The token count is above the maximum allowed | -| InvalidToken() | Invalid tokens (e.g., zero) cannot be registered | +| InvalidToken() | Invalid tokens (e.g.,zero) cannot be registered | | InvalidTokenType() | The token type given in a TokenConfig during pool registration is invalid | | InvalidTokenConfiguration() | The data in a TokenConfig struct is inconsistent or unsupported | -| TokensMismatch(address, address, address) | The token list passed into an operation does not match the pool tokens in the pool | +| TokensMismatch(address,address,address) | The token list passed into an operation does not match the pool tokens in the pool | | BalanceNotSettled() | A transient accounting operation completed with outstanding token deltas | -| VaultIsNotUnlocked() | A user called a Vault function (swap, add/remove liquidity) outside the lock context | +| VaultIsNotUnlocked() | A user called a Vault function (swap,add/remove liquidity) outside the lock context | | DynamicSwapFeeHookFailed() | The pool has returned false to the beforeSwap hook, indicating the transaction should revert | | BeforeSwapHookFailed() | The pool has returned false to the beforeSwap hook, indicating the transaction should revert | | AfterSwapHookFailed() | The pool has returned false to the afterSwap hook, indicating the transaction should revert | @@ -51,33 +51,33 @@ Balancer uses custom errors which provide a convenient and gas-efficient way to | AfterAddLiquidityHookFailed() | The pool has returned false to the afterAddLiquidity hook, indicating the transaction should revert | | BeforeRemoveLiquidityHookFailed() | The pool has returned false to the beforeRemoveLiquidity hook, indicating the transaction should revert | | AfterRemoveLiquidityHookFailed() | The pool has returned false to the afterRemoveLiquidity hook, indicating the transaction should revert | -| RouterNotTrusted() | An unauthorized Router tried to call a permissioned function (i.e., using the Vault's token allowance) | +| RouterNotTrusted() | An unauthorized Router tried to call a permissioned function (i.e.,using the Vault's token allowance) | | AmountGivenZero() | The user tried to swap zero tokens | | CannotSwapSameToken() | The user attempted to swap a token for itself | -| TokenNotRegistered() | The user attempted to swap a token not in the pool | -| SwapLimit(uint256, uint256) | An amount in or out has exceeded the limit specified in the swap request | -| HookAdjustedSwapLimit(uint256, uint256) | A hook adjusted amount in or out has exceeded the limit specified in the swap request | +| TokenNotRegistered(IERC20) | The user attempted to swap a token not in the pool | +| SwapLimit(uint256,uint256) | An amount in or out has exceeded the limit specified in the swap request | +| HookAdjustedSwapLimit(uint256,uint256) | A hook adjusted amount in or out has exceeded the limit specified in the swap request | | TradeAmountTooSmall() | The amount given or calculated for an operation is below the minimum limit | | InvalidAddLiquidityKind() | Add liquidity kind not supported | -| AmountInAboveMax(IERC20, uint256, uint256) | A required amountIn exceeds the maximum limit specified for the operation | -| HookAdjustedAmountInAboveMax(IERC20, uint256, uint256) | A hook adjusted amountIn exceeds the maximum limit specified for the operation | -| BptAmountOutBelowMin(uint256, uint256) | The BPT amount received from adding liquidity is below the minimum specified for the operation | +| AmountInAboveMax(IERC20,uint256,uint256) | A required amountIn exceeds the maximum limit specified for the operation | +| HookAdjustedAmountInAboveMax(IERC20,uint256,uint256) | A hook adjusted amountIn exceeds the maximum limit specified for the operation | +| BptAmountOutBelowMin(uint256,uint256) | The BPT amount received from adding liquidity is below the minimum specified for the operation | | DoesNotSupportAddLiquidityCustom() | Pool does not support adding liquidity with a customized input | | DoesNotSupportDonation() | Pool does not support adding liquidity through donation | | InvalidRemoveLiquidityKind() | Remove liquidity kind not supported | -| AmountOutBelowMin(IERC20, uint256, uint256) | The actual amount out is below the minimum limit specified for the operation | -| HookAdjustedAmountOutBelowMin(IERC20, uint256, uint256) | The hook adjusted amount out is below the minimum limit specified for the operation | -| BptAmountInAboveMax(uint256, uint256) | The required BPT amount in exceeds the maximum limit specified for the operation | +| AmountOutBelowMin(IERC20,uint256,uint256) | The actual amount out is below the minimum limit specified for the operation | +| HookAdjustedAmountOutBelowMin(IERC20,uint256,uint256) | The hook adjusted amount out is below the minimum limit specified for the operation | +| BptAmountInAboveMax(uint256,uint256) | The required BPT amount in exceeds the maximum limit specified for the operation | | DoesNotSupportRemoveLiquidityCustom() | Pool does not support removing liquidity with a customized input | | ProtocolFeesExceedTotalCollected() | Error raised when the sum of the parts (aggregate swap or yield fee) | | SwapFeePercentageTooLow() | Error raised when the swap fee percentage is less than the minimum allowed value | | SwapFeePercentageTooHigh() | Error raised when the swap fee percentage exceeds the maximum allowed value | | FeePrecisionTooHigh() | Primary fee percentages result in an aggregate fee that cannot be stored with the required precision | -| PercentageAboveMax() | A given percentage is above the maximum (usually FixedPoint.ONE, or 1e18 wei) | +| PercentageAboveMax() | A given percentage is above the maximum (usually FixedPoint.ONE,or 1e18 wei) | | QueriesDisabled() | A user tried to execute a query operation when they were disabled | | PoolInRecoveryMode(address) | Cannot enable recovery mode when already enabled | | PoolNotInRecoveryMode(address) | Cannot disable recovery mode when not enabled | -| SenderIsNotVault(address) | Error indicating the sender is not the Vault (e.g., someone is trying to call a permissioned function) | +| SenderIsNotVault(address) | Error indicating the sender is not the Vault (e.g.,someone is trying to call a permissioned function) | | VaultPauseWindowDurationTooLarge() | The caller specified a pause window period longer than the maximum | | PauseBufferPeriodDurationTooLarge() | The caller specified a buffer period longer than the maximum | | VaultPaused() | A user tried to perform an operation while the Vault was paused | @@ -89,9 +89,9 @@ Balancer uses custom errors which provide a convenient and gas-efficient way to | BufferAlreadyInitialized(IERC4626)| Buffer for the given wrapped token was already initialized | | BufferNotInitialized(IERC4626)| Buffer for the given wrapped token was not initialized | | NotEnoughBufferShares()| The user is trying to remove more than their allocated shares from the buffer | -| WrongUnderlyingToken(IERC4626, address)| The wrapped token asset does not match the underlying token | +| WrongUnderlyingToken(IERC4626,address)| The wrapped token asset does not match the underlying token | | InvalidUnderlyingToken(IERC4626)| A wrapped token reported the zero address as its underlying token asset | -| WrapAmountTooSmall(IERC4626)| The amount given to wrap/unwrap was too small, which can introduce rounding issues | +| WrapAmountTooSmall(IERC4626)| The amount given to wrap/unwrap was too small,which can introduce rounding issues | | VaultBuffersArePaused()| Buffer operation attempted while vault buffers are paused | | BufferSharesInvalidReceiver()| Buffer shares were minted to an invalid address | | BufferSharesInvalidOwner()| Buffer shares were burned from an invalid address | @@ -109,11 +109,18 @@ Balancer uses custom errors which provide a convenient and gas-efficient way to ## IProtocolFeeController | Error | Comment | +| -------------- | ----------------------------------------------- | | ProtocolSwapFeePercentageTooHigh() | Error raised when the protocol swap fee percentage exceeds the maximum allowed value | | ProtocolYieldFeePercentageTooHigh() | Error raised when the protocol yield fee percentage exceeds the maximum allowed value | -| PoolCreatorFeePercentageTooHigh() | Error raised when the pool creator swap or yield fee percentage exceeds the maximum allowed value | | PoolCreatorNotRegistered(address) | Error raised if there is no pool creator on a withdrawal attempt from the given pool | -| CallerIsNotPoolCreator(address, address) | Error raised if the wrong account attempts to withdraw pool creator fees | +| CallerIsNotPoolCreator(address,address) | Error raised if the wrong account attempts to withdraw pool creator fees | +| PoolCreatorFeePercentageTooHigh() | Error raised when the pool creator swap or yield fee percentage exceeds the maximum allowed value | + +## ICompositeLiquidityRouter + +| Error | Comment | +| -------------- | ----------------------------------------------- | +| WrongTokensOut(address[],address[]) | `tokensOut` array does not have all the tokens from `expectedTokensOut` | ## RouterCommon @@ -152,6 +159,7 @@ Balancer uses custom errors which provide a convenient and gas-efficient way to | --------------- | ----------------------------------------------- | | MinWeight() | Indicates that one of the pool tokens' weight is below the minimum allowed | | NormalizedWeightInvariant() | Indicates that the sum of the pool tokens' weights is not FP 1 | +| WeightedPoolBptRateUnsupported | Weighted Pools cannot safely use themselves as rate providers (especially nested) | ## EVMCallCodeHelpers @@ -192,15 +200,13 @@ Balancer uses custom errors which provide a convenient and gas-efficient way to ## StableMath | Error | Comment | | ----------------------------- | ----------------------------------------------- | -| StableInvariantDidntConverge()| The iterations to calculate the invariant didn't converge | -| StableGetBalanceDidntConverge()| The iterations to calculate the balance didn't converge | +| StableInvariantDidNotConverge()| The iterations to calculate the invariant didn't converge | +| StableComputeBalanceDidNotConverge()| The iterations to calculate the balance didn't converge | ## WeightedMath | Error | Comment | | --------------------- | ----------------------------------------------- | -| MinBPTInForTokenOut() | User Attempted to burn less BPT than allowed for a specific amountOut | -| MaxOutBptForTokenIn() | User attempted to mint more BPT than allowed for a specific amountIn | | MaxOutRatio() | User attempted to extract a disproportionate amountOut of tokens from a pool | | MaxInRatio() | User attempted to add a disproportionate amountIn of tokens to a pool | | ZeroInvariant() | Error thrown when the calculated invariant is zero, indicating an issue with the invariant calculation | @@ -227,14 +233,14 @@ Balancer uses custom errors which provide a convenient and gas-efficient way to | Error | Comment | | ----------------------------- | ----------------------------------------------- | | ERC2612ExpiredSignature(uint256) | Permit deadline has expired | -| ERC2612InvalidSigner(address, address) | Mismatched signature | +| ERC2612InvalidSigner(address,address) | Mismatched signature | ## VaultFactory | Error | Comment | | --------------------- | ----------------------------------------------- | -| VaultAlreadyCreated() | Vault has already been deployed, so this factory is disabled | | VaultAddressMismatch()| The given salt does not match the generated address when attempting to create the Vault | +| InvalidBytecode(string) | The bytecode for the given contract does not match the expected bytecode | ## FactoryWidePauseWindow | Error | Comment | @@ -249,13 +255,18 @@ Balancer uses custom errors which provide a convenient and gas-efficient way to ## RevertCodec | Error | Comment | | ----------------- | ----------------------------------------------- | -| Result(bytes) | On success of the primary operation in a `quoteAndRevert`, this error is thrown with the return data | -| ErrorSelectorNotFound() | Handle the "reverted without a reason" case (i.e., no return data) | +| Result(bytes) | On success of the primary operation in a `quoteAndRevert`,this error is thrown with the return data | +| ErrorSelectorNotFound() | Handle the "reverted without a reason" case (i.e.,no return data) | ## TransientStorageHelpers | Error | Comment | | ----------------- | ----------------------------------------------- | -| TransientIndexOutOfBounds() | An index is out of bounds on an array operation (e.g., at) | +| TransientIndexOutOfBounds() | An index is out of bounds on an array operation (e.g.,at) | + +## ERC20TestToken +| Error | Comment | +| ----------------- | ----------------------------------------------- | +| ZeroTransfer | A zero value transfer occurred (test tokens revert here to ensure the Vault supports tokens that prohibit zero value transfers) |