├── references ├── contracts │ ├── README.md │ └── apis │ │ └── README.md ├── lp-tokens │ ├── README.md │ ├── how-many-bpt-in-vebal.md │ ├── underlying.md │ └── valuing.md ├── valuing-balancer-lp-tokens │ ├── pools │ │ ├── weightedpool.md │ │ ├── liquiditybootstrappingpool.md │ │ ├── stablepools.md │ │ ├── investmentpools.md │ │ ├── weightedpool2tokens.md │ │ ├── README.md │ │ └── metastablepools.md │ ├── balancerhelpers.md │ ├── asset-managers.md │ ├── events.md │ └── uml-diagrams.md └── subgraphs.md ├── resources ├── deploy-pools-from-factory │ ├── README.md │ └── creation │ │ ├── liquidity-bootstrapping-pool.md │ │ ├── weightedpool.md │ │ ├── stablepool.md │ │ ├── metastable-pool.md │ │ ├── stablephantom-pool.md │ │ ├── boosted-metapool.md │ │ ├── aavelinearpool.md │ │ └── README.md ├── joins-and-exits │ ├── README.md │ ├── pool-exits.md │ └── pool-joins.md ├── vebal-and-gauges │ ├── README.md │ ├── estimating-gauge-incentive-aprs │ │ ├── apr-calculation.md │ │ ├── README.md │ │ └── data-fetching.md │ ├── vebal.md │ └── gauges.md ├── swaps │ ├── README.md │ ├── single-swap.md │ ├── flash-swaps.md │ └── batch-swaps.md ├── pool-math │ ├── README.md │ ├── stable-math.md │ └── weighted-math.md ├── flash-loans.md ├── pool-interfacing │ ├── stable-pool.md │ ├── weighted-pool.md │ ├── oracle-pools.md │ ├── managed-pools.md │ ├── liquidity-bootstrapping-pool.md │ ├── README.md │ └── metastable-pool.md ├── internal-user-balances.md ├── rate-providers.md ├── query-batchswap-join-exit.md └── smart-order-router.md ├── deep-dive ├── inside-balancer-contracts │ ├── README.md │ ├── recovery-mode.md │ └── timelock-authorizer.md └── guided-tour-of-balancer-vault │ ├── README.md │ └── episode-2-joins │ └── detour-the-init-join.md ├── .gitbook └── assets │ ├── image (1).png │ ├── createGraph.png │ ├── spotpriceaftertrade.png │ ├── Screen Shot 2021-10-05 at 8.16.07 AM.png │ ├── Screen Shot 2021-10-05 at 8.16.12 AM.png │ ├── Screen Shot 2021-11-18 at 2.35.27 PM.png │ ├── Screen Shot 2021-11-18 at 2.38.28 PM.png │ ├── Screen Shot 2021-11-18 at 2.42.26 PM.png │ ├── Screen Shot 2021-11-18 at 2.43.14 PM.png │ ├── Screen Shot 2021-11-18 at 2.44.44 PM.png │ ├── Screen Shot 2021-11-18 at 2.49.42 PM.png │ ├── Screen Shot 2021-11-18 at 2.51.16 PM.png │ ├── Screen Shot 2021-11-18 at 2.51.48 PM.png │ ├── Screen Shot 2022-04-21 at 3.31.52 PM.png │ ├── Screen Shot 2022-04-22 at 9.50.34 AM.png │ ├── Screen Shot 2022-04-27 at 11.07.35 AM.png │ ├── Screen Shot 2022-04-27 at 11.25.09 AM.png │ ├── Screen Shot 2021-10-05 at 8.16.07 AM (1).png │ ├── Screen Shot 2021-10-05 at 8.16.12 AM (1).png │ ├── Screen Shot 2021-11-18 at 2.44.44 PM (1).png │ └── Screen Shot 2021-11-18 at 2.44.44 PM (2).png ├── README.md ├── helpers ├── encoding.md └── using-native-eth.md ├── guides ├── swaps │ └── README.md └── data │ ├── README.md │ ├── balancer-subgraph │ └── deployment.md │ ├── inferring-historical-liquidity-mining-aprs-legacy.md │ └── balancer-subgraph.md └── SUMMARY.md /references/contracts/README.md: -------------------------------------------------------------------------------- 1 | # Contracts 2 | 3 | -------------------------------------------------------------------------------- /references/contracts/apis/README.md: -------------------------------------------------------------------------------- 1 | # APIs 2 | 3 | -------------------------------------------------------------------------------- /references/lp-tokens/README.md: -------------------------------------------------------------------------------- 1 | # LP Tokens 2 | 3 | -------------------------------------------------------------------------------- /resources/deploy-pools-from-factory/README.md: -------------------------------------------------------------------------------- 1 | # Pools 2 | 3 | -------------------------------------------------------------------------------- /resources/joins-and-exits/README.md: -------------------------------------------------------------------------------- 1 | # Joins and Exits 2 | 3 | -------------------------------------------------------------------------------- /resources/vebal-and-gauges/README.md: -------------------------------------------------------------------------------- 1 | # veBAL and Gauges 2 | 3 | -------------------------------------------------------------------------------- /deep-dive/inside-balancer-contracts/README.md: -------------------------------------------------------------------------------- 1 | # Inside Balancer Contracts 2 | 3 | -------------------------------------------------------------------------------- /.gitbook/assets/image (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/image (1).png -------------------------------------------------------------------------------- /.gitbook/assets/createGraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/createGraph.png -------------------------------------------------------------------------------- /.gitbook/assets/spotpriceaftertrade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/spotpriceaftertrade.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-10-05 at 8.16.07 AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-10-05 at 8.16.07 AM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-10-05 at 8.16.12 AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-10-05 at 8.16.12 AM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-11-18 at 2.35.27 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-11-18 at 2.35.27 PM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-11-18 at 2.38.28 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-11-18 at 2.38.28 PM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-11-18 at 2.42.26 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-11-18 at 2.42.26 PM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-11-18 at 2.43.14 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-11-18 at 2.43.14 PM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-11-18 at 2.44.44 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-11-18 at 2.44.44 PM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-11-18 at 2.49.42 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-11-18 at 2.49.42 PM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-11-18 at 2.51.16 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-11-18 at 2.51.16 PM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-11-18 at 2.51.48 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-11-18 at 2.51.48 PM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2022-04-21 at 3.31.52 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2022-04-21 at 3.31.52 PM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2022-04-22 at 9.50.34 AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2022-04-22 at 9.50.34 AM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2022-04-27 at 11.07.35 AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2022-04-27 at 11.07.35 AM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2022-04-27 at 11.25.09 AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2022-04-27 at 11.25.09 AM.png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-10-05 at 8.16.07 AM (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-10-05 at 8.16.07 AM (1).png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-10-05 at 8.16.12 AM (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-10-05 at 8.16.12 AM (1).png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-11-18 at 2.44.44 PM (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-11-18 at 2.44.44 PM (1).png -------------------------------------------------------------------------------- /.gitbook/assets/Screen Shot 2021-11-18 at 2.44.44 PM (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balancer/docs-developers/HEAD/.gitbook/assets/Screen Shot 2021-11-18 at 2.44.44 PM (2).png -------------------------------------------------------------------------------- /resources/swaps/README.md: -------------------------------------------------------------------------------- 1 | # Swaps 2 | 3 | ## What kind of swap should I be using? 4 | 5 | In most cases, you'll want to use **Batch Swaps**. The only time you would want to use a Single Swap is when you're making a trade between just two tokens in one pool. **Executing multiple Single Swaps in a single transaction is inefficient and should instead be batched**. 6 | -------------------------------------------------------------------------------- /deep-dive/guided-tour-of-balancer-vault/README.md: -------------------------------------------------------------------------------- 1 | # Guided Tour of Balancer Vault 2 | 3 | ## Let's Read the Source Code! 4 | 5 | In the Guided Tour of the Balancer Vault, we'll start with common actions such as swaps, joins, and pool deployments. We'll follow along as these calls jump through different contracts and functions, and we'll come out the other side understanding how the magic happens. 6 | -------------------------------------------------------------------------------- /resources/pool-math/README.md: -------------------------------------------------------------------------------- 1 | # Pool Math 2 | 3 | ## Overview 4 | 5 | Different pool types utilize different underlying equations relevant to their specific use cases. 6 | 7 | * [Weighted Math](weighted-math.md) 8 | * WeightedPool 9 | * WeightedPool2Tokens 10 | * LiquidityBootstrappingPool 11 | * InvestmentPool 12 | * [Stable Math](stable-math.md) 13 | * StablePool 14 | * MetaStablePool 15 | -------------------------------------------------------------------------------- /references/valuing-balancer-lp-tokens/pools/weightedpool.md: -------------------------------------------------------------------------------- 1 | # WeightedPool 2 | 3 | ## More functions are inherited 4 | 5 | This page breaks out the functions that are not common to all pools. See more functions on the [Pools page](./). 6 | 7 | ## API 8 | 9 | ### `onSwap` 10 | 11 | ```cpp 12 | // Inherited from BaseMinimalSwapInfoPool 13 | onSwap(SwapRequest request, 14 | uint256 balanceTokenIn, 15 | uint256 balanceTokenOut) 16 | returns (uint256 amount[In/Out]) 17 | ``` 18 | 19 | When the Vault is handling a swap, it will call `onSwap` to ask the pool what the amounts should be. Pools that use weighted math only need the input/output tokens to determine price. 20 | -------------------------------------------------------------------------------- /references/valuing-balancer-lp-tokens/balancerhelpers.md: -------------------------------------------------------------------------------- 1 | # BalancerHelpers 2 | 3 | ### `queryJoin` 4 | 5 | ``` 6 | queryJoin( 7 | bytes32 poolId, 8 | address sender, 9 | address recipient, 10 | JoinPoolRequest request) 11 | returns (uint256 bptOut, uint256[] amountsIn) 12 | 13 | JoinPoolRequest( 14 | address[] assets, 15 | uint256[] maxAmountsIn, 16 | bytes userData, 17 | bool fromInternalBalance 18 | ) 19 | ``` 20 | 21 | ### `queryExit` 22 | 23 | ``` 24 | queryExit( 25 | bytes32 poolId, 26 | address sender, 27 | address recipient, 28 | ExitPoolRequest request) 29 | returns (uint256 bptIn, uint256[] amountsOut) 30 | 31 | ExitPoolRequest( 32 | address[] assets, 33 | uint256[] minAmountsOut, 34 | bytes userData, 35 | bool toInternalBalance 36 | ) 37 | ``` 38 | -------------------------------------------------------------------------------- /resources/deploy-pools-from-factory/creation/liquidity-bootstrapping-pool.md: -------------------------------------------------------------------------------- 1 | # Liquidity Bootstrapping Pool 2 | 3 | ## Common Arguments 4 | 5 | In addition to the arguments listed below, you should also consider the [common arguments](./#common-arguments) and [those of the WeightedPool](weightedpool.md) when creating a Liquidity Bootstrapping Pool. 6 | 7 | ## Pool Creation Arguments 8 | 9 | ### `swapEnabledOnStart` 10 | 11 | The `swapEnabledOnStart` on argument allows pool creators to deploy the pool in a state with frozen trades; this can be highly advantageous for preventing bots from trading with the pool as soon as liquidity is added. 12 | 13 | While not every application needs this feature, deploying a pool with swaps disabled and later activating them (especially at an announced time) allows for a more level playing field. 14 | -------------------------------------------------------------------------------- /references/lp-tokens/how-many-bpt-in-vebal.md: -------------------------------------------------------------------------------- 1 | # How Many BPT in veBAL? 2 | 3 | Are you trying value or determine underlying assets that are locked in veBAL? You'll need to query a few things from the [veBAL contract](https://etherscan.io/address/0xc128a9954e6c874ea3d62ce62b468ba073093f25#readContract). Most notably: 4 | 5 | ``` 6 | underlyingBpt = veBAL.token(); 7 | (amount, end) = veBAL.locked(); 8 | ``` 9 | 10 | {% hint style="warning" %} 11 | Don't use `balanceOf` on the veBAL contract if you're trying to calculate value associated with underlying tokens. `balanceOf` returns a time dependent value only useful for querying a user's current voting power. 12 | {% endhint %} 13 | 14 | Now that you have the `underlyingBpt` address and the `amount` of those BPT that you have locked, you can now analyze, [value](broken-reference), or [determine underlying tokens](underlying.md). 15 | -------------------------------------------------------------------------------- /resources/deploy-pools-from-factory/creation/weightedpool.md: -------------------------------------------------------------------------------- 1 | # WeightedPool 2 | 3 | ## Common Arguments 4 | 5 | In addition to the arguments listed below, you should also consider the [common arguments](./#common-arguments) when creating a pool. 6 | 7 | ## Pool Creation Arguments 8 | 9 | ### Weights 10 | 11 | The weights for each token represent the percentage of the pool value that is denominated in that token (assuming an efficient market). 12 | 13 | For example a 50/50 WETH/WBTC pool will have 50% of its value is WETH and the other 50% in WBTC. Similarly, an 80/20 BAL/WETH pool will hold 80% of its value in BAL and 20% in WETH. 14 | 15 | Weight arguments use 18 decimals and must add up to 1. 16 | 17 | ### oracleEnabled (WeightedPool2Tokens only) 18 | 19 | If you're deploying a pool of type WeightedPool2Tokens, you're given the option of turning the oracle functionality on/off. This is passed as a boolean. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome 2 | 3 | ## #BuiltOnBalancer 4 | 5 | These docs are intended for anyone who is interfacing with or building on top of Balancer. Whether you're a trade aggregator, yield aggregator, trader, liquidity provider, founder, web dev, arbitrageur, AMM designer, mathematician, or curious individual, this is the place for you. 6 | 7 | ## Quick Links 8 | 9 | * [Contract APIs](references/contracts/apis/) 10 | * [Contract Deployment Addresses](references/valuing-balancer-lp-tokens/deployment-addresses.md) 11 | * [Pool Interfacing](resources/pool-interfacing/) 12 | * [Deploying a Pool](resources/deploy-pools-from-factory/) 13 | * [Subgraph](references/subgraphs.md) 14 | * [Encoding `userData`](helpers/encoding.md) 15 | 16 | ## Can't find something? 17 | 18 | If you're having trouble finding the answer to your question, ask for help in the `#dev` channel of the [Balancer Discord](https://discord.balancer.fi). Notice something missing? [You can contribute ](https://github.com/balancer-labs/docs-developers)to these docs! 19 | -------------------------------------------------------------------------------- /resources/vebal-and-gauges/estimating-gauge-incentive-aprs/apr-calculation.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Formula to calculate the annual percentage rate (APR) of gauge incentives 3 | --- 4 | 5 | # APR Calculation 6 | 7 | Congratulations, you now obtained all relevant data to calculate the gauge APR. Please note that for mainnet liquidity mining APRs are boostable, therefore a range of 1x to 2.5x of the calculated APR is possible. 8 | 9 | $$ 10 | minAPR(1x) = \frac{\frac{0.4}{ (workingSupply + 0.4) }* gaugeRelWeight * weeklyBALemission * 52 * priceOfBAL}{ pricePerBPT} * 100 11 | $$ 12 | 13 | where 14 | 15 | * `workingSupply` = value obtained from the gauge vyper contract in step 2 16 | * `gaugeRelWeight` = relative voting weight obtained from the gauge controller contract from step 3 17 | * `weeklyBALemissions` = **** currently active weekly emissions, fixed at 145’000 BAL 18 | * `priceOfBAL` = price of BAL in $ as obtained from an external pricing provider 19 | * `pricePerBPT` = price per balancer pool token as inferred from step 4 20 | -------------------------------------------------------------------------------- /resources/flash-loans.md: -------------------------------------------------------------------------------- 1 | # Flash Loans 2 | 3 | ## Overview 4 | 5 | Since the Vault holds all tokens for all pools, the consolidated token balances are available as Flash Loans. 6 | 7 | ## Example Code 8 | 9 | ```clike 10 | pragma solidity ^0.7.0; 11 | 12 | import "@balancer-labs/v2-vault/contracts/interfaces/IVault.sol"; 13 | import "@balancer-labs/v2-vault/contracts/interfaces/IFlashLoanRecipient.sol"; 14 | 15 | contract FlashLoanRecipient is IFlashLoanRecipient { 16 | IVault private constant vault = "0xBA12222222228d8Ba445958a75a0704d566BF2C8"; 17 | 18 | function makeFlashLoan( 19 | IERC20[] memory tokens, 20 | uint256[] memory amounts, 21 | bytes memory userData 22 | ) external { 23 | vault.flashLoan(this, tokens, amounts, userData); 24 | } 25 | 26 | function receiveFlashLoan( 27 | IERC20[] memory tokens, 28 | uint256[] memory amounts, 29 | uint256[] memory feeAmounts, 30 | bytes memory userData 31 | ) external override { 32 | require(msg.sender == vault); 33 | ... 34 | } 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /helpers/encoding.md: -------------------------------------------------------------------------------- 1 | # Encoding userData 2 | 3 | There are some arguments that need to be encoded when interacting with Balancer V2 Smart Contracts. For convenience, this page provides an example of how to encode arguments. 4 | 5 | {% tabs %} 6 | {% tab title="Solidity" %} 7 | ```cpp 8 | uint256 joinKind = 0; 9 | uint256[] memory initBalances = new uint256[](2); 10 | initBalances[0] = 1e18; 11 | initBalances[1] = 2e18; 12 | bytes memory userDataEncoded = abi.encode(joinKind, initBalances); 13 | ``` 14 | {% endtab %} 15 | 16 | {% tab title="js" %} 17 | ```javascript 18 | import { defaultAbiCoder } from '@ethersproject/abi'; 19 | 20 | const joinKind = 0; 21 | const initBalances = [1e18, 2e18]; 22 | const abi = ['uint256', 'uint256[]']; 23 | const data = [joinKind, initBalances]; 24 | const userDataEncoded = defaultAbiCoder.encode(abi,data); 25 | ``` 26 | {% endtab %} 27 | 28 | {% tab title="Python" %} 29 | ```python 30 | import eth_abi 31 | 32 | joinKind = 0 33 | initBalances = [1e18, 2e18] 34 | abi = ['uint256', 'uint256[]'] 35 | data = [joinKind, initBalances] 36 | userDataEncoded = eth_abi.encode_abi(abi, data) 37 | ``` 38 | {% endtab %} 39 | {% endtabs %} 40 | -------------------------------------------------------------------------------- /resources/pool-interfacing/stable-pool.md: -------------------------------------------------------------------------------- 1 | # Stable Pool 2 | 3 | ## Overview 4 | 5 | For advantages and use cases of Stable Pools, please refer to [the standard documentation](https://docs.balancer.fi/products/balancer-pools/stable-pools). 6 | 7 | For more interfaces, such as updating the `amplificationParameter`, see the [StablePool API](../../references/valuing-balancer-lp-tokens/pools/stablepools.md#api). 8 | 9 | ## Interfacing 10 | 11 | Some elements to consider when interfacing with Stable Pools: 12 | 13 | * Using [Stable Math](../pool-math/stable-math.md) 14 | * Pools have between 2 and 5 tokens 15 | * Pools rely on the `amplificationParameter`, which is defined at pool creation and can be gradually updated later. 16 | 17 | ## Getting Pool Data 18 | 19 | In addition to the [common pool data](./#getting-common-pool-data), you will likely want the following data when interfacing with Stable Pools: 20 | 21 | ### Amplification Parameter 22 | 23 | The Amplification Parameter is stored at the pool level. For example, calling 24 | 25 | ``` 26 | pool.getGetAmplificationParameter() 27 | ``` 28 | 29 | returns something resembling 30 | 31 | ``` 32 | value : 620000 33 | isUpdating : False 34 | precision : 1000 35 | ``` 36 | 37 | where the amplification parameter is $$\frac{value}{precision} = \frac{620000}{1000}=620$$ in this case. 38 | -------------------------------------------------------------------------------- /references/lp-tokens/underlying.md: -------------------------------------------------------------------------------- 1 | # Underlying 2 | 3 | ## Overview 4 | 5 | It's common to want to know what a Balancer LP token has as its underlying tokens. 6 | 7 | ## Directly Query The Vault and LP Tokens 8 | 9 | The Vault can tell you exactly how many tokens are in the pool. You can query that, and then divide by the amount of the pool that is yours 10 | 11 | ### Pseudocode 12 | 13 | ``` 14 | (tokens, balances, lastChangeBlock) = vault.getPoolTokens(poolId); 15 | yourPoolShare = bpt.balanceOf(yourAddress)/bpt.totalSupply(); 16 | uint256 yourUnderlyingBalances = new uint256[](balances.length); 17 | for(i=0, i; 14 | userAddress=; 15 | tokenAddress0=; 16 | tokenAddress1=; 17 | tokens = [tokenAddress0, tokenAddress1]; 18 | 19 | feeDistributorContract = contract(feeDistributorAddress, feeDistributorAbi); 20 | claimableTokens = feeDistributorContract.claimTokens(userAddress,tokens).call(); 21 | ``` 22 | 23 | ## How to Claim Pending Tokens for a veBAL Holders 24 | 25 | The process is identical to [querying as above](vebal.md#how-to-query-pending-tokens-for-a-vebal-holders), except instead of `eth_call`, you will use `eth_sendTransaction`. 26 | 27 | ## How do I Know Which Tokens to Query/Claim? 28 | 29 | At the time of this writing, there is no subgraph tracking tokens added to the `FeeDistributor`. For now, an easy way you can find the available tokens for claiming is checking what the contract holds on Etherscan 30 | 31 | ![](<../../.gitbook/assets/Screen Shot 2022-04-21 at 3.31.52 PM.png>) 32 | -------------------------------------------------------------------------------- /resources/pool-interfacing/weighted-pool.md: -------------------------------------------------------------------------------- 1 | # Weighted Pool 2 | 3 | ## Overview 4 | 5 | For advantages and use cases of Weighted Pools, please refer to [the standard documentation](https://docs.balancer.fi/products/balancer-pools/weighted-pools). 6 | 7 | For more interfaces, see the [WeightedPool API](../../references/valuing-balancer-lp-tokens/pools/weightedpool.md#api) and [WeightedPool2Tokens API](../../references/valuing-balancer-lp-tokens/pools/weightedpool2tokens.md#api). 8 | 9 | ## Interfacing 10 | 11 | Some elements to consider when interfacing with Weighted Pools: 12 | 13 | * Using [Weighted Math](../pool-math/weighted-math.md) 14 | * Pool weights are static, defined at pool creation 15 | * Pools have between 2 and 8 tokens 16 | * Pool weights range from 1% to 99% 17 | 18 | ## Getting Pool Data 19 | 20 | In addition to the [common pool data](./#getting-common-pool-data), you will likely want the following data when interfacing with Weighted Pools: 21 | 22 | ### Weights 23 | 24 | Weights are stored at the pool level. For example, calling 25 | 26 | ``` 27 | pool.getNormalizedWeights() 28 | ``` 29 | 30 | returns something resembling 31 | 32 | ``` 33 | [800000000000000000, 200000000000000000] 34 | ``` 35 | 36 | which are the weights represented with 18 decimals. A pool with 80%/20% weights corresponds to \[0.8, 0.2] after scaling for decimals. 37 | 38 | ### Oracle Data 39 | 40 | To query oracle data from a pool of type `WeightedPool2Tokens`, refer to the [Oracle Pools ](oracle-pools.md#overview)interfacing page. 41 | -------------------------------------------------------------------------------- /references/valuing-balancer-lp-tokens/pools/liquiditybootstrappingpool.md: -------------------------------------------------------------------------------- 1 | # LiquidityBootstrappingPool 2 | 3 | ## More functions are inherited 4 | 5 | This page breaks out the functions that are not common to all pools. See more functions on the [Pools page](./). 6 | 7 | ## API 8 | 9 | ### `onSwap` 10 | 11 | ```cpp 12 | // Inherited from BaseMinimalSwapInfoPool 13 | onSwap(SwapRequest request, 14 | uint256 balanceTokenIn, 15 | uint256 balanceTokenOut) 16 | returns (uint256 amount[In/Out]) 17 | ``` 18 | 19 | When the Vault is handling a swap, it will call `onSwap` to ask the pool what the amounts should be. Pools that use weighted math only need the input/output tokens to determine price. 20 | 21 | ### `getSwapEnabled` 22 | 23 | ```cpp 24 | function getSwapEnabled() 25 | returns (bool) 26 | ``` 27 | 28 | Returns `True` if the pool has swaps enabled. 29 | 30 | ### `getGradualWeightUpdateParam` 31 | 32 | ```cpp 33 | function getGradualWeightUpdateParams() 34 | returns ( 35 | uint256 startTime, 36 | uint256 endTime, 37 | uint256[] endWeights) 38 | ``` 39 | 40 | Return start time, end time, and endWeights as an array. Current weights should be retrieved via getNormalizedWeights(). 41 | 42 | ## Permissioned Functions 43 | 44 | All of the following functions are only callable by the pool owner. 45 | 46 | ### `setSwapEnabled` 47 | 48 | ```cpp 49 | function setSwapEnabled(bool swapEnabled) 50 | ``` 51 | 52 | Enables swaps if passed `True`, disables them if passed `False`. 53 | 54 | ### `updateWeightsGradually` 55 | 56 | ```cpp 57 | function updateWeightsGradually( 58 | uint256 startTime, 59 | uint256 endTime, 60 | uint256[] endWeights) 61 | ``` 62 | 63 | Schedule a gradual weight change, from the current weights to the given `endWeights`, from `startTime` to `endTime`. 64 | -------------------------------------------------------------------------------- /resources/vebal-and-gauges/estimating-gauge-incentive-aprs/README.md: -------------------------------------------------------------------------------- 1 | # Estimating Gauge Incentive APRs 2 | 3 | ## How can I obtain/calculate the liquidity mining APR for a certain gauge? 4 | 5 | Balancer introduced a new tokenomics system for the Balancer Governance Token (BAL) based on Curve's _ve_ model. The system revolves around following concepts: 6 | 7 | * A new token “veBAL” is obtained by locking 80:20 BAL:WETH BPTs for a certain duration. This token allows a user to participate in governance, determine where liquidity mining incentives go, and collect a share of protocol fees 8 | * veBAL holders can decide where liquidity mining incentives are directed by allocating their vote to "gauges" that are referencing staking contracts of pre-approved pools in the Balancer ecosystem (Mainnet, Arbitrum, Polygon, and Optimism) 9 | * The overall gauge vote percentage directs the weekly BAL emissions. If the weekly total amount is 145,000 BAL per week, a pool gauge with 1% of the vote will net in 1,450 BAL towards that gauge 10 | * Emissions are set based on the previous weeks voting round which concludes each Thursday 00:00 UTC 11 | 12 | To estimate the BAL liquidity mining APR for a certain gauge, various endpoints have to be read. Currently, there is not an API to obtain the APRs as they have to be calculated on the fly. 13 | 14 | Follow the steps below to calculate the liquidity mining APR for a certain gauge (explained in more detail in [data fetching](data-fetching.md)): 15 | 16 | 1. Obtain the current gauge whitelist from the front-end repo 17 | 2. Obtain the working supply for each gauge by reading gauge vyper contracts 18 | 3. Obtain the relative weight for each gauge via the gauge controller contract 19 | 4. Infer the price per Balancer Pool Token (BPT) price from the balancer-v2 subgraph 20 | 5. Fetch the current BAL price 21 | 22 | -------------------------------------------------------------------------------- /guides/swaps/README.md: -------------------------------------------------------------------------------- 1 | # Swaps 2 | 3 | Swaps are a cornerstone of any exchange, and Balancer has a few types for different purposes. 4 | 5 | {% hint style="info" %} 6 | This section will dive into code for executing swaps on Balancer V2. This functionality is also available in the Python Library **balpy**. Find it on [PyPI](https://pypi.org/project/balpy/) and the source (with samples!) on [GitHub](https://github.com/gerrrg/balpy/). 7 | {% endhint %} 8 | 9 | ### Batch Swaps 10 | 11 | You'll want to use **Batch Swaps** when you're making a trade that hops through multiple pools. These are useful for swapping between two tokens that aren't in the same pool, and for routes with better prices than naive single swaps. 12 | 13 | #### Flash Swaps 14 | 15 | There is a specific case of Batch Swap called a Flash Swap that enables trades with no input tokens. These are useful for doing arbitrage among Balancer pools. To make a Flash Swap, create a Batch Swap with all your token limits set to zero. 16 | 17 | ### Single Swaps 18 | 19 | You'll want to use **Single Swaps** when you're making a trade between two tokens in one pool. While it's possible to do this with a one-step Batch Swap, using a **Single Swap** will save \~6,000 gas. 20 | 21 | ## What kind of swap should I be using? 22 | 23 | In most cases, you'll want to use **Batch Swaps**. The only time you would want to use a Single Swap is when you're making a trade between just two tokens in one pool. **Executing multiple Single Swaps in a single transaction is inefficient and should instead be batched**. 24 | 25 | ![Sample gas costs for trades executed through multiple weighted pools](<../../.gitbook/assets/Screen Shot 2021-10-05 at 8.16.12 AM (1).png>) 26 | 27 | ![Sample gas costs for trades executed through Weighted->Stable->Weighted pools](<../../.gitbook/assets/Screen Shot 2021-10-05 at 8.16.07 AM (1).png>) 28 | -------------------------------------------------------------------------------- /references/valuing-balancer-lp-tokens/pools/stablepools.md: -------------------------------------------------------------------------------- 1 | # StablePools 2 | 3 | ## More functions are inherited 4 | 5 | This page breaks out the functions that are not common to all pools. See more functions on the [Pools page](./). 6 | 7 | ## API 8 | 9 | ### `onSwap` 10 | 11 | ```cpp 12 | // Inherited from BaseGeneralPool 13 | onSwap(SwapRequest swapRequest, 14 | uint256[] balances, 15 | uint256 indexIn, 16 | uint256 indexOut) 17 | returns (uint256 amount[In/Out]) 18 | ``` 19 | 20 | When the Vault is handling a swap, it will call `onSwap` to ask the pool what the amounts should be. Pools that use stable math need the all tokens balances to determine price. 21 | 22 | ### `getAmplificationParameter` 23 | 24 | ``` 25 | function getAmplificationParameter() 26 | returns (uint256 value, bool isUpdating, uint256 precision) 27 | ``` 28 | 29 | Returns the amplification parameter value, a boolean to determine if it's updating, and its precision. 30 | 31 | ## Permissioned Functions 32 | 33 | All of the following functions are only callable by the pool owner. 34 | 35 | ### `startAmplificationParameterUpdate` 36 | 37 | ``` 38 | function startAmplificationParameterUpdate( 39 | uint256 rawEndValue, 40 | uint256 endTime) 41 | ``` 42 | 43 | Begins changing the amplification parameter to `rawEndValue` over time. The value will change linearly until `endTime` is reached, when it will be `rawEndValue`. 44 | 45 | **NOTE**: Internally, the amplification parameter is represented using higher precision. The values returned by `getAmplificationParameter` have to be corrected to account for this when comparing to `rawEndValue`. 46 | 47 | ### `stopAmplificationParameterUpdate` 48 | 49 | ``` 50 | function stopAmplificationParameterUpdate() external 51 | ``` 52 | 53 | Stops the amplification parameter change process, keeping the current value. 54 | -------------------------------------------------------------------------------- /references/valuing-balancer-lp-tokens/asset-managers.md: -------------------------------------------------------------------------------- 1 | # Asset Managers 2 | 3 | {% hint style="warning" %} 4 | ### Heads up! 5 | 6 | Asset Managers can only be used with pools deployed from factories that enable them in their pools. There currently are no pool factories that support pools with Asset Managers. The first use cases are expected to become available after Managed Pools (which have admin controls) are launched. 7 | 8 | ### Why? 9 | 10 | While it is possible to write an Asset Manager, their primary intended use-case is more elegantly solved with Boosted Pools. There may still be use-cases for which Asset Managers are advantageous, but for now they have not yet been implemented. 11 | {% endhint %} 12 | 13 | ## `managePoolBalance` 14 | 15 | ``` 16 | enum PoolBalanceOpKind { WITHDRAW, DEPOSIT, UPDATE } 17 | 18 | struct PoolBalanceOp { 19 | PoolBalanceOpKind kind; 20 | bytes32 poolId; 21 | IERC20 token; 22 | uint256 amount; 23 | } 24 | 25 | function managePoolBalance(PoolBalanceOp[] ops); 26 | ``` 27 | 28 | Each token in the pool can be associated with an Asset Manager contract. Authorized Asset Managers can call the Vault's `managePoolBalance` function to perform batch operations on pools. They can withdraw tokens from the Vault (and then "invest" them according to the strategy), return them to the Vault with deposit, or update the total (to report gains/losses). 29 | 30 | ## Inheriting from `AssetManager` 31 | 32 | Asset Managers will subclass the base `AssetManager`, which has not yet been fully defined. The expected interface will likely be similar to the following 33 | 34 | ``` 35 | getBalance(bytes32 poolId) returns (uint256 managedTokenBalance) 36 | 37 | getInvestablePercent(bytes32 poolId) returns (uint256) 38 | 39 | setInvestablePercent(bytes32 poolId, uint256 investmentPercent) 40 | 41 | maxInvestableBalance(bytes32 poolId) returns (int256 maxBalance) 42 | 43 | // Called by anyone, to realize gains/losses 44 | updateBalanceOfPool(bytes32 poolId) 45 | ``` 46 | -------------------------------------------------------------------------------- /references/lp-tokens/valuing.md: -------------------------------------------------------------------------------- 1 | # Valuing 2 | 3 | ## Overview 4 | 5 | It's common to want to know what a Balancer LP token is worth. This generally comes down to its Net Asset Value (NAV), ie the value of the underlying tokens. 6 | 7 | ### Directly Calculating NAV 8 | 9 | Directly calculating NAV is the simplest way to value an LP token. In short, it is calculated by getting all underlying balances, multiplying those by their market prices, and dividing by the total supply of LP tokens. 10 | 11 | This method is quick and easy for informational purposes, but is vulnerable to manipulation if you need to know a Balancer LP token's value on-chain. An added bonus is that this technique works for all pool types because it simply gets the value of all underlying tokens. 12 | 13 | #### Equation 14 | 15 | $$ 16 | Price_{LP token}=\frac{\Sigma_{i}{(Balance_i*Price_i)}}{Supply_{LP Tokens}} 17 | $$ 18 | 19 | #### Pseudocode 20 | 21 | ``` 22 | (tokens, balances, lastChangeBlock) = vault.getPoolTokens(poolId); 23 | prices = fetchPricesFromPriceProvider(tokens); //ex. CoinGecko 24 | poolValueUsd = sum(balances[i]*price[i]); 25 | bptPriceUsd = poolValueUsd/bpt.totalSupply(); 26 | ``` 27 | 28 | {% hint style="warning" %} 29 | #### **Note about pre-minted BPT** 30 | 31 | Some pools (like bb-a-USD) have pre-minted BPT. This means all LP tokens are minted at the time of pool creation so that you can use a swap to effectively join/exit the pool. Because of this, when querying the supply, you should **NOT** use `bpt.getSupply()`, but rather use `bpt.getVirtualSupply()`. 32 | {% endhint %} 33 | 34 | **And if you want to calculate a pool value for a given address...** 35 | 36 | `myBptValueUsd = bpt.balanceOf(myAddress) * bptPriceUsd;` 37 | 38 | {% hint style="warning" %} 39 | The above assumes you have your BPT in your wallet. If you have staked your BPT in a gauge, you'll need to calculate your BPT holdings as:\ 40 | `myBpt = bpt.balanceOf(yourAddress) + bptGaugeDeposit.balanceOf(yourAddress);` 41 | {% endhint %} 42 | 43 | ### Estimating Price On-chain 44 | 45 | Balancer Labs is working on a contract to facilitate this. Coming Soon$$^{TM}$$ 46 | 47 | -------------------------------------------------------------------------------- /resources/pool-interfacing/oracle-pools.md: -------------------------------------------------------------------------------- 1 | # Oracle Pools 2 | 3 | ## Overview 4 | 5 | Oracle Pools are Balancer pools that can report back time-weighted average prices for tokens within the pool. 6 | 7 | Different `poolTypes` can implement Oracle Functionality. Since `WeightedPool2Tokens` were the first pool to implement the oracles, they were originally referred to as "Oracle Pools," but they are no longer alone in providing this functionality. 8 | 9 | ### Pool Types with Oracles 10 | 11 | The types of pools that have oracles include but are not limited to: 12 | 13 | * WeightedPool2Tokens ([Full API](../../references/valuing-balancer-lp-tokens/pools/weightedpool2tokens.md)) 14 | * MetaStablePool ([Full API](../../references/valuing-balancer-lp-tokens/pools/metastablepools.md)) 15 | 16 | ## Getting Oracle Data 17 | 18 | ### `getTimeWeightedAverage` 19 | 20 | ```cpp 21 | enum Variable { PAIR_PRICE, BPT_PRICE, INVARIANT } 22 | struct OracleAverageQuery { 23 | Variable variable; 24 | uint256 secs; 25 | uint256 ago; 26 | } 27 | getTimeWeightedAverage(OracleAverageQuery[] queries) 28 | returns (uint256[] results) 29 | ``` 30 | 31 | Returns time weighted average prices corresponding to the variables in each query. 32 | 33 | * `variable` - One of `{ PAIR_PRICE, BPT_PRICE, INVARIANT }` 34 | * `secs` - The duration of the query in seconds 35 | * `ago` - The time in seconds from since **end** of that duration. 36 | 37 | Prices are represented as 18 decimal fixed point values. 38 | 39 | ### `getLatest` 40 | 41 | ``` 42 | enum Variable { PAIR_PRICE, BPT_PRICE, INVARIANT } 43 | getLatest(Variable variable) returns (uint256) 44 | ``` 45 | 46 | For a simpler query (which is more prone to error/manipulation), you can also query getLatest to get the latest value of `variable`, which is one of `{ PAIR_PRICE, BPT_PRICE, INVARIANT }`. 47 | 48 | ### Additional Queries 49 | 50 | The above are just samples of what you can query. For a more complete list, please refer to the Full APIs for the [WeightedPool2Tokens](../../references/valuing-balancer-lp-tokens/pools/weightedpool2tokens.md) and [MetaStablePool](../../references/valuing-balancer-lp-tokens/pools/metastablepools.md). 51 | -------------------------------------------------------------------------------- /guides/data/README.md: -------------------------------------------------------------------------------- 1 | # Data 2 | 3 | If you're trying to interface with Balancer V2, chances are you're going to want to see what's going on. This section will go over some methods for querying Balancer data. 4 | 5 | ## Balancer Subgraph 6 | 7 | The Balancer Subgraph is a great way to query pools, balances, and more. The Subgraph tracks each pool by poolId, and can be queried using filtering and sorting techniques built into GraphQL. 8 | 9 | The hosted version of The Graph can experience downtime, so there are some cases where it's advantageous not to rely on it. As The Graph decentralizes, it will become more resilient and reliable. 10 | 11 | ## On-chain Queries 12 | 13 | Sometimes you might want to query the Ethereum (or EVM-compatible) blockchain directly for Balancer data. There is no on-chain list of Balancer pools, so you'll need to issue a preliminary query to the Subgraph, or maintain your own cached pool list. Depending on if you have a local node or a remote node (like Infura), query time and query volume may be concerns. We'll go over techniques for how to most efficiently query on-chain data. 14 | 15 | ## FAQs 16 | 17 | ### Is there a contract that I can query with two token addresses to get the pool address or `poolId`? 18 | 19 | No, Balancer does not have an on-chain registry of pools and their corresponding tokens. 20 | 21 | #### Why not? 22 | 23 | Balancer does not have canonical pools; anyone can deploy a pool of any composition they desire, so there is no guarantee that there exists only one DAI, WETH pool, for example. Since Balancer supports multi-token pools, DAI and WETH could be two tokens among many more in certain pools. Further, there could be pools of different types that have overlapping tokens, such as WeightedPool, StablePools, and LiquidityBootstrappingPools. 24 | 25 | #### Ok, but how do I get all pools that have DAI and WETH in them? 26 | 27 | The subgraph, of course! To query pools with specific tokens, do something like the following: 28 | 29 | ``` 30 | { 31 | pools(first: 100, where:{tokensList_contains:["0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","0x6b175474e89094c44da98b954eedeac495271d0f"]}) { 32 | id 33 | poolType 34 | tokens { 35 | address 36 | } 37 | } 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /resources/deploy-pools-from-factory/creation/metastable-pool.md: -------------------------------------------------------------------------------- 1 | # MetaStable Pool 2 | 3 | {% hint style="warning" %} 4 | This is a page for the original implementation of the MetaStable Pool. While it is valid to use one of these pools, it is advantageous to use the more flexible StablePhantomPool in many cases. 5 | 6 | * Support for nesting BPT with the use of Phantom BPT; pool join/exit operations are done with swaps 7 | {% endhint %} 8 | 9 | ## Common Arguments 10 | 11 | In addition to the arguments listed below, you should also consider the [common arguments](./#common-arguments) and [those of the StablePool](stablepool.md) when creating a MetaStable Pool. 12 | 13 | ## Pool Creation Arguments 14 | 15 | ### `rateProvider`s 16 | 17 | MetaStable Pools rely on rate providers to inform the exchange rate between the tokens in the pool. A rate provider is a contract which implements a `getRate()` function to return a price feed for its corresponding token. If you pass the zero address (`0x0...0`) as a token's rate provider, it will return a value of 1. 18 | 19 | #### When should I use the zero address as a `rateProvider`? 20 | 21 | If there is a pool with `TokenA` and `TokenB` and a rate provider `rateProviderAB` with a `getRate()` function that returns the price of `TokenA` relative to `TokenB`, then we can pass the address of `rateProviderAB` for `TokenA` and the zero address (`0x0...0`) for `TokenB`. 22 | 23 | #### When should I pass a `rateProvider` for each token? 24 | 25 | Let's say there is a pool with `TokenA` and `TokenB.` We also have a rate provider `rateProviderAC` with a `getRate()` function that returns the price of `TokenA` relative to `TokenC` and a rate provider `rateProviderBC` with a `getRate()` function that returns the price of `TokenB` relative to `TokenC`. 26 | 27 | Passing \[`rateProviderAC`, `rateProviderBC`] for \[`TokenA`, `TokenB`] respectively will give us the correct price since `PriceC` in each rate cancels out. 28 | 29 | $$ 30 | \frac{\frac{Price_A}{Price_C}}{\frac{Price_B}{Price_C}} = \frac{Price_A}{Price_B} 31 | $$ 32 | 33 | ### `priceRateCacheDuration` 34 | 35 | The `priceRateCacheDuration` for each `rateProvider` defines how long that the rate should be cached in seconds. Using a cached time saves gas on swaps. For prices that change very slowly, a long cache time can be helpful. A `priceRateCacheDuration` of zero will query the `rateProvider` each time. 36 | 37 | -------------------------------------------------------------------------------- /resources/deploy-pools-from-factory/creation/stablephantom-pool.md: -------------------------------------------------------------------------------- 1 | # StablePhantom Pool 2 | 3 | ## Common Arguments 4 | 5 | In addition to the arguments listed below, you should also consider the [common arguments](./#common-arguments) and [those of the StablePool](stablepool.md) when creating a StablePhantom Pool. 6 | 7 | {% hint style="warning" %} 8 | When performing an INIT join on a StablePhantom Pool, **you must include the BPT of the pool itself** as one of the `tokens` you provide. The amount you should pass for this token is the max UINT112 `(2**112 - 1)`. This is an artifact of the pool holding its own pre-minted, or "Phantom," BPT. 9 | 10 | 11 | 12 | After performing the `INIT` join, any other join on the pool will fail; with Phantom BPT, all _effective_ joins and exits are done via swapping to/from BPT within the pool. 13 | {% endhint %} 14 | 15 | ## Pool Creation Arguments 16 | 17 | ### `rateProvider`s 18 | 19 | MetaStable Pools rely on rate providers to inform the exchange rate between the tokens in the pool. A rate provider is a contract which implements a `getRate()` function to return a price feed for its corresponding token. If you pass the zero address (`0x0...0`) as a token's rate provider, it will return a value of 1. 20 | 21 | #### When should I use the zero address as a `rateProvider`? 22 | 23 | If there is a pool with `TokenA` and `TokenB` and a rate provider `rateProviderAB` with a `getRate()` function that returns the price of `TokenA` relative to `TokenB`, then we can pass the address of `rateProviderAB` for `TokenA` and the zero address (`0x0...0`) for `TokenB`. 24 | 25 | #### When should I pass a `rateProvider` for each token? 26 | 27 | Let's say there is a pool with `TokenA` and `TokenB.` We also have a rate provider `rateProviderAC` with a `getRate()` function that returns the price of `TokenA` relative to `TokenC` and a rate provider `rateProviderBC` with a `getRate()` function that returns the price of `TokenB` relative to `TokenC`. 28 | 29 | Passing \[`rateProviderAC`, `rateProviderBC`] for \[`TokenA`, `TokenB`] respectively will give us the correct price since `PriceC` in each rate cancels out. 30 | 31 | $$ 32 | \frac{\frac{Price_A}{Price_C}}{\frac{Price_B}{Price_C}} = \frac{Price_A}{Price_B} 33 | $$ 34 | 35 | ### `priceRateCacheDuration` 36 | 37 | The `priceRateCacheDuration` for each `rateProvider` defines how long that the rate should be cached in seconds. Using a cached time saves gas on swaps. For prices that change very slowly, a long cache time can be helpful. A `priceRateCacheDuration` of zero will query the `rateProvider` each time. 38 | 39 | -------------------------------------------------------------------------------- /resources/deploy-pools-from-factory/creation/boosted-metapool.md: -------------------------------------------------------------------------------- 1 | # Boosted MetaPool 2 | 3 | {% hint style="warning" %} 4 | This architecture can be applied to **any** type of Boosted Pool. For the sake of a concrete example, this page will explain how the process as it applies to AaveBoosted MetaPools. 5 | {% endhint %} 6 | 7 | ## Definition 8 | 9 | An AaveBoosted MetaPool is a StablePhantomPool that facilitates trades between existing AaveBoosted Stable Pool tokens and those of a new AaveBoosted LinearPool. 10 | 11 | For example, if we have a new US Dollar stablecoin called `USDX`, we could create an AaveBoosted LinearPool `bb-a-USDX` and put that in a StablePhantomPool with the existing `bb-a-USD` pool token. This would facilitate trades between: 12 | 13 | | Base Tokens | aTokens | 14 | | ----------- | ------- | 15 | | `DAI` | `aDAI` | 16 | | `USDC` | `aUSDC` | 17 | | `USDT` | `aUSDT` | 18 | | `USDX` | `aUSDX` | 19 | 20 | ![](<../../../.gitbook/assets/Screen Shot 2022-04-22 at 9.50.34 AM.png>) 21 | 22 | ## Advantages 23 | 24 | ### Why create an AaveBoosted MetaPool? 25 | 26 | * Stablecoins liquidity available for traders and Aave Boosting available for Liquidity Providers 27 | * Facilitate USDX swaps with DAI, USDC, USDT, and their respective aTokens without fracturing liquidity 28 | * As more MetaPools come online, bb-a-USD can become a common bridge for new stablecoins. If we have two MetaPools `[bb-a-USDX, bb-a-USD]` and `[bb-a-USDY, bb-a-USD]`, we can hop from `USDX` ->`bb-a-USDX` -> `bb-a-USD` -> `bb-a-USDY` -> `USDY`. 29 | 30 | ## Steps 31 | 32 | To build this pool, you will need to: 33 | 34 | 1. [Acquire some amount of `bb-a-USD`](boosted-metapool.md#acquire-some-amount-of-bb-a-usd)`` 35 | 2. [Deploy a `bb-a-USDX` AaveLinearPool](boosted-metapool.md#deploy-a-bb-a-usdx-aavelinearpool) 36 | 3. [Deploy a StablePhantomPool with both `bb-a-USD` and `bb-a-USDX`](boosted-metapool.md#deploy-a-stablephantompool-with-both-bb-a-usd-and-bb-a-usdx)`` 37 | 38 | ## Acquire some amount of `bb-a-USD` 39 | 40 | The easiest way to get `bb-a-USD` is to join the `bb-a-USD` pool with your choice of `DAI`, `USDC`, `USDT`, or one of their respective aTokens. For this, you can use the [Balancer UI](https://app.balancer.fi). 41 | 42 | ## Deploy a `bb-a-USDX` AaveLinearPool 43 | 44 | For instructions on how to launch bb-a-USDX, please refer to the [AaveLinearPool](aavelinearpool.md) instructions. 45 | 46 | ## Deploy a StablePhantomPool with both `bb-a-USD` and `bb-a-USDX` 47 | 48 | For instructions on how to launch a StablePhantomPool, please refer to the [StablePhantom Pool ](stablephantom-pool.md)instructions. 49 | -------------------------------------------------------------------------------- /references/valuing-balancer-lp-tokens/pools/investmentpools.md: -------------------------------------------------------------------------------- 1 | # ManagedPools (prev. "InvestmentPools") 2 | 3 | ## More functions are inherited 4 | 5 | This page breaks out the functions that are not common to all pools. See more functions on the [Pools page](./). 6 | 7 | ## API 8 | 9 | ### `onSwap` 10 | 11 | ```cpp 12 | // Inherited from BaseMinimalSwapInfoPool 13 | onSwap(SwapRequest request, 14 | uint256 balanceTokenIn, 15 | uint256 balanceTokenOut) 16 | returns (uint256 amount[In/Out]) 17 | ``` 18 | 19 | When the Vault is handling a swap, it will call `onSwap` to ask the pool what the amounts should be. Pools that use weighted math only need the input/output tokens to determine price. 20 | 21 | ### `getSwapEnabled` 22 | 23 | ```cpp 24 | function getSwapEnabled() 25 | returns (bool) 26 | ``` 27 | 28 | Returns `True` if the pool has swaps enabled. 29 | 30 | ### `getGradualWeightUpdateParam` 31 | 32 | ```cpp 33 | function getGradualWeightUpdateParams() 34 | returns ( 35 | uint256 startTime, 36 | uint256 endTime, 37 | uint256[] endWeights) 38 | ``` 39 | 40 | Return start time, end time, and endWeights as an array. Current weights should be retrieved via getNormalizedWeights(). 41 | 42 | ### `getManagementSwapFeePercentage` 43 | 44 | ``` 45 | function getManagementSwapFeePercentage() 46 | public view returns (uint256) 47 | ``` 48 | 49 | Returns the management swap fee percentage with 18 decimals. 50 | 51 | ### `getMinimumWeightChangeDuration` 52 | 53 | ``` 54 | function getMinimumWeightChangeDuration() 55 | returns (uint256) 56 | ``` 57 | 58 | Returns the minimum duration of a gradual weight change. 59 | 60 | ### `getCollectedManagementFees` 61 | 62 | ``` 63 | function getCollectedManagementFees() 64 | returns (IERC20[] tokens, uint256[] collectedFees) 65 | ``` 66 | 67 | Returns the amount of management fees collected 68 | 69 | ## Permissioned Functions 70 | 71 | All of the following functions are only callable by the pool owner. 72 | 73 | ### `setSwapEnabled` 74 | 75 | ```cpp 76 | function setSwapEnabled(bool swapEnabled) 77 | ``` 78 | 79 | Enables swaps if passed `True`, disables them if passed `False`. 80 | 81 | ### `updateWeightsGradually` 82 | 83 | ```cpp 84 | function updateWeightsGradually( 85 | uint256 startTime, 86 | uint256 endTime, 87 | uint256[] endWeights) 88 | ``` 89 | 90 | Schedule a gradual weight change, from the current weights to the given `endWeights`, from `startTime` to `endTime`. 91 | 92 | ### `withdrawCollectedManagementFees` 93 | 94 | ``` 95 | function withdrawCollectedManagementFees(address recipient) 96 | ``` 97 | 98 | Withdraw the collected management fees. 99 | 100 | -------------------------------------------------------------------------------- /resources/pool-interfacing/managed-pools.md: -------------------------------------------------------------------------------- 1 | # Managed Pools 2 | 3 | ## Overview 4 | 5 | For advantages and use cases of Managed Pools, please refer to [the standard documentation](https://docs.balancer.fi/products/balancer-pools/managed-pools). 6 | 7 | For more interfaces, such as updating pool weights, see the [Managed Pools API](../../references/valuing-balancer-lp-tokens/pools/investmentpools.md). 8 | 9 | ## Interfacing 10 | 11 | Some elements to consider when interfacing with Managed Pools: 12 | 13 | * Using [Weighted Math](../pool-math/weighted-math.md) 14 | * Pool weights can be dynamic 15 | * Pool swaps may be disabled by the pool owner. Typically this is to prevent swaps before the weight shifting occurs, but this can technically happen at any time. 16 | * Pool weights range from 1% to 99% 17 | * Pools can have up to 50 tokens 18 | 19 | ## Getting Pool Data 20 | 21 | In addition to the [common pool data](./#getting-common-pool-data), you will likely want the following data when interfacing with Liquidity Bootstrapping Pools: 22 | 23 | ### Weights 24 | 25 | #### Instantaneous Query 26 | 27 | Weights are stored at the pool level. For example, calling 28 | 29 | ``` 30 | pool.getNormalizedWeights() 31 | ``` 32 | 33 | returns something resembling 34 | 35 | ``` 36 | [800000000000000000, 200000000000000000] 37 | ``` 38 | 39 | which are the weights represented with 18 decimals. A pool with 80%/20% weights corresponds to \[0.8, 0.2] after scaling for decimals. It is important to note that this method will only query **instantaneous weights**. If you are querying a pool that is actively changing weights, the pool weights can differ between off-chain query and weights at time of execution. 40 | 41 | #### Calculate Weights at a Specific Time 42 | 43 | By combining the above instantaneous weight query with the weight-shifting parameters, we can calculate weights at a point in the future. Calling 44 | 45 | ``` 46 | pool.getGradualWeightUpdateParams() 47 | ``` 48 | 49 | returns something resembling 50 | 51 | ``` 52 | startTime : 1631523600 53 | endTime : 1638781200 54 | endWeights : [180010681315327688, 820004577706569009] 55 | ``` 56 | 57 | With these datapoints, you can calculate the weights at a given point in time by interpolating between the current weights and the final weights. 58 | 59 | ### Are Swaps Enabled? 60 | 61 | Pool owners may choose to enable/disable swaps based on what they're doing with their pool. Oftentimes, pool owners will have swaps paused before a weight-shifting event and enable them as the weight-shifting begins. Technically, however, pool owners can pause swaps whenever they like; therefore, it's important to check it swaps are enabled when dealing with these pools. Simply calling 62 | 63 | ``` 64 | pool.getSwapEnabled() 65 | ``` 66 | 67 | returns `True` or `False`. 68 | -------------------------------------------------------------------------------- /resources/pool-interfacing/liquidity-bootstrapping-pool.md: -------------------------------------------------------------------------------- 1 | # Liquidity Bootstrapping Pool 2 | 3 | ## Overview 4 | 5 | For advantages and use cases of Liquidity Bootstrapping Pools (LBPs), please refer to [the standard documentation](https://docs.balancer.fi/products/balancer-pools/liquidity-bootstrapping-pools-lbps). 6 | 7 | For more interfaces, such as updating pool weights, see the [Liquidity Bootstrapping Pool API](../../references/valuing-balancer-lp-tokens/pools/liquiditybootstrappingpool.md#api). 8 | 9 | ## Interfacing 10 | 11 | Some elements to consider when interfacing with Liquidity Bootstrapping Pools: 12 | 13 | * Using [Weighted Math](../pool-math/weighted-math.md) 14 | * Pool weights can be dynamic 15 | * Pool swaps may be disabled by the pool owner. Typically this is to prevent swaps before the weight shifting occurs, but this can technically happen at any time. 16 | * Pool weights range from 1% to 99% 17 | * Pools have between 2 and 4 tokens 18 | 19 | ## Getting Pool Data 20 | 21 | In addition to the [common pool data](./#getting-common-pool-data), you will likely want the following data when interfacing with Liquidity Bootstrapping Pools: 22 | 23 | ### Weights 24 | 25 | #### Instantaneous Query 26 | 27 | Weights are stored at the pool level. For example, calling 28 | 29 | ``` 30 | pool.getNormalizedWeights() 31 | ``` 32 | 33 | returns something resembling 34 | 35 | ``` 36 | [800000000000000000, 200000000000000000] 37 | ``` 38 | 39 | which are the weights represented with 18 decimals. A pool with 80%/20% weights corresponds to \[0.8, 0.2] after scaling for decimals. It is important to note that this method will only query **instantaneous weights**. If you are querying a pool that is actively changing weights, the pool weights can differ between off-chain query and weights at time of execution. 40 | 41 | #### Calculate Weights at a Specific Time 42 | 43 | By combining the above instantaneous weight query with the weight-shifting parameters, we can calculate weights at a point in the future. Calling 44 | 45 | ``` 46 | pool.getGradualWeightUpdateParams() 47 | ``` 48 | 49 | returns something resembling 50 | 51 | ``` 52 | startTime : 1631523600 53 | endTime : 1638781200 54 | endWeights : [180010681315327688, 820004577706569009] 55 | ``` 56 | 57 | With these datapoints, you can calculate the weights at a given point in time by interpolating between the current weights and the final weights. 58 | 59 | ### Are Swaps Enabled? 60 | 61 | Pool owners may choose to enable/disable swaps based on what they're doing with their pool. Oftentimes, pool owners will have swaps paused before a weight-shifting event and enable them as the weight-shifting begins. Technically, however, pool owners can pause swaps whenever they like; therefore, it's important to check it swaps are enabled when dealing with these pools. Simply calling 62 | 63 | ``` 64 | pool.getSwapEnabled() 65 | ``` 66 | 67 | returns `True` or `False`. 68 | -------------------------------------------------------------------------------- /resources/swaps/single-swap.md: -------------------------------------------------------------------------------- 1 | # Single Swap 2 | 3 | ## Function and Struct Definitions 4 | 5 | ### Swap function 6 | 7 | ``` 8 | swap(SingleSwap singleSwap, 9 | FundManagement funds, 10 | uint256 limit, 11 | uint256 deadline) returns (uint256 amountCalculated[In/Out]) 12 | ``` 13 | 14 | * `singleSwap`: A definition of the swap to be executed, defined below 15 | * `funds`: A definition of where funds are going to/from, defined below 16 | * `limit`: The meaning of `limit` depends on the value of `singleSwap.kind` 17 | * `GIVEN_IN`: The minimum amount of tokens we would accept to receive from the swap. 18 | * `GIVEN_OUT`: The maximum amount of tokens we would accept having to send for the swap. 19 | * `deadline`: The UNIX timestamp at which our trade must be completed by - if the transaction is confirmed after this time then the transaction will fail. 20 | 21 | ### SingleSwap struct 22 | 23 | The `SingleSwap` struct defines which pool we're trading with and what kind of swap we want to perform. The `SingleSwap` struct is defined as below. 24 | 25 | ``` 26 | enum SwapKind { GIVEN_IN, GIVEN_OUT } 27 | 28 | struct SingleSwap { 29 | bytes32 poolId; 30 | SwapKind kind; 31 | IAsset assetIn; 32 | IAsset assetOut; 33 | uint256 amount; 34 | bytes userData; 35 | } 36 | ``` 37 | 38 | * `poolId`: The id of the pool to trade with. 39 | * `kind`: The type of swap we want to perform - either "Out Given In" or "In Given Out." We either know the amount of tokens we're sending to the pool and want to know how many we'll receive, or vice versa. 40 | * `assetIn`: The address of the token which we are sending to the pool. 41 | * `assetOut`: The address of the token which we will receive in return. 42 | * `amount`: The meaning of `amount` depends on the value of `kind`. 43 | * `GIVEN_IN`: The amount of tokens we are sending to the pool. 44 | * `GIVEN_OUT`: The amount of tokens we want to receive from the pool. 45 | * `userData`: Any additional data which the pool requires to perform the swap. This allows pools to have more flexible swapping logic in future - for all current Balancer pools this can be left empty. 46 | 47 | ### FundManagement struct 48 | 49 | The `FundManagement` struct defines where the input tokens for the swap are coming from and where the output tokens should be sent. The `FundManagement` struct is defined as below. 50 | 51 | ``` 52 | struct FundManagement { 53 | address sender; 54 | bool fromInternalBalance; 55 | address payable recipient; 56 | bool toInternalBalance; 57 | } 58 | ``` 59 | 60 | * `sender`: The address from which tokens will be taken to perform the trade 61 | * `fromInternalBalance`: Whether the trade should use tokens owned by the `sender` which are already stored in the Vault. 62 | * `recipient`: The address to which tokens will be sent to after the trade. 63 | * `toInternalBalance`: Whether the tokens should be sent to the `recipient` or stored within their internal balance within the Vault. 64 | -------------------------------------------------------------------------------- /resources/internal-user-balances.md: -------------------------------------------------------------------------------- 1 | # Internal User Balances 2 | 3 | ## Overview 4 | 5 | Similar to how the Vault keeps track of what tokens are in a pool, the Vault can also maintain balances for users or any other smart contract. Balances can be deposited to, withdrawn from, and transferred. When utilizing internal balances in Balancer transactions (swap/join/trade), gas costs are reduced since there are fewer (or no) ERC20 tokens transferred and everything is handled by the Vault's bookkeeping. 6 | 7 | ## API 8 | 9 | ```cpp 10 | // Vault function 11 | manageUserBalance( 12 | UserBalanceOp[] ops 13 | ) 14 | 15 | // Struct Definition 16 | UserBalanceOp{ 17 | uint8 kind, 18 | address asset, 19 | uint256 amount, 20 | address sender, 21 | address recipient 22 | } 23 | 24 | // Relevant Enum definition for "kind" 25 | enum UserBalanceOpKind { 26 | DEPOSIT_INTERNAL, 27 | WITHDRAW_INTERNAL, 28 | TRANSFER_INTERNAL, 29 | TRANSFER_EXTERNAL 30 | } 31 | ``` 32 | 33 | ### Arguments Explained 34 | 35 | * `ops` - An array of UserBalanceOps, explained below 36 | * `kind` - Enum of type `UserBalanceOpKind` 37 | * `asset` - The token you are moving 38 | * `sender` - Address sending tokens or internal balance 39 | * `recipient` - Address receiving tokens or internal balance 40 | 41 | ### Enums Explained 42 | 43 | This explanation is copied for convenience from the original [UserBalanceOpKind definition in IVault.sol](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/vault/contracts/interfaces/IVault.sol#L181). 44 | 45 | * `DEPOSIT_INTERNAL` 46 | * Increases the Internal Balance of the `recipient` account by transferring tokens from the corresponding`sender`. The sender must have allowed the Vault to use their tokens via `IERC20.approve()`. ETH can be used by passing the ETH sentinel value (the zero address) as the asset and forwarding ETH in the call: it will be wrapped and deposited as WETH. Any ETH amount remaining will be sent back to the caller (not the sender, which is relevant for relayers). Emits an `InternalBalanceChanged` event. 47 | * `WITHDRAW_INTERNAL` 48 | * Decreases the Internal Balance of the `sender` account by transferring tokens to the `recipient`. ETH can be used by passing the ETH sentinel value (the zero address) as the asset. This will deduct WETH instead, unwrap it and send 49 | 50 | it to the recipient as ETH. Emits an `InternalBalanceChanged` event. 51 | * `TRANSFER_INTERNAL` 52 | * Transfers tokens from the Internal Balance of the `sender` account to the Internal Balance of `recipient`. Reverts if the ETH sentinel value (the zero address) is passed. Emits an `InternalBalanceChanged` event. 53 | * `TRANSFER_EXTERNAL` 54 | * Transfers tokens from `sender` to `recipient`, using the Vault's ERC20 allowance. This is typically used by relayers, as it lets them reuse a user's Vault allowance. Reverts if the ETH sentinel value (the zero address) is passed. Emits an `ExternalBalanceTransfer` event. 55 | -------------------------------------------------------------------------------- /helpers/using-native-eth.md: -------------------------------------------------------------------------------- 1 | # Using Native ETH 2 | 3 | {% hint style="info" %} 4 | This article discusses how the Vault can wrap ETH to WETH. While this concept applies to the native assets on other EVM compatible chains (ex. MATIC on Polygon), this article will refer to native assets as ETH and their wrapped counterparts as WETH. 5 | {% endhint %} 6 | 7 | ## Overview 8 | 9 | While native ETH is not _directly_ compatible with Balancer, the Vault can automatically wrap/unwrap ETH to/from WETH when performing typical operations. 10 | 11 | ## Sentinel Value 12 | 13 | In the documentation and smart contract comments, you may find references to the _Sentinel Value_ when dealing with Native ETH. This is a placeholder address to denote that the asset you're dealing with is not an ERC20 token, but is instead Native ETH. 14 | 15 | When dealing with Native ETH, the Sentinel Value you'll provide is the zero address`0x0000000000000000000000000000000000000000` 16 | 17 | ## Swaps 18 | 19 | ### Single Swaps 20 | 21 | If you wish to send or receive native ETH in a single swap, simply provide the sentinel address for `assetIn` or `assetOut` respectively. 22 | 23 | ### Batch Swaps 24 | 25 | If you wish to send or receive native ETH in a batch swap, provide the sentinel address in the `assets` array and refer to it by its index in your swap steps as you would any other token. For Batch Swaps, you must sort the sentinel address numerically; do not sort it in the array as if it is the WETH address.\ 26 | \ 27 | Note: it is possible to send ETH and WETH in the same swap by providing both of the corresponding asset indices as inputs. 28 | 29 | ## Join/Exit 30 | 31 | When joining or exiting a pool, you have to construct a `JoinPoolRequest` or `ExitPoolRequest` struct. Within this struct, you have the `assets` array: 32 | 33 | ```cpp 34 | address[] assets 35 | ``` 36 | 37 | As you'll find in the documentation for [Joins and Exits](../resources/joins-and-exits/), this array must be sorted numerically; there is a caveat here though. If you wish to join with or exit to Native ETH, you need to **order the array** **as if you're dealing with WETH**. **** Note that it is not possible to combine ETH and WETH in the same join/exit; any excess ETH will be sent back to the caller (not the sender, which is important for relayers). 38 | 39 | #### Correctly Ordered Example with WETH 40 | 41 | | Token | Address | 42 | | ----- | ------------------------------------------ | 43 | | DAI | 0x6b175474e89094c44da98b954eedeac495271d0f | 44 | | BAL | 0xba100000625a3754423978a60c9317c58a424e3D | 45 | | WETH | 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 | 46 | 47 | **Correctly Ordered Example with Native ETH** 48 | 49 | | Token | Address | 50 | | ----- | ------------------------------------------ | 51 | | DAI | 0x6b175474e89094c44da98b954eedeac495271d0f | 52 | | BAL | 0xba100000625a3754423978a60c9317c58a424e3D | 53 | | ETH | 0x0000000000000000000000000000000000000000 | 54 | -------------------------------------------------------------------------------- /references/valuing-balancer-lp-tokens/pools/weightedpool2tokens.md: -------------------------------------------------------------------------------- 1 | # WeightedPool2Tokens 2 | 3 | ## More functions are inherited 4 | 5 | This page breaks out the functions that are not common to all pools. See more functions on the [Pools page](./). 6 | 7 | ## API 8 | 9 | ### `onSwap` 10 | 11 | ```cpp 12 | // Inherited from BaseMinimalSwapInfoPool 13 | onSwap(SwapRequest request, 14 | uint256 balanceTokenIn, 15 | uint256 balanceTokenOut) 16 | returns (uint256 amount[In/Out]) 17 | ``` 18 | 19 | When the Vault is handling a swap, it will call `onSwap` to ask the pool what the amounts should be. Pools that use weighted math only need the input/output tokens to determine price. 20 | 21 | ### `enableOracle` 22 | 23 | ```cpp 24 | enableOracle() 25 | ``` 26 | 27 | Enables the oracle functionality. 28 | 29 | ### `getMiscData` 30 | 31 | ```cpp 32 | getMiscData() 33 | returns ( 34 | int256 logInvariant, 35 | int256 logTotalSupply, 36 | uint256 oracleSampleCreationTimestamp, 37 | uint256 oracleIndex, 38 | bool oracleEnabled, 39 | uint256 swapFeePercentage) 40 | ``` 41 | 42 | Returns a variety of data fields, listed above. 43 | 44 | ### `getLargestSafeQueryWindow` 45 | 46 | ```cpp 47 | getLargestSafeQueryWindow() 48 | returns (uint256) 49 | ``` 50 | 51 | Returns largest safe query window. 52 | 53 | ### `getLatest` 54 | 55 | ```cpp 56 | enum Variable { PAIR_PRICE, BPT_PRICE, INVARIANT } 57 | getLatest(Variable variable) returns (uint256) 58 | ``` 59 | 60 | Returns latest pair price, BPT price, or invariant depending on what `variable` enum you pass. Samples are recorded by the pool as calculated with the pre-operation balances. For example, the spot price _**before**_ a swap is the value stored as the most recent `PAIR_PRICE`. 61 | 62 | ### `getTimeWeightedAverage` 63 | 64 | ```cpp 65 | enum Variable { PAIR_PRICE, BPT_PRICE, INVARIANT } 66 | struct OracleAverageQuery { 67 | Variable variable; 68 | uint256 secs; 69 | uint256 ago; 70 | } 71 | getTimeWeightedAverage(OracleAverageQuery[] queries) 72 | returns (uint256[] results) 73 | ``` 74 | 75 | Returns time weighted average prices corresponding to the variables in each query. `secs` is the duration of the query in seconds, and `ago` is the time in seconds from since **end** of that duration. Prices are represented as 18 decimal fixed point values. 76 | 77 | {% hint style="warning" %} 78 | Note that you can only call `getTimeWeightedAverage` after the buffer is full, or it will revert with `ORACLE_NOT_INITIALIZED`. If you call `getSample(1023)` and it returns 0's, that means the buffer's not full yet. 79 | {% endhint %} 80 | 81 | ### `getPastAccumulators` 82 | 83 | ```cpp 84 | enum Variable { PAIR_PRICE, BPT_PRICE, INVARIANT } 85 | struct OracleAccumulatorQuery { 86 | Variable variable; 87 | uint256 ago; 88 | } 89 | getPastAccumulators(OracleAccumulatorQuery[] queries 90 | returns (int256[] results) 91 | ``` 92 | 93 | Returns estimates for the accumulator at a time `ago` seconds ago for each query. 94 | -------------------------------------------------------------------------------- /resources/swaps/flash-swaps.md: -------------------------------------------------------------------------------- 1 | # Flash Swaps 2 | 3 | ## Overview 4 | 5 | {% hint style="info" %} 6 | To understand Flash Swaps, make sure you understand [Batch Swaps](batch-swaps.md) first. 7 | {% endhint %} 8 | 9 | Anyone who identifies a price discrepancy in two Balancer Pools can execute a **Flash Swap**. **** An arbitrageur who makes a flash swap **does not need to hold any** of the input **tokens** that one would normally need to make a trade. Instead, the trader identifies the imbalance, tells the Vault to make the swap, and is rewarded with the profit. 10 | 11 | ## Example 12 | 13 | In this sample transaction ([click here for logs](https://kovan.etherscan.io/tx/0x3afd88c42a8bd1ff696c38f4232da3b872a783660d54417db8c3e33f6ab957a4#eventlog) and [click here for execution trace](https://dashboard.tenderly.co/tx/kovan/0x3afd88c42a8bd1ff696c38f4232da3b872a783660d54417db8c3e33f6ab957a4)), the sender identifies a price discrepancy between two pools that contain the same two tokens. They realize that if they were to sell 1,000,000 units of token `0xc256`to pool `0x3a19` and then sell the proceeds to pool `0x32fc` they would come out with more than 1,000,000 units of token `0xc256`. Even though the sender doesn't hold any of the tokens, they make a call to `batchSwap.` 14 | 15 | ``` 16 | "kind": "0", 17 | "assets": [ 18 | "0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115", 19 | "0xdfcea9088c8a88a76ff74892c1457c17dfeef9c1" 20 | ], 21 | "limits": [ 22 | "0", 23 | "0" 24 | ], 25 | ``` 26 | 27 | Where: 28 | 29 | * `kind`: GIVEN\_IN (= 0) means that the trades are formatted such that the amount represents the amount being sent to the pool 30 | * `assets`: the list of tokens used in the trade 31 | * `limits`: the number of tokens the trader is willing to send to the Vault For a Flash Swap, these are set to zero (or less) since a Flash Swap doesn't require the trader to send any tokens to the Vault) 32 | 33 | ``` 34 | "swaps": [ 35 | { 36 | "poolId": "0x3a19030ed746bd1c3f2b0f996ff9479af04c5f0a000200000000000000000004", 37 | "assetInIndex": "0", 38 | "assetOutIndex": "1", 39 | "amount": "1000000" 40 | }, 41 | { 42 | "poolId": "0x32fc95287b14eaef3afa92cccc48c285ee3a280a000100000000000000000005", 43 | "assetInIndex": "1", 44 | "assetOutIndex": "0", 45 | "amount": "0" 46 | } 47 | ], 48 | ``` 49 | 50 | Here we see our swap definitions. The first swap, shows an exchange of 1000000 units of `assets[0]` for `assets[1]` in pool `0x3a19`. The next swap shows the opposite swap of `assets[1]` for `assets[0]` in pool `0x32fc`. You may notice in the second swap that the `amount` field is set to `0`. Sending zero as the amount in a swap step simple means it uses the value output by the previous swap. 51 | 52 | ``` 53 | "funds": { 54 | "sender": "0x44c42303d71fc693d553d71309c80461010b8457", 55 | "fromInternalBalance": false, 56 | "recipient": "0x44c42303d71fc693d553d71309c80461010b8457", 57 | "toInternalBalance": false 58 | }, 59 | "deadline": "999999999999999999" 60 | ``` 61 | 62 | Finally, the `FundManagement` struct and `deadline` are set the same way as any other `batchSwap`. The user defines the `sender` and `recipient` as themselves, and sets both internal balance flags to `false`. The net return after the two swaps is 2285700 units of token `0xc256`, which is sent directly to the `recipient`. 63 | -------------------------------------------------------------------------------- /references/subgraphs.md: -------------------------------------------------------------------------------- 1 | # Subgraphs 2 | 3 | The Balancer Subgraph indexes data on the Balancer smart contracts with a GraphQL interface. It updates data in response to function calls and contract events to maintain data on the `Vault`, `Pools`, `AssetManagers` etc, to power front-end apps and integrations. 4 | 5 | {% hint style="warning" %} 6 | Note that Balancer has not yet migrated to the new Subgraph Studio mainnet. Any Balancer subgraphs appearing there should not be considered "official" deployments. Use at your own risk! 7 | {% endhint %} 8 | 9 | | Network | Subgraph URL | 10 | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 11 | | Ethereum Mainnet | [https://thegraph.com/legacy-explorer/subgraph/balancer-labs/balancer-v2](https://thegraph.com/legacy-explorer/subgraph/balancer-labs/balancer-v2) | 12 | | Polygon | [https://thegraph.com/legacy-explorer/subgraph/balancer-labs/balancer-polygon-v2](https://thegraph.com/legacy-explorer/subgraph/balancer-labs/balancer-polygon-v2) | 13 | | Arbitrum | [https://thegraph.com/legacy-explorer/subgraph/balancer-labs/balancer-arbitrum-v2](https://thegraph.com/legacy-explorer/subgraph/balancer-labs/balancer-arbitrum-v2) | 14 | | Goerli | [https://thegraph.com/legacy-explorer/subgraph/balancer-labs/balancer-goerli-v2](https://thegraph.com/legacy-explorer/subgraph/balancer-labs/balancer-goerli-v2) | 15 | 16 | ### GraphQL Schema 17 | 18 | The schema of GraphQL elements available is defined in [`/schema.graphql` ](https://github.com/balancer-labs/balancer-subgraph-v2/blob/master/schema.graphql) 19 | 20 | The data included in this subgraph data layer is the data that is most applicable to the front-end. It aims at the very least to keep track of all the resources in the `Vault` contract, and keep track of basic pool data. 21 | 22 | ### Querying 23 | 24 | {% hint style="info" %} 25 | For an in-depth explanation of how to query the Subgraph, check out the [Balancer Subgraph](../guides/data/balancer-subgraph.md) article in Guides. 26 | {% endhint %} 27 | 28 | #### Examples 29 | 30 | Pools with > $100k liquidity 31 | 32 | ```graphql 33 | { 34 | pools(first: 1000, where: {totalLiquidity_gt: 100000}) { 35 | address, 36 | tokensList, 37 | totalLiquidity 38 | } 39 | } 40 | ``` 41 | 42 | Historical liquidity of a pool 43 | 44 | ```graphql 45 | { 46 | poolHistoricalLiquidities (where: {poolId: "0x09253c3554fb7242608ff67ce048918ccf7f9a96000200000000000000000009"}) { 47 | block, 48 | poolLiquidity 49 | } 50 | } 51 | ``` 52 | 53 | Fetch a Liquidity Provider's shares 54 | 55 | ```graphql 56 | { 57 | poolShares(first: 1000, where: {userAddress: "0xef8305e140ac520225daf050e2f71d5fbcc543e7", balance_gt: 0}) { 58 | balance 59 | poolId { 60 | tokensList, 61 | totalShares 62 | } 63 | } 64 | } 65 | ``` 66 | 67 | Fetch historical metrics for a given pool 68 | 69 | ```graphql 70 | { 71 | poolSnapshots(first:1000, orderBy: timestamp, orderDirection: asc, where: {pool: "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014"}) { 72 | amounts 73 | totalShares 74 | swapVolume 75 | swapFees 76 | liquidity 77 | pool { 78 | id 79 | } 80 | } 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /resources/pool-math/stable-math.md: -------------------------------------------------------------------------------- 1 | # Stable Math 2 | 3 | ## Overview 4 | 5 | Stable Math is designed to allow for swaps between any assets that have the same price, or are "pegged" to the same asset. The most common examples are stablecoins that track US Dollars (DAI, USDT, USDC), and assets that track the price of Bitcoin (WBTC, renBTC, sBTC). Prices are determined by the pool balances, the _amplification parameter_, and amounts of the tokens that are being swapped. 6 | 7 | ## Implementations 8 | 9 | ### TypeScript 10 | 11 | Developers can use the TypeScript math implementations used by the Smart Order router 12 | 13 | * [stableMath.ts](https://github.com/balancer-labs/balancer-sor/blob/john/v2-package-linear/src/pools/stablePool/stableMath.ts) 14 | * [metaStableMath.ts](https://github.com/balancer-labs/balancer-sor/blob/john/v2-package-linear/src/pools/metaStablePool/metaStableMath.ts) 15 | 16 | ### Python 17 | 18 | There are also Python implementations in progress 19 | 20 | * ~~~~[~~stableMath.py~~](https://github.com/officialnico/balancerv2cad/blob/main/src/balancerv2cad/StableMath.py) There are known bugs in this implementation. This warning will be removed when they are fixed. 21 | 22 | ## Invariant 23 | 24 | Since the Stable Math equation is quite complex, determining the invariant, $$D$$, is typically done iteratively. For an example of how to do this, please refer to [this function](https://github.com/georgeroman/balancer-v2-pools/blob/main/src/pools/stable/math.ts#L16). 25 | 26 | $$ 27 | A \cdot n^n \cdot \sum{x_i} +D = A \cdot D \cdot n^n + { \frac{D^{n+1}}{{n}^{n}\cdot \prod{x_i} } } 28 | $$ 29 | 30 | Where: 31 | 32 | * $$n$$ is the number of tokens 33 | * $$x_i$$ is is balance of token $$i$$ 34 | * $$A$$ is the amplification parameter 35 | 36 | ## Trade Equations 37 | 38 | 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). 39 | 40 | ### outGivenIn 41 | 42 | $$ 43 | y^2 + (\frac{D}{An^n} + \sum_{j \neq out}{x'_j} - D)y -\frac{D^{n+1}}{An^{2n} \prod_{j \neq out}{x'_j}}= 0 44 | $$ 45 | 46 | $$ 47 | a_{out} = x_{out} - x'_{out} = x_{out} - y 48 | $$ 49 | 50 | Where: 51 | 52 | * $$x'_i$$ is the **ending** amount of each token 53 | * $$a_{out}$$is the amount out 54 | * $$x_{out}$$is the **starting** amount of the output token 55 | * $$y = x'_{out}$$is the **ending** amount of the output token 56 | * $$D$$ is the pool invariant 57 | * $$A$$ is the amplification parameter 58 | * $$n$$ is the number of tokens 59 | 60 | ### inGivenOut 61 | 62 | $$ 63 | y^2 + (\frac{D}{An^n} + \sum_{j \neq in}{x'_j} - D)y -\frac{D^{n+1}}{An^{2n} \prod_{j \neq in}{x'_j}}= 0 64 | $$ 65 | 66 | $$ 67 | a_{in} = x'_{in} - x_{in} = y-x_{in} 68 | $$ 69 | 70 | Where: 71 | 72 | * $$x'_i$$ is the **ending** amount of each token 73 | * $$a_{in}$$is the amount in 74 | * $$x_{in}$$is the **starting** amount of the input token 75 | * $$y = x'_{in}$$is the **ending** amount of the input token 76 | * $$D$$ is the pool invariant 77 | * $$A$$ is the amplification parameter 78 | * $$n$$ is the number of tokens 79 | -------------------------------------------------------------------------------- /resources/rate-providers.md: -------------------------------------------------------------------------------- 1 | # Rate Providers 2 | 3 | ## Overview 4 | 5 | Rate Providers are contracts that provide an exchange rate between two assets. These exchange rates can come from any on-chain source, whether that may be an oracle, a ratio of queryable balances, or another calculation. 6 | 7 | Rate Providers implement a `getRate()` function that returns an exchange rate. 8 | 9 | ## Use Cases 10 | 11 | You can use `rateProvider`s for all, some, or none of the assets in your pool. If you are not using a rateProvider for an asset, you must pass the zero address (`0x0000000000000000000000000000000000000000`), which will result in a rate of 1. 12 | 13 | ### All Assets 14 | 15 | You will want to use `rateProvider`s for all assets in your pool when each asset has its own price that is independent of all the other assets' prices. If we have tokens A, B, and C and only have price feeds with respect to USD, then we would want all assets to have price feeds. When internally calculating relative prices, the USD would cancel out, giving us prices for A:B, A:C, B:C, and their inverses. 16 | 17 | ### Some Assets 18 | 19 | You will want to use `rateProvider`s for some assets in your pool when you have rates that directly convert between the assets. If we have tokens A and B and a rate provider that gives the price of A with respect to B, then the `rateProvider` corresponding to token A would get the A:B price feed, and the `rateProvider` corresponding to token B would be the zero address. 20 | 21 | ### None of the Assets 22 | 23 | You will have no `rateProvider`s in your pool when your tokens are price-pegged to each other. For example, a pool with `USDC`, `USDT`, and `DAI` would have all `rateProvider`s set to the zero address since the exchange rate between those tokens is 1. 24 | 25 | ## Examples 26 | 27 | ### Direct Balance Query 28 | 29 | Wrapping rebasing tokens, such as `stETH`, makes them compatible with Balancer, but knowing the exchange rate between the underlying rebasing token and the wrapped token is necessary to facilitate Stableswap trades. As such, the `wstETH` `rateProvider` has a `getRate()` function that calls `wstETH`'s own `stEthPerToken()` function. [See the contract here](https://github.com/balancer-labs/metastable-rate-providers/blob/master/contracts/WstETHRateProvider.sol). 30 | 31 | ### Oracles 32 | 33 | Using oracles for price feeds is a simple way to determine an exchange rate. There are two example contracts for how to use Chainlink as a price source: [`ChainlinkRegistryRateProvider`](https://github.com/balancer-labs/metastable-rate-providers/blob/master/contracts/ChainlinkRegistryRateProvider.sol) and [`ChainlinkRateProvider`](https://github.com/balancer-labs/metastable-rate-providers/blob/master/contracts/ChainlinkRateProvider.sol). 34 | 35 | #### ``[`ChainlinkRegistryRateProvider`](https://github.com/balancer-labs/metastable-rate-providers/blob/master/contracts/ChainlinkRegistryRateProvider.sol)`` 36 | 37 | This contract makes use of Chainlink's registry contract so it can handle if Chainlink migrates to a new price feed for a given asset pair. Though there are increased gas costs for this, its a tradeoff for ensuring the pool doesn't get stuck on an abandoned price feed. While this is an unlikely scenario, it doesn't hurt to be careful. 38 | 39 | #### ``[`ChainlinkRateProvider`](https://github.com/balancer-labs/metastable-rate-providers/blob/master/contracts/ChainlinkRateProvider.sol)`` 40 | 41 | If you're running on a network for which Chainlink doesn't have a registry and you think the risk of a deprecated price feed is low enough, then you can use the rateProvider that directly queries a given Chainlink oracle. 42 | -------------------------------------------------------------------------------- /resources/pool-math/weighted-math.md: -------------------------------------------------------------------------------- 1 | # Weighted Math 2 | 3 | ## Overview 4 | 5 | Weighted Math is designed to allow for swaps between any assets whether or not they have any price correlation. Prices are determined by the pool balances, pool weights, and amounts of the tokens that are being swapped. 6 | 7 | Balancer's Weighted Math equation is a generalization of the $$x*y=k$$ constant product formula, accounting for cases with $$n \geq2$$ tokens as well as weightings that are not an even 50/50 split. 8 | 9 | For more formulas and derivations of the below formulas, please refer to the [Balancer Whitepaper](https://balancer.fi/whitepaper.pdf). 10 | 11 | ## Implementations 12 | 13 | ### TypeScript 14 | 15 | Developers can use the TypeScript math implementations used by the Smart Order router 16 | 17 | * [weightedMath.ts](https://github.com/balancer-labs/balancer-sor/blob/john/v2-package-linear/src/pools/weightedPool/weightedMath.ts) 18 | 19 | ### Python 20 | 21 | There are also Python implementations in progress 22 | 23 | * [weightedMath.py](https://github.com/officialnico/balancerv2cad/blob/main/src/balancerv2cad/WeightedMath.py) 24 | 25 | ## Invariant 26 | 27 | The value function $$V$$is defined as: 28 | 29 | $$ 30 | V= \prod_t B_t^{W_t} 31 | $$ 32 | 33 | Where 34 | 35 | * $$t$$ ranges over the tokens in the pool 36 | * $$B_t$$ is the balance of the token in the pool 37 | * $$W_t$$​is the normalized weight of the tokens, such that the sum of all normalized weights is 1. 38 | 39 | ## Spot Price 40 | 41 | Each pair of tokens in a pool has a spot price defined entirely by the weights and balances of just that pair of tokens. The spot price between any two tokens,$$SpotPrice^o_i$$, or in short $$SP^o_i$$, is the the ratio of the token balances normalized by their weights: 42 | 43 | $$ 44 | SP^o_i = \frac{\frac{B_i}{W_i}}{\frac{B_o}{W_o}} 45 | $$ 46 | 47 | * $$B_i$$ is the balance of token $$i$$, the token being sold by the trader which is going into the pool 48 | * $$B_o$$ is the balance of token $$o$$, the token being bought by the trader which is going out of the pool 49 | * $$W_i$$ is the weight of token $$i$$ 50 | * $$W_o$$ is the weight of token $$o$$ 51 | 52 | ### Spot Price with Swap Fees 53 | 54 | When we consider swap fees, we do exactly the same calculations as without fees, but using $$A_i \cdot (1-swapFee)$$ instead of $$A_i$$ since fees are taken out of the input amount. The equation then becomes: 55 | 56 | $$ 57 | SP^o_i = \frac{\frac{B_i}{W_i}}{\frac{B_o}{W_o}} \cdot \frac{1}{1-swapFee} 58 | $$ 59 | 60 | ## Trade Equations 61 | 62 | ### outGivenIn 63 | 64 | When a user sends tokens $$i$$ to get tokens $$o$$, all other token balances remain the same. Therefore, if we define $$A_i$$ and $$A_o$$ as the amount of tokens $$i$$ and $$o$$ exchanged, and since the value function $$V$$ must be constant before and after the trade, we can calculate the amount $$A_o$$ a users gets when sending $$A_i$$. 65 | 66 | $$ 67 | A_o = B_o \cdot \left(1-\left(\frac{B_i}{B_i + A_i}\right)^{\frac{W_i}{W_o}}\right) 68 | $$ 69 | 70 | {% hint style="info" %} 71 | If you're computing this value yourself, remember that the pool collects swap fees as a percentage of the **input token**. In the equation above,$$A_i$$ is the amount that the pool actually swaps into the output token, not the amount sent by a trader, $$A_{sent}$$. To calculate through, we must compute:$$A_i = A_{sent} * (1-swapFee)$$ 72 | {% endhint %} 73 | 74 | ### inGivenOut 75 | 76 | It is also very useful for traders to know how much they need to send of the input token $$A_i$$ to get a desired amount of output token $$A_o$$: 77 | 78 | $$ 79 | A_i = B_i \cdot \left(\left(\frac{B_o}{B_o - A_o}\right)^{\frac{W_o}{W_i}}-1\right) 80 | $$ 81 | -------------------------------------------------------------------------------- /resources/pool-interfacing/README.md: -------------------------------------------------------------------------------- 1 | # Pool Interfacing 2 | 3 | ## Overview 4 | 5 | Since there are many different pool types, it's important to note the differences between them when interfacing with Balancer. Some pool use different pricing equations, some have dynamic pricing, and some might have swaps disabled periodically. 6 | 7 | ## `poolId`s 8 | 9 | If you want to interface with a pool, you'll first need to know its `poolId`. The `poolId` is a unique identifier, the first portion of which is the pool's contract address. For example, the pool with the id `0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014` has a contract address of `0x5c6ee304399dbdb9c8ef030ab642b10820db8f56`. 10 | 11 | You can get a `poolId` from: 12 | 13 | * A pool's URL: [https://app.balancer.fi/#/pool/0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014](https://app.balancer.fi/#/pool/0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014) 14 | * The [Subgraph](https://thegraph.com/hosted-service/subgraph/balancer-labs/balancer-v2) 15 | * Calling `getPoolId()` on the contract itself if you already have it 16 | 17 | ## Getting Common Pool Data 18 | 19 | {% hint style="warning" %} 20 | Below are the data fields common to all pools; however, each pool will have data specific to its pool type. 21 | {% endhint %} 22 | 23 | ### Pool Balances 24 | 25 | Since all tokens are held in the Vault, **you must query the Vault when querying on-chain pool balances**. For example, calling 26 | 27 | ``` 28 | vault.getPoolTokens(0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014_ 29 | ``` 30 | 31 | returns something resembling: 32 | 33 | ``` 34 | tokens: [0xba100000625a3754423978a60c9317c58a424e3D, 35 | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2] 36 | 37 | balances: [5720903090084350251216632, 38 | 7939247003721636150710] 39 | ``` 40 | 41 | ### Swap Fee 42 | 43 | Swap fees are stored at the pool level. To get a pool's swap fee, call: 44 | 45 | ``` 46 | pool.getSwapFeePercentage() 47 | ``` 48 | 49 | Values are returned with 18 decimals. At the time of writing, calling this on `0x5c6ee304399dbdb9c8ef030ab642b10820db8f56` returns `500000000000000`, which corresponds to a 0.05% swap fee. 50 | 51 | #### Some Pools have Dynamic Swap Fees! 52 | 53 | If you intend to cache pool data to minimize on-chain calls, be aware that some pools have Dynamic Swap Fees and can change at any moment! The convention for setting static fees is to set the pool owner to the zero address (`0x0000000000000000000000000000000000000000`). 54 | 55 | You can query pool owner with: 56 | 57 | ``` 58 | pool.getOwner() 59 | ``` 60 | 61 | ### Emergency Pause State 62 | 63 | {% hint style="info" %} 64 | Pools are not expected to be paused, so this explanation is listed here out of an abundance of caution. If you're executing trades programmatically, you can avoid transaction failures by first verifying if a pool is paused or not. 65 | 66 | **NOTE:** The emergency pause is different from the `swapEnabled` feature on Liquidity Bootstrapping and Managed Pools! 67 | {% endhint %} 68 | 69 | When pool factories are first launched, they often have an emergency pause period. The pause period is generally **90 days** from the deployment of the **pool factory**, not the pool itself. In the unlikely case that there is an issue with the pools, trades and pool joins can be paused. **Withdrawals are not paused**, so **users can always exit a pool**. 70 | 71 | To check if a pool is paused, calling 72 | 73 | ``` 74 | pool.getPausedState() 75 | ``` 76 | 77 | returns something resembling 78 | 79 | ``` 80 | paused : False 81 | pauseWindowEndTime : 1627668973 82 | bufferPeriodEndTime : 1630260973 83 | ``` 84 | -------------------------------------------------------------------------------- /references/valuing-balancer-lp-tokens/events.md: -------------------------------------------------------------------------------- 1 | # Events 2 | 3 | ## AuthorizerSet 4 | 5 | When governance changes the Authorizer contract 6 | 7 | ``` 8 | event AuthorizerSet(IAuthorizer indexed newAuthorizer) 9 | ``` 10 | 11 | ## RelayerApprovalChange 12 | 13 | When a relayer has privileges approved or revoked 14 | 15 | ``` 16 | event RelayerApprovalChanged(address indexed relayer, 17 | address indexed sender, 18 | bool approved) 19 | ``` 20 | 21 | ## SwapFeePercentageChanged 22 | 23 | When associated with a Pool, records a change to the Pool's swap fee. When emitted from the ProtocolFeeCollector, records a change to the Protocol Swap Fee 24 | 25 | ``` 26 | event SwapFeePercentageChanged(uint256 swapFeePercentage) 27 | ``` 28 | 29 | ## FlashLoanFeePercentageChanged 30 | 31 | Records a change to the Protocol Flash Loan Fee 32 | 33 | ``` 34 | event FlashLoanFeePercentageChanged(uint256 newFlashLoanFeePercentage) 35 | ``` 36 | 37 | ## PoolCreated and PoolRegistered 38 | 39 | Emitted by BasePoolFactory when a new pool is deployed 40 | 41 | ``` 42 | event PoolCreated(address indexed pool) 43 | 44 | event PoolRegistered(bytes32 indexed poolId, 45 | address indexed poolAddress, 46 | PoolSpecialization specialization) 47 | ``` 48 | 49 | ## TokensRegistered 50 | 51 | Add a set of tokens to a Pool 52 | 53 | ``` 54 | event TokensRegistered(bytes32 indexed poolId, 55 | IERC20[] tokens, 56 | address[] assetManagers) 57 | ``` 58 | 59 | ## TokensDeregistered 60 | 61 | Remove a set of tokens from a Pool 62 | 63 | ``` 64 | event TokensDeregistered(bytes32 indexed poolId, IERC20[] tokens) 65 | ``` 66 | 67 | ## InternalBalanceChanged 68 | 69 | Record Internal (User) Balance transaction 70 | 71 | ``` 72 | event InternalBalanceChanged(address indexed user, 73 | IERC20 indexed token, 74 | int256 delta) 75 | ``` 76 | 77 | ## ExternalBalanceTransfer 78 | 79 | Record transfer between external accounts, using Vault allowance 80 | 81 | ``` 82 | event ExternalBalanceTransfer(IERC20 indexed token, 83 | address indexed sender, 84 | address recipient, 85 | uint256 amount) 86 | ``` 87 | 88 | ## PoolBalanceChanged 89 | 90 | Emitted when an LP joins or exits a Pool 91 | 92 | ``` 93 | event PoolBalanceChanged(bytes32 indexed poolId, 94 | address indexed liquidityProvider, 95 | IERC20[] tokens, 96 | int256[] deltas, 97 | uint256[] protocolFeeAmounts) 98 | ``` 99 | 100 | ## Swap 101 | 102 | Records an individual swap with a Pool (might be part of a batch swap) 103 | 104 | ``` 105 | event Swap(bytes32 indexed poolId, 106 | IERC20 indexed tokenIn, 107 | IERC20 indexed tokenOut, 108 | uint256 amountIn, 109 | uint256 amountOut) 110 | ``` 111 | 112 | ## PoolBalanceManaged 113 | 114 | Records an Asset Manager interaction with a Pool (Deposit, Withdrawal, or Update) 115 | 116 | ``` 117 | event PoolBalanceManaged(bytes32 indexed poolId, 118 | address indexed assetManager, 119 | IERC20 indexed token, 120 | int256 cashDelta, 121 | int256 managedDelta) 122 | ``` 123 | 124 | ## FlashLoan 125 | 126 | Records a FlashLoan 127 | 128 | ``` 129 | event FlashLoan(IFlashLoanRecipient indexed recipient, 130 | IERC20 indexed token, 131 | uint256 amount, 132 | uint256 feeAmount) 133 | ``` 134 | -------------------------------------------------------------------------------- /references/valuing-balancer-lp-tokens/pools/README.md: -------------------------------------------------------------------------------- 1 | # Pools 2 | 3 | ## Many Pool Operations Done Via The Vault! 4 | 5 | See the [Vault API](../../contracts/apis/the-vault.md) for the following functions. 6 | 7 | ``` 8 | registerPool(PoolSpecialization specialization) returns (bytes32 poolID) 9 | 10 | getPool(bytes32 poolId) returns (address pool, PoolSpecialization) 11 | 12 | getPoolTokens(bytes32 poolId) returns ( 13 | IERC20[] tokens, 14 | uint256[] balances, 15 | uint256 maxBlockNumber) 16 | 17 | getPoolTokenInfo(bytes32 poolId, IERC20 token) returns ( 18 | uint256 cash, 19 | uint256 managed, 20 | uint256 blockNumber, 21 | address assetManager) 22 | 23 | joinPool(bytes32 poolId, 24 | address sender, 25 | address recipient, 26 | JoinPoolRequest request) 27 | 28 | exitPool(bytes32 poolId, 29 | address sender, 30 | address payable recipient, 31 | ExitPoolRequest request) 32 | ``` 33 | 34 | ## Didn't find what you were looking for in the Vault API? 35 | 36 | There are some function implemented in [BasePool.sol](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/pool-utils/contracts/BasePool.sol) that most (if not all) pools inherit. 37 | 38 | ### `getVault` 39 | 40 | ``` 41 | getVault() returns (IVault vaultAddress) 42 | ``` 43 | 44 | Returns pool's Vault. 45 | 46 | ### `getPoolId` 47 | 48 | ``` 49 | getPoolId() returns (bytes32 poolID) 50 | ``` 51 | 52 | Returns pool's poolId. 53 | 54 | ### `getSwapFeePercentage` 55 | 56 | ``` 57 | getSwapFeePercentage() returns (uint256 swapFeePercentage) 58 | ``` 59 | 60 | Returns the pool's current swap fee. 61 | 62 | ### `setSwapFeePercentage` 63 | 64 | ``` 65 | setSwapFeePercentage(uint256 swapFeePercentage) 66 | ``` 67 | 68 | Updates the pool's swap fee. 69 | 70 | Note: This can **only** be called by an **authorized account**, denoted by the pool's owner. There are three cases for swap fee control: 71 | 72 | * Swap Fees are immutable 73 | * Owner: 0x0000000000000000000000000000000000000000 74 | * Swap Fees are controlled by a third party (currently [Gauntlet](https://medium.com/balancer-protocol/balancer-partners-with-gauntlet-to-make-dynamic-fee-pools-a-reality-97b3fb1760df)) 75 | * Owner: 0xBA1BA1ba1BA1bA1bA1Ba1BA1ba1BA1bA1ba1ba1B 76 | * Swap Fees are controlled by an account immutably set at pool creation 77 | * Owner: the account which was set at pool creation 78 | 79 | ### `setPaused` 80 | 81 | ``` 82 | function setPaused(bool paused) 83 | ``` 84 | 85 | Pauses trading within the pool. Users can exit their positions proportionally. 86 | 87 | Note: This can **only** be called by an **authorized account** and is intended to be used only as an emergency stop if something goes wrong. 88 | 89 | ### `on{Join,Exit}Pool` 90 | 91 | ``` 92 | onJoinPool( 93 | bytes32 poolId, 94 | address sender, 95 | address recipient, 96 | uint256[] currentBalances, 97 | uint256 latestBlockNumberUsed, 98 | uint256 protocolSwapFeePercentage, 99 | bytes userData 100 | ) returns (uint256[] amountsIn, uint256[] dueProtocolFeeAmounts) 101 | 102 | onExitPool( 103 | bytes32 poolId, 104 | address sender, 105 | address recipient, 106 | uint256[] currentBalances, 107 | uint256 latestBlockNumberUsed, 108 | uint256 protocolSwapFeePercentage, 109 | bytes userData 110 | ) returns (uint256[] amountsOut, uint256[] dueProtocolFeeAmounts) { 111 | ``` 112 | 113 | `onJoinPool` and `onExitPool` are called by the Vault when an account joins or exits a pool. 114 | 115 | ## Additional Interfaces 116 | 117 | Some pools have their own specific functions. These are defined in the respective pool-specific pages. 118 | -------------------------------------------------------------------------------- /resources/query-batchswap-join-exit.md: -------------------------------------------------------------------------------- 1 | # query{BatchSwap,Join,Exit} 2 | 3 | ## Overview 4 | 5 | In many scenarios, you might want to know how much X of TokenA you'll receive for Y of TokenB. Fortunately, Balancer has query functions to simulate transactions. 6 | 7 | {% hint style="warning" %} 8 | If you look on Etherscan (or similar), all three of the query functions will show up as "Write" functions. That is ok! You can still call these with `eth_call` to get numerical results without spending any gas. 9 | {% endhint %} 10 | 11 | ## !⚠️! WARNING !⚠️! 12 | 13 | ### Be very careful if you call `query*` from a contract!!! 14 | 15 | If you are using `queryBatchSwap`, `queryJoin`, or `queryExit` to calculate `limits`, `maxAmountsIn`, or `minAmountsOut`, this is ONLY useful if you simulate these calls OUTSIDE of the transaction you end up making. You SHOULD NOT call these functions to calculate limits from a smart contract at transaction time. 16 | 17 | There are valid use cases for calling these functions on chain, but **do not use them to determine limits**. 18 | 19 | ### Why? 20 | 21 | Calculating these values ahead of time is useful for enforcing slippage tolerances to mitigate losses due to sandwich attacks. **If you are querying these values at execution time, you end up getting your values mid-sandwich!** This leaves you entirely vulnerable to the attacker's manipulation, and is as foolish as using a 100% slippage tolerance. 22 | 23 | ## Queries 24 | 25 | {% hint style="info" %} 26 | ### Are you trying to calculate amounts for a pool that uses Phantom BPT? 27 | 28 | **\*LinearPools** and **StablePhantomPools** do not have join or exit functionality since those are handled as swaps! For example, if you want to figure out how much `bb-a-USD` you'll get for an amount of `DAI`, you'll need to use `queryBatchSwap` on a trade route that swaps `DAI` for `bb-a-DAI` and then swaps `bb-a-DAI` for `bb-a-USD`. 29 | {% endhint %} 30 | 31 | ### `queryBatchSwap` 32 | 33 | To calculate the inputs/outputs for a trade (you can specify given-in or given-out), you will use the `queryBatchSwap` function in the [`Vault`](../references/contracts/apis/the-vault.md#querybatchswap). This functionality is important if not crucial for calculating your limits when constructing your `batchSwap` arguments. 34 | 35 | ``` 36 | queryBatchSwap( 37 | SwapKind kind, 38 | BatchSwapStep[] swaps, 39 | IAsset[] assets, 40 | FundManagement funds) 41 | returns (int256[] assetDeltas) 42 | ``` 43 | 44 | ### `queryJoin` 45 | 46 | To calculate amounts of BPT out and tokens in, you will use `queryJoin` in [`BalancerHelpers`](../references/valuing-balancer-lp-tokens/balancerhelpers.md#queryjoin). This functionality is important for calculating `maxAmountsIn` and/or `minBptOut` on joins 47 | 48 | ``` 49 | queryJoin( 50 | bytes32 poolId, 51 | address sender, 52 | address recipient, 53 | JoinPoolRequest request) 54 | returns (uint256 bptOut, uint256[] amountsIn) 55 | ``` 56 | 57 | ### `queryExit` 58 | 59 | To calculate amounts of BPT in and tokens out, you will use `queryExit` in [`BalancerHelpers`](../references/valuing-balancer-lp-tokens/balancerhelpers.md#queryexit). This functionality is important for calculating `minAmountsOut` and/or `maxBptIn` on exits. 60 | 61 | ``` 62 | queryExit( 63 | bytes32 poolId, 64 | address sender, 65 | address recipient, 66 | ExitPoolRequest request) 67 | returns (uint256 bptIn, uint256[] amountsOut) 68 | ``` 69 | 70 | ### `sender` and `recipient` params 71 | 72 | The query function perform no checks on the `sender` and `recipient` field in the fund structs. This makes it suitable to be called by off-chain applications via `eth_call` without needing to hold tokens, approve them for the Vault, or even know a user's address. Similarly for `joins` and `exits`, these go directly to the pool and bypass the vault (which does the checks). 73 | -------------------------------------------------------------------------------- /guides/data/balancer-subgraph/deployment.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | A short overview of how to deploy the balancer-v2 subgraphs to the hosted 4 | service of TheGraph. 5 | --- 6 | 7 | # Deployment 8 | 9 | ## Getting started 10 | 11 | {% hint style="info" %} 12 | Please note that the hosted service of TheGraph is a free service. There could be potential changes of how Graphs can be deployed in the future. Check the development status and policies directly at [https://thegraph.com/hosted-service/](https://thegraph.com/hosted-service/) 13 | {% endhint %} 14 | 15 | ### Prerequisites 16 | 17 | You need the following tools / accounts set up in order to be able to deploy your own subgraphs: 18 | 19 | * a [GitHub ](https://github.com)account 20 | * a [Graph Hosted Service](https://thegraph.com/hosted-service/) account (can be linked with your GitHub account) 21 | * an IDE, such as Visual Studio Code or similar development environment to compile and deploy a subgraph 22 | 23 | ## Prepare the Graph Hosted Service Instance 24 | 25 | 26 | 27 | You first need to set up your [hosted service](https://thegraph.com/hosted-service/) account and instance, before you can host your graph: 28 | 29 | 1. Login to [https://thegraph.com/hosted-service/dashboard](https://thegraph.com/hosted-service/dashboard) (e.g. with your GitHub account) 30 | 2. Click "Add Subgraph" 31 | 3. Specify a unique name and further information such as your GitHub repo you want to deploy (e.g. your own fork of [balancer-subgraph-v2](https://github.com/balancer-labs/balancer-subgraph-v2)). Check out other subgraphs for examples 32 | 33 | The creation screen should look something like this: 34 | 35 | ![Example graph instantiation screen at https://thegraph.com/hosted-service/](../../../.gitbook/assets/createGraph.png) 36 | 37 | ## Configure your Local Instance 38 | 39 | ### Install local dependencies (and initialize your graph) 40 | 41 | ```shell 42 | #Run locally in your terminal or dev environment 43 | #npm: 44 | npm install -g @graphprotocol/graph-cli 45 | #Yarn: 46 | yarn global add @graphprotocol/graph-cli 47 | ``` 48 | 49 | #### Optional: Initialize a new subgraph folder from scratch 50 | 51 | {% hint style="info" %} 52 | We highly recommend to create your own .yaml files with all the needed specifications. You can also do this manually to create a subgraph manifest, as explained below) 53 | {% endhint %} 54 | 55 | ```shell 56 | #Initialization with above example 57 | graph init --product balancer-labs/balancer-v2-documentation 58 | ``` 59 | 60 | You are then asked to correctly configure / initialize your graph: 61 | 62 | 1. Choose protocol 63 | 2. Choose product (in our case hosted-service) 64 | 3. Provide a name (same as above, e.g. balancer-labs/balancer-v2-documentation) 65 | 4. Choose which chain you want to index (mainnet, matic, arbitrum, etc) 66 | 5. Define the contract address 67 | 68 | ### Deployment based on .yaml files 69 | 70 | {% hint style="info" %} 71 | If you want to deploy your own fork with your contracts, make sure to first deploy them and specify the starting blocks at which TheGraph should start to index. This is mission critical for a successful deployment. You can check out the subraph.\.yaml example files from the [Balancer Labs subgraph](https://github.com/balancer-labs/balancer-subgraph-v2) 72 | {% endhint %} 73 | 74 | Execute following commands in your subgraph folder after granting access to the hosted service: 75 | 76 | ``` 77 | #Codegen 78 | yarn codegen subgraph.yaml 79 | #Deploy 80 | graph deploy --product hosted-service balancer-labs/balancer-v2-documentation subgraph.yaml 81 | ``` 82 | 83 | To summarize, following steps had to be taken to deploy a subgraph instance to the hosted service 84 | 85 | 1. Set up an account at TheGraph Hosted Service platform 86 | 2. Initalize a subgraph 87 | 3. Create your own or clone the balancer-v2 subgraph 88 | 4. Modify .yaml files and subgraph logic to your desire 89 | 5. Deploy the instance based on a .yaml-file configuration 90 | -------------------------------------------------------------------------------- /references/valuing-balancer-lp-tokens/uml-diagrams.md: -------------------------------------------------------------------------------- 1 | # UML Diagrams 2 | 3 | 4 | 5 | | Name | Type | Simplified | 6 | | ----------------------------------------------------------------------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------- | 7 | | [AssetManagers](https://drive.google.com/file/d/1APxTuzAuGTAD0e\_AdHJKK24o98d3ghsB/view?usp=sharing) | Abstract Contract | [Diagram](https://drive.google.com/file/d/1QyDpuwAxTFPMHHV2SnSWyInmFxqXqYCS/view?usp=sharing) | 8 | | [AssetTransfersHandler](https://drive.google.com/file/d/1yCv8UgfqnQUX5SkISHoXCX8239OsIXCX/view?usp=sharing) | Abstract Contract | | 9 | | [Authorizer](https://drive.google.com/file/d/1webM\_w6tV30qFk0zxhiRBb7ujcetNhHd/view?usp=sharing) | Contract | [Diagram](https://drive.google.com/file/d/1SwAD3fvPxwcZNt-qIs4D0UBrzMLFh79H/view?usp=sharing) | 10 | | [Balances](https://drive.google.com/file/d/1OA2XI2JmUJzIxnmgD9MnOdEIvKGnrV0K/view?usp=sharing) | Directory | [Diagram](https://drive.google.com/file/d/1SwAD3fvPxwcZNt-qIs4D0UBrzMLFh79H/view?usp=sharing) | 11 | | [Fees](https://drive.google.com/file/d/170V8w0sPqyt\_oRsoPvD8-xt0BQlrnDwK/view?usp=sharing) | Abstract Contract | [Diagram](https://drive.google.com/file/d/1PpB6VCVnQS7aE3QeIgVxThUSIvOug3RP/view?usp=sharing) | 12 | | [FlashLoans](https://drive.google.com/file/d/1NsddcFLPyvcdwobDtPXw6frr--ZvN9JU/view?usp=sharing) | Abstract Contract | | 13 | | [Interfaces](https://drive.google.com/file/d/19NJL6RE6PAld1s9z7L1QBEyJfx6DezRA/view?usp=sharing) | Directory | [Diagram](https://drive.google.com/file/d/103B8b3sFERF1n6QTHOLkhUJ2WtGR5k-D/view?usp=sharing) | 14 | | [PoolBalances](https://drive.google.com/file/d/1krHNvCa4d\_WqftwLC-wcJZWgSQNMi24G/view?usp=sharing) | Abstract Contract | [Diagram](https://drive.google.com/file/d/1AbWvvy9hS9\_Pw4w6x0QT5G1tLHXIY5ky/view?usp=sharing) | 15 | | [PoolPriceOracle](https://drive.google.com/file/d/1kPyQN-V-4CqqIx-IVLpLc-agYqYMtYiz/view?usp=sharing) | Directory | | 16 | | [PoolRegistry](https://drive.google.com/file/d/1U7hc\_siZ6\_eKU\_CJPxVVXXoMB0eOF8vB/view?usp=sharing) | Abstract Contract | [Diagram](https://drive.google.com/file/d/1kLJSPNGqorc6jj8q2KXyV5QsWg8zegy0/view?usp=sharing) | 17 | | [PoolTokens](https://drive.google.com/file/d/1HFTWdXLez\_fbHs5zOOzB9ph9oukDyAnG/view?usp=sharing) | Abstract Contract | [Diagram](https://drive.google.com/file/d/1CNowTDzjUYOt1vW7Ueu-8dyb97GTszZl/view?usp=sharing) | 18 | | [Pools](https://drive.google.com/file/d/1vpu93s3lgu\_h4Mm4OSSp2\_0axTun828W/view?usp=sharing) | Directory | [Diagram](https://drive.google.com/file/d/1PNiYEZuIDfFvUPChB9MSnzwIkZ5aeEJS/view?usp=sharing) | 19 | | [ProtocolFeesCollector](https://drive.google.com/file/d/1262iB1iYoO6S9XCMy2pbWw8GYwF3HoN-/view?usp=sharing) | Abstract Contract | | 20 | | [Swaps](https://drive.google.com/file/d/1OuW0FE4hEZTbRzvOiQlPX03vwKhNBNfW/view?usp=sharing) | Abstract Contract | [Diagram](https://drive.google.com/file/d/19pMq1LU05aST-CLuweaZVD2oG4-oQp1C/view?usp=sharing) | 21 | | [UserBalance](https://drive.google.com/file/d/1Yc1LXDAvVm-HfRUYXxdu1sxyU3vM87ua/view?usp=sharing) | Abstract Contract | [Diagram](https://drive.google.com/file/d/1itUdj\_E3\_8QxxPd4uDjq0AtQAPnMiMKh/view?usp=sharing) | 22 | | [Vault](https://drive.google.com/file/d/1P4syKeEN0OO00tU-2CUG7mrp9p9Mr\_r3/view?usp=sharing) | Contract | [Diagram](https://drive.google.com/file/d/1G5Tgg5mRyrm8PDZCQcj3Mlneb8N6mUyM/view?usp=sharing) | 23 | | [VaultAuthorization](https://drive.google.com/file/d/1EvmdgH6oLjvbpOA-N8l-OqoPNaD4\_6\_w/view?usp=sharing) | Abstract Contract | | 24 | -------------------------------------------------------------------------------- /resources/deploy-pools-from-factory/creation/aavelinearpool.md: -------------------------------------------------------------------------------- 1 | # AaveLinearPool 2 | 3 | ## Overview 4 | 5 | AaveLinearPools facilitate trades between token/waToken. They can only be deployed for tokens that have Aave aTokens and require you to deploy an Aave aToken Wrapper if one does not already exist. For this page, we will assume we have a new USD stablecoin called `USDX` that already has an Aave aToken, `aUSDX`. 6 | 7 | ### Requirements 8 | 9 | * Some quantity of `USDX` 10 | * `aUSDX` must be an existing aToken available on Aave. Having some quantity of this token is **optional** for pool creation. 11 | * An Aave aToken Wrapper for compatibility with Balancer 12 | * Deploy AaveLinearPool 13 | 14 | {% hint style="info" %} 15 | Want to arbitrage an AaveLinearPool in order to keep it within the desired balance range? [Here's an example contract](https://etherscan.io/address/0x1ed9c8bd3dccb85f704a5287444b552f9d5e1a26#code) that takes a Flashloan from Uniswap and rebalances the Token/aToken between Aave and the AaveLinearPool. 16 | {% endhint %} 17 | 18 | ## Aave aToken Wrapper 19 | 20 | ### Why? 21 | 22 | Aave aTokens have streaming balances, which are incompatible with Balancer. For this reason they must be wrapped before they're deposited into a pool. 23 | 24 | ### How? 25 | 26 | * Clone [Aave's protocol-v2 repo](https://github.com/aave/protocol-v2/tree/feat/233-staticATokenLM-support-old-atoken) and switch to the **feat/233-staticATokenLM-support-old-atoken** branch (latest revision is commit `1d80f9a`) 27 | * Create a `.env` file with your secrets 28 | 29 | ``` 30 | cat > .env << EOF 31 | MNEMONIC= 32 | ETHERSCAN_KEY= 33 | ALCHEMY_KEY= 34 | EOF 35 | ``` 36 | 37 | * Start docker compose, it will install all dependencies 38 | 39 | ``` 40 | docker-compose up 41 | ``` 42 | 43 | * Get into the container in another terminal/session 44 | 45 | ``` 46 | docker-compose exec contracts-env bash 47 | ``` 48 | 49 | * Compile contracts 50 | 51 | ``` 52 | npm run compile 53 | ``` 54 | 55 | * Run the Static AToken task 56 | 57 | ``` 58 | npx hardhat --network deploy-atoken-wrapper \\ 59 | --pool \\ 60 | --a-token-address \\ 61 | --proxy-admin \\ 62 | --verify # --verify flag is to verify the contracts automatically if you have ETHERSCAN_KEY env variable correctly set. 63 | ``` 64 | 65 | #### Sample Command for `waDAI` 66 | 67 | ``` 68 | npx hardhat --network main deploy-atoken-wrapper \\ 69 | --pool 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9 \\ 70 | --a-token-address 0x028171bCA77440897B824Ca71D1c56caC55b68A3 \\ 71 | --proxy-admin 0xEE56e2B3D491590B5b31738cC34d5232F378a8D5 \\ 72 | --verify 73 | ``` 74 | 75 | ### Deploy an AaveLinearPool 76 | 77 | The simplest way to deploy an AaveLinearPool is with [balpy](https://github.com/balancer-labs/balpy). 78 | 79 | * Clone balpy repo: 80 | 81 | ``` 82 | git clone https://github.com/balancer-labs/balpy.git 83 | ``` 84 | 85 | * Go to the poolCreation samples 86 | 87 | ``` 88 | cd balpy/samples/poolCreation 89 | ``` 90 | 91 | * Follow the directions in README.md 92 | 93 | ``` 94 | # Make a virtual environment 95 | python3 -m venv ./venv 96 | source ./venv/bin/activate 97 | 98 | # Install balpy 99 | python3 -m pip install balpy 100 | 101 | # Source your environment (balpy will warn you if you forget this step) 102 | 103 | # Copy a pool config for the poolType you want to create 104 | cp sampleAaveLinearPool.json mySuperAwesomePool.json 105 | 106 | # Edit your new pool file in your favorite text editor 107 | 108 | # Run the Python script 109 | python3 poolCreationSample.py mySuperAwesomePool.json 110 | ``` 111 | 112 | ## Common Arguments 113 | 114 | In addition to the arguments listed below, you should also consider the [common arguments](./#common-arguments) when creating an AaveLinearPool. 115 | 116 | ## Pool Creation Arguments 117 | 118 | ### `upperTarget` 119 | 120 | The `upperTarget` argument defines the upper limit of the "no fee zone." The "no fee zone" is the a range for the desired amount of `baseToken` in the pool. In general, LinearPools strive to keep enough `baseToken` liquidity to facilitate trades while attempting to maximize the `wrappedToken` balance to take advantage of the benefits of wrapped tokens (ie wrapped aTokens). 121 | -------------------------------------------------------------------------------- /deep-dive/inside-balancer-contracts/recovery-mode.md: -------------------------------------------------------------------------------- 1 | # Recovery Mode 2 | 3 | ## Credit 4 | 5 | Credit for the below article goes to [0xSkly](https://twitter.com/0xSkly) of the BeethovenX Team. You can find the original article on [Medium](https://medium.com/@0xSkly/inside-balancer-code-recoverymode-9af34ce5ab72). 6 | 7 | Disclaimer: The article has been lightly edited for formatting and uniformity with the rest of the docs, but has been kept in a first-person format. Any opinions expressed below are those of the author and should not be considered opinions of Balancer Labs, BeethovenX, BalancerDAO, or any other organization. 8 | 9 | ## `RecoveryMode` 10 | 11 | Balancer Labs introduces a new mechanism to secure user funds in emergency situations. This is a technical insight into the `RecoveryMode` class which enables this functionality when inherited. 12 | 13 | > ``` 14 | > This is intended to provide a safe way to exit any pool during some 15 | > kind of emergency, to avoid locking funds in the event the pool 16 | > enters a non-functional state (i.e., some code that normally runs 17 | > during exits is causing them to revert). 18 | > ``` 19 | 20 | ### What's the Effect When a Pool is in RecoveryMode ? 21 | 22 | A special clean exit function is enabled which runs the absolute minimum code necessary to exit proportionally. 23 | 24 | In particular: 25 | 26 | * Stable pools do not try to calculate the invariant 27 | * No protocol fees are collected 28 | 29 | It’s important to note that the pool is not disabled, which means regular swaps, joins, and exits are still possible. 30 | 31 | ### Who is Authorized to Put a Pool Into RecoveryMode ? 32 | 33 | The external function `enableRecoveryMode` is protected by the `Authorizer` configured on the Vault. Therefore any address granted permission on the `Authorizer` can enable recovery mode. 34 | 35 | This permission needs to be treated with care since the RecoveryMode functionality could be misused. 36 | 37 | ### What Pools Have a Recovery Exit Available ? 38 | 39 | The `BasePool` class which is a base layer for all current pools inherits from the `RecoveryMode` class enabling the recovery exit functionality for all new pools deployed (which are based on the `BasePool` class). 40 | 41 | {% hint style="info" %} 42 | Note: Many pools of many poolTypes were created before this was added to BasePool, so there is a decent chance the pool you're in does not have this functionality. As more pools are created using newer factories, this scenario should change. 43 | {% endhint %} 44 | 45 | ### Inside the `RecoveryMode` Exit 46 | 47 | The default implementation is within the `RecoveryMode` class and needs to be overwritten for special pools like a `PhantomStablePool` 48 | 49 | ![](https://miro.medium.com/max/1400/1\*U3pP4UaMPeibjMX9n4WD4Q.png) 50 | 51 | I’ve added some comments to the function parameters for some context. So we extract the provided BPT from the `userData` and calculate the share of the total supply by `bptAmountIn / totalSupply` . Based on this ratio we can then calculate the share of each token in the pool. See `_computeProportionalAmountsOut` 52 | 53 | ![](https://miro.medium.com/max/1400/1\*0NXuNlvuVip99N4kNBzFxw.png) 54 | 55 | ### But What About StablePhantomPools ? 56 | 57 | `StablePhantomPools` use pre-minted BPT, so therefore we cannot use the `totalSupply` to calculate the share of a user. That’s why there is a custom implementation. 58 | 59 | ![](https://miro.medium.com/max/1400/1\*GrsRRz5PPVuHodDWxVmR6g.png) 60 | 61 | We see that before calling the default implementation with `super._doRecoveryModeExit(...)`, we subtract the pre-minted BPT. This results in a ‘virtual supply’ which is the actual BPT in circulation. 62 | 63 | But how do we know the actual BPT in circulation? Remember the `balances` parameter which includes the total balance of all tokens in the pool. This also includes the pre minted BPT. So with this, we can do `totalSupply — balances[indexOfBpt]` which results in all BPT in circulation. Additionally we also need to remove the BPT token from the `balances` array. This is what happens in `_dropBptItemFromBalances(balances)` 64 | 65 | ### How is it All Wired Up ? 66 | 67 | The entry point to a pool exit is the `Vault` class which delegates the call to the respective pool contract. The base layer of each pool contract is the `BasePool` which also implements the `onExitPool` hook: 68 | 69 | ![](https://miro.medium.com/max/1400/1\*tmG7z8bmIeAwG0GkF9D72A.png) 70 | 71 | ## Conclusion 72 | 73 | With this mechanism in place, we gain an additional safety layer for users to exit with their funds in case of some unexpected pool behavior. 74 | -------------------------------------------------------------------------------- /resources/joins-and-exits/pool-exits.md: -------------------------------------------------------------------------------- 1 | # Pool Exits 2 | 3 | {% hint style="info" %} 4 | Calls to `exitPool()` are made on the Vault contract! You cannot send this command directly to a pool. 5 | {% endhint %} 6 | 7 | ## API 8 | 9 | ```cpp 10 | exitPool( 11 | bytes32 poolId, 12 | address sender, 13 | address recipient, 14 | ExitPoolRequest request 15 | ) 16 | 17 | ExitPoolRequest( 18 | address[] assets, 19 | uint256[] minAmountsOut, 20 | bytes userData, 21 | bool toInternalBalance 22 | ) 23 | ``` 24 | 25 | ### Arguments Explained 26 | 27 | * `poolId` - ID of the pool you're interacting with 28 | * `sender` - Address sending BPT 29 | * `recipient` - Address receiving tokens (usually the same as sender) 30 | * `request` - ExitPoolRequest tuple made up of the following: 31 | * `assets` - List of your tokens, ordered (see below) 32 | * `minAmountsOut` - Minimum token receive amounts (see below) 33 | * `userData` - Custom bytes field (see below) 34 | * `toInternalBalance` - `True` if you receiving tokens as internal token balances. `False` if receiving as ERC20. 35 | 36 | ### Token Ordering 37 | 38 | When providing your assets, you must ensure that the tokens are sorted numerically by token address. It's also important to note that the values in minAmountsOut correspond to the same index value in assets, so these arrays must be made in parallel _after_ sorting. 39 | 40 | ### `minAmountsOut` 41 | 42 | In the exitPool call, you have to provide `minAmountsOut`, the lower limits for the tokens to receive. In short, what are the minimum amounts you would find acceptable, given the amount of BPT you are providing? 43 | 44 | A good practice would be to user [`queryExit` in `BalancerHelpers`](../query-batchswap-join-exit.md#queryexit) to find the current amounts of tokens you would get for your BPT, and then account for some possible slippage. 45 | 46 | Let's say that you want to allow a 1% slippage. After computing how many tokens you expect for a given amount of BPT, you'd apply a factor of 0.99 to all the amounts. These thresholds are important because it's possible for token amounts to change in the pool between the time you send your transaction and the when your transaction executes. 47 | 48 | ### `userData` 49 | 50 | userData is a highly versatile field; as such, it needs to be encoded for its specific use case. For exits, userData encodes a `ExitKind` to tell the pool what style of exit you're performing. Since pool types are customizable, not every pool necessarily uses the same `ExitKind`, so it's important to keep track of what each pool type can handle. 51 | 52 | #### [WeightedPool](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/pool-weighted/contracts/BaseWeightedPool.sol#L40) ExitKinds 53 | 54 | ```cpp 55 | enum ExitKind { 56 | EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, 57 | EXACT_BPT_IN_FOR_TOKENS_OUT, 58 | BPT_IN_FOR_EXACT_TOKENS_OUT, 59 | MANAGEMENT_FEE_TOKENS_OUT // for InvestmentPool 60 | } 61 | ``` 62 | 63 | The **first three** types of`ExitKind` apply to the following pools: 64 | 65 | * WeightedPool 66 | * WeightedPool2Tokens 67 | * LiquidityBootstrappingPool 68 | * InvestmentPool 69 | 70 | As noted in the comment, only the InvestmentPool has a fourth:`MANAGEMENT_FEE_TOKENS_OUT`. This is used as an `internal` function only, so it is out of the scope of this page. 71 | 72 | 73 | 74 | **Composable Stable V2 ExitKinds** 75 | 76 | ```javascript 77 | enum ExitKind { 78 | EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, 79 | BPT_IN_FOR_EXACT_TOKENS_OUT, 80 | EXACT_BPT_IN_FOR_ALL_TOKENS_OUT 81 | } 82 | ``` 83 | 84 | Applies to: 85 | 86 | * Composable StablePool 87 | 88 | #### Exit Types Explained 89 | 90 | * **Single Asset Exit** (`EXACT_BPT_IN_FOR_ONE_TOKEN_OUT`) 91 | * User sends a precise quantity of BPT, and receives an estimated but unknown (computed at run time) quantity of a single token. 92 | * **Proportional Exit** (`EXACT_BPT_IN_FOR_TOKENS_OUT`) 93 | * User sends a precise quantity of BPT, and receives an estimated but unknown (computed at run time) quantities of all tokens. 94 | * **Custom Exit** (`BPT_IN_FOR_EXACT_TOKENS_OUT`) 95 | * User sends an estimated but unknown (computed at run time) quantity of BPT, and receives precise quantities of specified tokens. 96 | 97 | #### Encoding ([How do I encode?](../../helpers/encoding.md)) 98 | 99 | * Single Asset Exit 100 | * userData ABI 101 | * `['uint256', 'uint256', 'uint256']` 102 | * userData 103 | * `[EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, bptAmountIn, exitTokenIndex]` 104 | * Proportional Exit 105 | * userData ABI 106 | * `['uint256', 'uint256']` 107 | * userData 108 | * `[EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn]` 109 | * Custom Exit 110 | * userData ABI 111 | * `['uint256', 'uint256[]', 'uint256']` 112 | * userData 113 | * `[BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]` 114 | -------------------------------------------------------------------------------- /resources/joins-and-exits/pool-joins.md: -------------------------------------------------------------------------------- 1 | # Pool Joins 2 | 3 | {% hint style="info" %} 4 | Calls to `joinPool()` are made on the Vault contract! You cannot send this command directly to a pool. 5 | {% endhint %} 6 | 7 | ## API 8 | 9 | ```cpp 10 | joinPool( 11 | bytes32 poolId, 12 | address sender, 13 | address recipient, 14 | JoinPoolRequest request 15 | ) 16 | 17 | JoinPoolRequest( 18 | address[] assets, 19 | uint256[] maxAmountsIn, 20 | bytes userData, 21 | bool fromInternalBalance 22 | ) 23 | ``` 24 | 25 | ### Arguments Explained 26 | 27 | * `poolId` - ID of the pool you're interacting with 28 | * `sender` - Address sending tokens to the pool 29 | * `recipient` - Address receiving BPT (usually the same as sender) 30 | * `request` - JoinPoolRequest tuple made up of the following: 31 | * `assets` - Sorted list of all tokens in pool (see below) 32 | * `maxAmountsIn` - Maximum token send amounts (see below) 33 | * `userData` - Custom bytes field (see below) 34 | * `fromInternalBalance` - `True` if sending from internal token balances. `False` if sending ERC20. 35 | 36 | ### Token Ordering 37 | 38 | When providing your assets, you must ensure that the tokens are sorted numerically by token address. It's also important to note that the values in maxAmountsIn correspond to the same index value in assets, so these arrays must be made in parallel _after_ sorting. 39 | 40 | ### `maxAmountsIn` 41 | 42 | In the joinPool call, you have to provide `maxAmountsIn`, the upper limits for the tokens to send. In short, what are the maximum amounts you would find acceptable to send, given the amount of BPT you are receiving? 43 | 44 | A good practice is to use [`queryJoin` in `BalancerHelpers`](../query-batchswap-join-exit.md#queryjoin) to find the current amount of BPT you would get for your tokens, and then account for some possible slippage. 45 | 46 | Let's say that you want to allow a 1% slippage. After computing how many tokens you expect to provide for a given amount of BPT, you'd apply a factor of 1.01 to all the amounts. These thresholds are important because it's possible for token amounts to change in the pool between the time you send your transaction and when your transaction executes. 47 | 48 | ### `userData` 49 | 50 | userData is a highly versatile field; as such, it needs to be encoded for its specific use case. For joins, userData encodes a `JoinKind` to tell the pool what style of join you're performing. Not every pool uses every `JoinKind`, so it's important to keep track of what each pool type can handle. 51 | 52 | #### [WeightedPool](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/pool-weighted/contracts/BaseWeightedPool.sol#L39) JoinKinds 53 | 54 | ```cpp 55 | enum JoinKind { 56 | INIT, 57 | EXACT_TOKENS_IN_FOR_BPT_OUT, 58 | TOKEN_IN_FOR_EXACT_BPT_OUT, 59 | ALL_TOKENS_IN_FOR_EXACT_BPT_OUT 60 | } 61 | ``` 62 | 63 | Applies to: 64 | 65 | * WeightedPool 66 | * WeightedPool2Tokens 67 | * LiquidityBootstrappingPool 68 | * InvestmentPool 69 | 70 | #### [StablePool](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/pool-stable/contracts/StablePool.sol#L78) JoinKinds 71 | 72 | ```cpp 73 | enum JoinKind { 74 | INIT, 75 | EXACT_TOKENS_IN_FOR_BPT_OUT, 76 | TOKEN_IN_FOR_EXACT_BPT_OUT 77 | } 78 | ``` 79 | 80 | Applies to: 81 | 82 | * StablePool 83 | * MetaStablePool 84 | 85 | #### JoinKinds Explained 86 | 87 | * **Initial Join** (`INIT`) 88 | * User sends the precise initial tokens to seed a pool. This can be done only once. 89 | * **Exact Tokens Join** (`EXACT_TOKENS_IN_FOR_BPT_OUT`) 90 | * User sends precise quantities of tokens, and receives an estimated but unknown (computed at run time) quantity of BPT. 91 | * **Single Token Join** (`TOKEN_IN_FOR_EXACT_BPT_OUT`) 92 | * User sends an estimated but unknown (computed at run time) quantity of a single token, and receives a precise quantity of BPT. 93 | * **Proportional Join** (`ALL_TOKENS_IN_FOR_EXACT_BPT_OUT`) 94 | * User sends estimated but unknown (computed at run time) quantities of tokens, and receives precise quantity of BPT. 95 | 96 | #### Encoding ([How do I encode?](../../helpers/encoding.md)) 97 | 98 | * **Initial Join** 99 | * userData ABI 100 | * `['uint256', 'uint256[]']` 101 | * userData 102 | * `[INIT, amountsIn]` 103 | * **Exact Tokens Join** 104 | * userData ABI 105 | * `['uint256', 'uint256[]', 'uint256']` 106 | * userData 107 | * `[EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]` 108 | * **Single Token Join** 109 | * userData ABI 110 | * `['uint256', 'uint256', 'uint256']` 111 | * userData 112 | * `[TOKEN_IN_FOR_EXACT_BPT_OUT, bptAmountOut, enterTokenIndex]` 113 | * **Proportional Join** 114 | * userData ABI 115 | * `['uint256', 'uint256']` 116 | * userData 117 | * `[ALL_TOKENS_IN_FOR_EXACT_BPT_OUT, bptAmountOut]` 118 | -------------------------------------------------------------------------------- /references/valuing-balancer-lp-tokens/pools/metastablepools.md: -------------------------------------------------------------------------------- 1 | # MetaStablePools 2 | 3 | ## More functions are inherited 4 | 5 | This page breaks out the functions that are not common to all pools. See more functions on the [Pools page](./). 6 | 7 | ## API 8 | 9 | ### `onSwap` 10 | 11 | ```cpp 12 | // Inherited from BaseGeneralPool 13 | onSwap(SwapRequest swapRequest, 14 | uint256[] balances, 15 | uint256 indexIn, 16 | uint256 indexOut) 17 | returns (uint256 amount[In/Out]) 18 | ``` 19 | 20 | When the Vault is handling a swap, it will call `onSwap` to ask the pool what the amounts should be. Pools that use stable math need the all tokens balances to determine price. 21 | 22 | ### `getAmplificationParameter` 23 | 24 | ``` 25 | function getAmplificationParameter() 26 | returns (uint256 value, bool isUpdating, uint256 precision) 27 | ``` 28 | 29 | Returns the amplification parameter value, a boolean to determine if it's updating, and its precision. 30 | 31 | ### `enableOracle` 32 | 33 | ```cpp 34 | enableOracle() 35 | ``` 36 | 37 | Enables the oracle functionality. 38 | 39 | ### `getMiscData` 40 | 41 | ```cpp 42 | getMiscData() 43 | returns ( 44 | int256 logInvariant, 45 | int256 logTotalSupply, 46 | uint256 oracleSampleCreationTimestamp, 47 | uint256 oracleIndex, 48 | bool oracleEnabled, 49 | uint256 swapFeePercentage) 50 | ``` 51 | 52 | Returns a variety of data fields, listed above. 53 | 54 | ### `getLargestSafeQueryWindow` 55 | 56 | ```cpp 57 | getLargestSafeQueryWindow() 58 | returns (uint256) 59 | ``` 60 | 61 | Returns largest safe query window. 62 | 63 | ### `getLatest` 64 | 65 | ```cpp 66 | enum Variable { PAIR_PRICE, BPT_PRICE, INVARIANT } 67 | getLatest(Variable variable) returns (uint256) 68 | ``` 69 | 70 | Returns latest pair price, BPT price, or invariant depending on what `variable` enum you pass. 71 | 72 | ### `getTimeWeightedAverage` 73 | 74 | ```cpp 75 | enum Variable { PAIR_PRICE, BPT_PRICE, INVARIANT } 76 | struct OracleAverageQuery { 77 | Variable variable; 78 | uint256 secs; 79 | uint256 ago; 80 | } 81 | getTimeWeightedAverage(OracleAverageQuery[] queries) 82 | returns (uint256[] results) 83 | ``` 84 | 85 | Returns time weighted average prices corresponding to the variables in each query. `secs` is the duration of the query in seconds, and `ago` is the time in seconds from since **end** of that duration. Prices are represented as 18 decimal fixed point values. 86 | 87 | {% hint style="warning" %} 88 | Note that you can only call `getTimeWeightedAverage` after the buffer is full, or it will revert with `ORACLE_NOT_INITIALIZED`. If you call `getSample(1023)` and it returns 0's, that means the buffer's not full yet. 89 | {% endhint %} 90 | 91 | ### `getPastAccumulators` 92 | 93 | ```cpp 94 | enum Variable { PAIR_PRICE, BPT_PRICE, INVARIANT } 95 | struct OracleAccumulatorQuery { 96 | Variable variable; 97 | uint256 ago; 98 | } 99 | getPastAccumulators(OracleAccumulatorQuery[] queries 100 | returns (int256[] results) 101 | ``` 102 | 103 | Returns estimates for the accumulator at a time `ago` seconds ago for each query. 104 | 105 | ### `getRateProviders` 106 | 107 | ``` 108 | function getRateProviders() 109 | returns (IRateProvider[] providers) 110 | ``` 111 | 112 | Returns rate providers that provide exchange rates between the tokens 113 | 114 | ### `getPriceRateCache` 115 | 116 | ``` 117 | function getPriceRateCache(IERC20 token) 118 | returns (uint256 rate, uint256 duration, uint256 expires) 119 | ``` 120 | 121 | Returned the cached rate, the cache duration, and the time of expiry for the current rate. 122 | 123 | ### `updatePriceRateCache` 124 | 125 | ``` 126 | function updatePriceRateCache(IERC20 token) 127 | ``` 128 | 129 | Updates the cached price rate for the given token. 130 | 131 | ## Permissioned Functions 132 | 133 | All of the following functions are only callable by the pool owner. 134 | 135 | ### `startAmplificationParameterUpdate` 136 | 137 | ``` 138 | function startAmplificationParameterUpdate( 139 | uint256 rawEndValue, 140 | uint256 endTime) 141 | ``` 142 | 143 | Begins changing the amplification parameter to `rawEndValue` over time. The value will change linearly until `endTime` is reached, when it will be `rawEndValue`. 144 | 145 | **NOTE**: Internally, the amplification parameter is represented using higher precision. The values returned by `getAmplificationParameter` have to be corrected to account for this when comparing to `rawEndValue`. 146 | 147 | ### `stopAmplificationParameterUpdate` 148 | 149 | ``` 150 | function stopAmplificationParameterUpdate() external 151 | ``` 152 | 153 | Stops the amplification parameter change process, keeping the current value. 154 | 155 | ### `setPriceRateCacheDuration` 156 | 157 | ``` 158 | function setPriceRateCacheDuration( 159 | IERC20 token, 160 | uint256 duration) 161 | ``` 162 | 163 | Sets the given `token`'s price rate cache duration to `duration`. 164 | -------------------------------------------------------------------------------- /resources/pool-interfacing/metastable-pool.md: -------------------------------------------------------------------------------- 1 | # MetaStable Pool 2 | 3 | ## Overview 4 | 5 | For advantages and use cases of MetaStable Pools, please refer to [the standard documentation](https://docs.balancer.fi/products/balancer-pools/metastable-pools). 6 | 7 | For more interfaces, such as updating the `amplificationParameter` or querying price data from the pool oracle interface, see the [MetaStablePool API](../../references/valuing-balancer-lp-tokens/pools/metastablepools.md). 8 | 9 | ## Interfacing 10 | 11 | Some elements to consider when interfacing with Stable Pools: 12 | 13 | * Using the **MetaStable extension** of [Stable Math](../pool-math/stable-math.md) (be sure to see `MetaStable.ts`!) 14 | * Pools have 2 tokens 15 | * Pools rely on the `amplificationParameter`, which is defined at pool creation and can be gradually updated later. 16 | * Pools also rely on `RateProviders` for each token in the pool to determine the exchange rate. If a token's `RateProvider` is set to the zero address, the rate is set to `1`. 17 | 18 | ## Getting Pool Data 19 | 20 | In addition to the [common pool data](./#getting-common-pool-data), you will likely want the following data when interfacing with MetaStable Pools: 21 | 22 | ### Amplification Parameter 23 | 24 | The Amplification Parameter is stored at the pool level. For example, calling 25 | 26 | ``` 27 | pool.getAmplificationParameter() 28 | ``` 29 | 30 | returns something resembling 31 | 32 | ``` 33 | value : 620000 34 | isUpdating : False 35 | precision : 1000 36 | ``` 37 | 38 | where the amplification parameter is $$\frac{value}{precision} = \frac{620000}{1000}=620$$ in this case. 39 | 40 | ### Rate Providers 41 | 42 | The `RateProviders` are contract addresses stored at the pool level. The pool calls these providers to get the current exchange rates between the tokens in the pool. Calling 43 | 44 | ``` 45 | pool.getRateProviders() 46 | ``` 47 | 48 | returns something resembling 49 | 50 | ``` 51 | [0x72d07d7dca67b8a406ad1ec34ce969c90bfee768] 52 | ``` 53 | 54 | where the contract at [0x72d07d7dca67b8a406ad1ec34ce969c90bfee768](https://etherscan.io/address/0x72d07d7dca67b8a406ad1ec34ce969c90bfee768) is a `RateProvider` that has a function called `getRate()`. In this example, this MetaStable Pool has only one `RateProvider`. This implies that the rate is the ratio of Token1:Token2. 55 | 56 | {% hint style="info" %} 57 | In other use cases, there may be multiple `RateProviders` that provide a price from each token to a common base. For example, the rate providers could be 58 | 59 | `Token1RateProvider` returns Token1:USD 60 | 61 | `Token2RateProvider` returns Token2:USD 62 | 63 | And to find the price of Token1:Token2, you would divide Token1:USD/Token2:USD to cancel out the intermediate USD. 64 | {% endhint %} 65 | 66 | ### Price Rate Cache 67 | 68 | MetaStable Pools cache prices from `RateProviders` up to a maximum duration. These cached prices are updated either manually using `updatePriceRateCache()` or automatically during a trade when the cache has expired. 69 | 70 | #### `getScalingFactors` 71 | 72 | To access the simplest form of Price Rate data, you can call 73 | 74 | ``` 75 | pool.getScalingFactors() 76 | ``` 77 | 78 | which will return something resembling 79 | 80 | ``` 81 | [1047560735332976763, 1000000000000000000] 82 | ``` 83 | 84 | where the values are the cached prices scaled to have appropriate decimals. For tokens that do not have a RateProvider (set to `0x0000000000000000000000000000000000000000`), they will return a `scalingFactor` of `1000000000000000000` = $$1e18$$. 85 | 86 | Values are ordered according to numerically sorted token addresses. In this example, these values correspond to `[0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0, 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2]`. 87 | 88 | #### `getPriceRateCache` 89 | 90 | To see exactly what price rate data a MetaStable pool is currently using, call 91 | 92 | ``` 93 | pool.getPriceRateCache(tokenAddress) 94 | ``` 95 | 96 | and the results will resemble one of the following: 97 | 98 | #### For tokens that DO have a Price Rate 99 | 100 | ``` 101 | rate uint256 : 1047560735332976763 102 | duration uint256 : 10800 103 | expires uint256 : 1636558429 104 | ``` 105 | 106 | #### For tokens that DO NOT have a Price Rate 107 | 108 | ``` 109 | rate uint256 : 0 110 | duration uint256 : 0 111 | expires uint256 : 0 112 | ``` 113 | 114 | In these samples, you can see that the token **with a Price Rate** has a `duration` of 10800 seconds (3 hours) and an expiration timestamp. The `rate` is cached exactly as it comes from the `RateProvider`. It's possible that a token price may need to be decimal adjusted for the price rate to make sense. 115 | 116 | For the token **without a Price Rate**, the `rate` is not actually interpreted as 0, but rather as `1000000000000000000` = $$1e18$$. 117 | 118 | ### Oracle Data 119 | 120 | To query oracle data from a MetaStable Pool, refer to the [Oracle Pools ](oracle-pools.md#overview)interfacing page. 121 | -------------------------------------------------------------------------------- /guides/data/inferring-historical-liquidity-mining-aprs-legacy.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | This section describes the legacy liquidity mining incentives program and how 4 | APRs could be calculated 5 | --- 6 | 7 | # Inferring Historical Liquidity Mining APRs (Legacy) 8 | 9 | The initial system of the liquidity mining incentives program was community driven and maintained by a Liquidity Mining Committee composed of “Ballers”. You can read about the legacy system [here](https://docs.balancer.fi/getting-started/faqs/liquidity-mining). 10 | 11 | In the following article, we describe how the legacy system was set up and maintained so that historical liquidity mining incentive data can be accessed easily. 12 | 13 | {% hint style="info" %} 14 | If you are looking for an in-depth guide on how to calculate Gauge APRs, consult the [veBAL and Gauges section](../../resources/vebal-and-gauges/estimating-gauge-incentive-aprs/#how-can-i-obtain-calculate-the-liquidity-mining-apr-for-a-certain-gauge) 15 | {% endhint %} 16 | 17 | ## **How was the legacy system set up?** 18 | 19 | The legacy system was set up and controlled by three subsystems: 20 | 21 | 1. A Liquidity Mining .json-file holding the corresponding BAL and partner token allocations for a given liquidity mining week. This .json file was maintained my the Liquidity Mining Committee 22 | 2. A claiming and distribution system developed by Balancer Labs called [Merkle Orchard ](https://docs.balancer.fi/products/merkle-orchard) 23 | 3. [A BAL liquidity mining repository](https://github.com/balancer-labs/bal-mining-scripts) calculating the per address distributions for the Merkle Orchards [per week](https://github.com/balancer-labs/bal-mining-scripts/tree/master/reports) 24 | 25 | ### Liquidity Mining .json Data Structure 26 | 27 | The [liquidity mining json file](https://raw.githubusercontent.com/balancer-labs/frontend-v2/89c9ee2e297425865475d91e20a9e8f8d14f59e1/src/lib/utils/liquidityMining/MultiTokenLiquidityMining.json) contains the BAL and partner token allocations per week per chain. Historically, balancer-v2 started in liquidity mining week 52. Therefore, the first entry is “week\_52”. The data structure is set up as follows: 28 | 29 | ```json 30 | "week_99": [ 31 | { 32 | "chainId": 137, 33 | "pools": { 34 | "0x0297e37f1873d2dab4487aa67cd56b58e2f27875000100000000000000000002": [ 35 | { 36 | "tokenAddress": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3", 37 | "amount": 5400 38 | } 39 | ], 40 | "0x36128d5436d2d70cab39c9af9cce146c38554ff0000100000000000000000008": [ 41 | { 42 | "tokenAddress": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3", 43 | "amount": 3190 44 | } 45 | ], 46 | "0x03cd191f589d12b0582a99808cf19851e468e6b500010000000000000000000a": [ 47 | { 48 | "tokenAddress": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3", 49 | "amount": 2250 50 | } 51 | ], 52 | } 53 | } 54 | ] 55 | ``` 56 | 57 | 1. week\_XY, where XY starts at 52 and ends with 99 58 | 2. chainID 59 | 1. 1 - mainnet 60 | 2. 137 - Polygon 61 | 3. 42161 - Arbitrum 62 | 3. A set of pools per chain 63 | 4. A set of token addresses and their corresponding emission amount 64 | 65 | ## How to calculate the historical APR for a given pool 66 | 67 | Given the above mentioned data structure from the liquidity mining .json file, one can calculate historical APRs by fetching historical pool data from the subgraph and cross-referencing it to the emission schedule. 68 | 69 | {% hint style="info" %} 70 | `week_52` in the liquidity mining json corresponds the 23rd of May 2021 with a unix start timestamp at `1621720800` 71 | {% endhint %} 72 | 73 | **Fetch historical pool data** 74 | 75 | ```graphql 76 | { 77 | poolHistoricalLiquidities (where: {poolId: "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014", block: "12376326" 78 | }) { 79 | block 80 | poolLiquidity 81 | pricingAsset 82 | } 83 | } 84 | ``` 85 | 86 | The total liquidity in the pool can then be inferred by multiplying the poolLiquidity with the price of the pricing asset at that time point (e.g. fetch historical BAL price at that block time). 87 | 88 | To fetch the historical price data of the pricingAsset of the underlying pool, do the following (example of BAL/WETH following above query): 89 | 90 | ```graphql 91 | { 92 | tokenSnapshots( 93 | first: 1000 94 | orderBy: timestamp 95 | orderDirection: asc 96 | where: { token: "0xba100000625a3754423978a60c9317c58a424e3d", timestamp_gte: 1620208633 } 97 | ) { 98 | id 99 | timestamp 100 | totalBalanceUSD 101 | totalBalanceNotional 102 | totalVolumeUSD 103 | totalVolumeNotional 104 | totalSwapCount 105 | } 106 | } 107 | ``` 108 | 109 | The token price at a given timestamp can then be derived as 110 | 111 | $$ 112 | tokenPriceAtTimeStamp = totalBalanceUSD / totalBalanceNotional 113 | $$ 114 | 115 | **With this information we can calculate the APR at the queried block as** 116 | 117 | $$ 118 | APR_{historical} = \frac{totalIncentivesAtCorrespondingLMWeek * tokenPriceAtTimeStamp * 52 * 100}{ poolLiquidity * priceOfPoolPricingAsset_{inferred}} 119 | $$ 120 | 121 | ​ 122 | -------------------------------------------------------------------------------- /guides/data/balancer-subgraph.md: -------------------------------------------------------------------------------- 1 | # Balancer Subgraph 2 | 3 | ## Why should I use the Balancer Subgraph? 4 | 5 | The Subgraph features easy-to-query data using GraphQL, and can log data in such a way that you can easily access data that's difficult to query on-chain. For example, there is no on-chain list of all Balancer pools (similar to how there's no on-chain list of all ERC20 tokens), but on the Subgraph, you can easily query all pools, even filtering by PoolType. 6 | 7 | Explaining what TheGraph is and how it works is outside the scope of this article, but if you'd like to know more, please refer to [their documentation](https://thegraph.com/docs/about/introduction). 8 | 9 | ## Code Walkthrough 10 | 11 | Select your desired programming language in the tabs below for the relevant tutorial. 12 | 13 | {% tabs %} 14 | {% tab title="Python" %} 15 | Let's step through [an example](https://github.com/gerrrg/balancer-tutorials/blob/master/python/data/subgraph.py) chunk by chunk of getting Balancer Pools and their tokens from the Subgraph. 16 | 17 | #### Dependencies 18 | 19 | ```python 20 | import json 21 | 22 | # thegraph queries 23 | from gql import gql, Client 24 | from gql.transport.requests import RequestsHTTPTransport 25 | ``` 26 | 27 | This sample relies on TheGraph's [gql library](https://github.com/graphql-python/gql) to query TheGraph and on a few other libraries. These dependencies can be found in the [requirements.txt](https://github.com/gerrrg/balancer-tutorials/blob/master/python/requirements.txt) file in the Python sample repository. 28 | 29 | #### Initialize TheGraph Connection 30 | 31 | ```python 32 | # Initialize subgraph 33 | subgraph_url = "https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2" 34 | balancer_transport=RequestsHTTPTransport( 35 | url=subgraph_url, 36 | verify=True, 37 | retries=3 38 | ) 39 | client = Client(transport=balancer_transport) 40 | ``` 41 | 42 | Here, we are creating the client for the Subgraph, pointing it to the Balancer Ethereum mainnet Subgraph. There are Subgraphs for each network on which Balancer runs. We will use this client to make all of our calls. 43 | 44 | #### Craft a Query and Request Data (Getting Pools) 45 | 46 | ```python 47 | query_string = ''' 48 | query {{ 49 | pools(first: {first}, skip: {skip}) {{ 50 | id 51 | address 52 | poolType 53 | strategyType 54 | swapFee 55 | amp 56 | }} 57 | }} 58 | ''' 59 | num_pools_to_query = 100 60 | formatted_query_string = query_string.format(first=num_pools_to_query, skip=0) 61 | response = client.execute(gql(formatted_query_string)) 62 | ``` 63 | 64 | Here, we create a **query\_string** that is written in GraphQL. We request pools with a variety of attributes for each one, specifying the maximum number of pools we want in _this_ request. The **first**/**skip** notation allows us to query pools in batches. We then get a response from the client, formatted as a Python _dict_. 65 | 66 | #### Request Tokens for each Pool 67 | 68 | ```python 69 | for pool in response["pools"]: 70 | pool_token_query = ''' 71 | query {{ 72 | poolTokens(first: 8, where: {{ poolId: "{pool_id}" }}) {{ 73 | id 74 | symbol 75 | name 76 | decimals 77 | address 78 | balance 79 | invested 80 | investments 81 | weight 82 | }} 83 | }} 84 | ''' 85 | formatted_query_string = pool_token_query.format(pool_id=pool["id"]) 86 | token_response = client.execute(gql(formatted_query_string)) 87 | pool["poolTokens"] = token_response["poolTokens"] 88 | 89 | print(json.dumps(response["pools"], indent=4)) 90 | ``` 91 | 92 | Using the list of pools from the previous call, we can now request **poolTokens** data for each pool. The **where** specifier in the query header filters for the specific pool that we're interested in. We can now add the **poolTokens** response into our pool _dict_ to keep all our data in the same structure. 93 | 94 | Finally, we print out the data formatted nicely as a json. See below for some sample output data: 95 | 96 | ```python 97 | ... 98 | { 99 | "address": "0x0b09dea16768f0799065c475be02919503cb2a35", 100 | "amp": null, 101 | "id": "0x0b09dea16768f0799065c475be02919503cb2a3500020000000000000000001a", 102 | "poolType": "Weighted", 103 | "strategyType": 2, 104 | "swapFee": "0.0027", 105 | "poolTokens": [ 106 | { 107 | "address": "0x6b175474e89094c44da98b954eedeac495271d0f", 108 | "balance": "41417760.60370376773502331", 109 | "decimals": 18, 110 | "id": "0x0b09dea16768f0799065c475be02919503cb2a3500020000000000000000001a-0x6b175474e89094c44da98b954eedeac495271d0f", 111 | "invested": "0", 112 | "investments": [], 113 | "name": "Dai Stablecoin", 114 | "symbol": "DAI", 115 | "weight": "0.4" 116 | }, 117 | { 118 | "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", 119 | "balance": "19467.723570242016061162", 120 | "decimals": 18, 121 | "id": "0x0b09dea16768f0799065c475be02919503cb2a3500020000000000000000001a-0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", 122 | "invested": "0", 123 | "investments": [], 124 | "name": "Wrapped Ether", 125 | "symbol": "WETH", 126 | "weight": "0.6" 127 | } 128 | ] 129 | }, 130 | ... 131 | ``` 132 | {% endtab %} 133 | {% endtabs %} 134 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Welcome](README.md) 4 | 5 | ## Guides 6 | 7 | * [Swaps](guides/swaps/README.md) 8 | * [Batch Swaps](guides/swaps/batch-swaps.md) 9 | * [Single Swaps](guides/swaps/single-swaps.md) 10 | * [Data](guides/data/README.md) 11 | * [Balancer Subgraph](guides/data/balancer-subgraph.md) 12 | * [Deployment](guides/data/balancer-subgraph/deployment.md) 13 | * [Inferring Historical Liquidity Mining APRs (Legacy)](guides/data/inferring-historical-liquidity-mining-aprs-legacy.md) 14 | 15 | ## Resources 16 | 17 | * [Deploy Pools from Factory](resources/deploy-pools-from-factory/README.md) 18 | * [Creation](resources/deploy-pools-from-factory/creation/README.md) 19 | * [WeightedPool](resources/deploy-pools-from-factory/creation/weightedpool.md) 20 | * [StablePhantom Pool](resources/deploy-pools-from-factory/creation/stablephantom-pool.md) 21 | * [Liquidity Bootstrapping Pool](resources/deploy-pools-from-factory/creation/liquidity-bootstrapping-pool.md) 22 | * [AaveLinearPool](resources/deploy-pools-from-factory/creation/aavelinearpool.md) 23 | * [Boosted MetaPool](resources/deploy-pools-from-factory/creation/boosted-metapool.md) 24 | * [StablePool](resources/deploy-pools-from-factory/creation/stablepool.md) 25 | * [MetaStable Pool](resources/deploy-pools-from-factory/creation/metastable-pool.md) 26 | * [Verification](resources/deploy-pools-from-factory/verification.md) 27 | * [Pool Interfacing](resources/pool-interfacing/README.md) 28 | * [Weighted Pool](resources/pool-interfacing/weighted-pool.md) 29 | * [Stable Pool](resources/pool-interfacing/stable-pool.md) 30 | * [MetaStable Pool](resources/pool-interfacing/metastable-pool.md) 31 | * [Liquidity Bootstrapping Pool](resources/pool-interfacing/liquidity-bootstrapping-pool.md) 32 | * [Managed Pools](resources/pool-interfacing/managed-pools.md) 33 | * [Oracle Pools](resources/pool-interfacing/oracle-pools.md) 34 | * [Pool Math](resources/pool-math/README.md) 35 | * [Weighted Math](resources/pool-math/weighted-math.md) 36 | * [Stable Math](resources/pool-math/stable-math.md) 37 | * [query{BatchSwap,Join,Exit}](resources/query-batchswap-join-exit.md) 38 | * [Joins and Exits](resources/joins-and-exits/README.md) 39 | * [Pool Joins](resources/joins-and-exits/pool-joins.md) 40 | * [Pool Exits](resources/joins-and-exits/pool-exits.md) 41 | * [Swaps](resources/swaps/README.md) 42 | * [Batch Swaps](resources/swaps/batch-swaps.md) 43 | * [Single Swap](resources/swaps/single-swap.md) 44 | * [Flash Swaps](resources/swaps/flash-swaps.md) 45 | * [Internal User Balances](resources/internal-user-balances.md) 46 | * [Smart Order Router](resources/smart-order-router.md) 47 | * [Flash Loans](resources/flash-loans.md) 48 | * [Rate Providers](resources/rate-providers.md) 49 | * [veBAL and Gauges](resources/vebal-and-gauges/README.md) 50 | * [veBAL](resources/vebal-and-gauges/vebal.md) 51 | * [Gauges](resources/vebal-and-gauges/gauges.md) 52 | * [Estimating Gauge Incentive APRs](resources/vebal-and-gauges/estimating-gauge-incentive-aprs/README.md) 53 | * [Data Fetching](resources/vebal-and-gauges/estimating-gauge-incentive-aprs/data-fetching.md) 54 | * [APR Calculation](resources/vebal-and-gauges/estimating-gauge-incentive-aprs/apr-calculation.md) 55 | 56 | ## Helpers 57 | 58 | * [Using Native ETH](helpers/using-native-eth.md) 59 | * [Encoding userData](helpers/encoding.md) 60 | 61 | ## References 62 | 63 | * [Contracts](references/contracts/README.md) 64 | * [APIs](references/contracts/apis/README.md) 65 | * [The Vault](references/contracts/apis/the-vault.md) 66 | * [BalancerHelpers](references/valuing-balancer-lp-tokens/balancerhelpers.md) 67 | * [Pools](references/valuing-balancer-lp-tokens/pools/README.md) 68 | * [WeightedPool](references/valuing-balancer-lp-tokens/pools/weightedpool.md) 69 | * [WeightedPool2Tokens](references/valuing-balancer-lp-tokens/pools/weightedpool2tokens.md) 70 | * [LiquidityBootstrappingPool](references/valuing-balancer-lp-tokens/pools/liquiditybootstrappingpool.md) 71 | * [ManagedPools (prev. "InvestmentPools")](references/valuing-balancer-lp-tokens/pools/investmentpools.md) 72 | * [StablePools](references/valuing-balancer-lp-tokens/pools/stablepools.md) 73 | * [MetaStablePools](references/valuing-balancer-lp-tokens/pools/metastablepools.md) 74 | * [Asset Managers](references/valuing-balancer-lp-tokens/asset-managers.md) 75 | * [Deployment Addresses](references/valuing-balancer-lp-tokens/deployment-addresses.md) 76 | * [Events](references/valuing-balancer-lp-tokens/events.md) 77 | * [UML Diagrams](references/valuing-balancer-lp-tokens/uml-diagrams.md) 78 | * [LP Tokens](references/lp-tokens/README.md) 79 | * [How Many BPT in veBAL?](references/lp-tokens/how-many-bpt-in-vebal.md) 80 | * [Valuing](references/lp-tokens/valuing.md) 81 | * [Underlying](references/lp-tokens/underlying.md) 82 | * [Error Codes](references/error-codes.md) 83 | * [Subgraphs](references/subgraphs.md) 84 | * [Github](https://github.com/balancer-labs/) 85 | 86 | ## Deep Dive 87 | 88 | * [Guided Tour of Balancer Vault](deep-dive/guided-tour-of-balancer-vault/README.md) 89 | * [Episode 1: The batchSwap](deep-dive/guided-tour-of-balancer-vault/episode-1-the-batchswap.md) 90 | * [Episode 2: Joins](deep-dive/guided-tour-of-balancer-vault/episode-2-joins/README.md) 91 | * [Detour: The INIT Join](deep-dive/guided-tour-of-balancer-vault/episode-2-joins/detour-the-init-join.md) 92 | * [Episode 3: Deploying a Pool](deep-dive/guided-tour-of-balancer-vault/episode-3-deploying-a-pool.md) 93 | * [Inside Balancer Contracts](deep-dive/inside-balancer-contracts/README.md) 94 | * [BasePool](deep-dive/inside-balancer-contracts/basepool.md) 95 | * [Timelock Authorizer](deep-dive/inside-balancer-contracts/timelock-authorizer.md) 96 | * [Recovery Mode](deep-dive/inside-balancer-contracts/recovery-mode.md) 97 | -------------------------------------------------------------------------------- /resources/vebal-and-gauges/gauges.md: -------------------------------------------------------------------------------- 1 | # Gauges 2 | 3 | ## How do I get a Gauge for a Given Mainnet Pool? 4 | 5 | The easiest way is to query `getPoolGauge(poolAddress)` on the [`LiquidityGaugeFactory`](https://etherscan.io/address/0x4E7bBd911cf1EFa442BC1b2e9Ea01ffE785412EC#code). 6 | 7 | ![Example query for bb-b-USD](<../../.gitbook/assets/Screen Shot 2022-04-27 at 11.07.35 AM.png>) 8 | 9 | ## How do I get a Gauge for a Given Pool on an alternate chain? 10 | 11 | To get a gauge, query `getPoolGauge(poolAddress)` on the given network's `ChildChainLiquidityGaugeFactory`**.** 12 | 13 | | Network | ChildChainLiquidityGaugeFactory | 14 | | -------- | ----------------------------------------------------------------------------------------------------------------------------------- | 15 | | Polygon | ``[`0x3b8cA519122CdD8efb272b0D3085453404B25bD0`](https://polygonscan.com/address/0x3b8cA519122CdD8efb272b0D3085453404B25bD0#code)`` | 16 | | Arbitrum | ``[`0xb08E16cFc07C684dAA2f93C70323BAdb2A6CBFd2`](https://arbiscan.io/address/0xb08E16cFc07C684dAA2f93C70323BAdb2A6CBFd2#code)`` | 17 | 18 | ![Example Call on Polygon](<../../.gitbook/assets/Screen Shot 2022-04-27 at 11.25.09 AM.png>) 19 | 20 | ## How to Query Pending Tokens for a Given Pool? 21 | 22 | The process differs slightly depending on if we're on Ethereum mainnet or an alternate network (ie Polygon, Arbitrum). No matter the network though, we need to first start at the relevant subgraph: 23 | 24 | * [Ethereum Gauges Subgraph](https://thegraph.com/hosted-service/subgraph/balancer-labs/balancer-gauges) 25 | * [Polygon Gauges Subgraph](https://thegraph.com/hosted-service/subgraph/balancer-labs/balancer-gauges-polygon) 26 | * [Arbitrum Gauges Subgraph](https://thegraph.com/hosted-service/subgraph/balancer-labs/balancer-gauges-arbitrum) 27 | 28 | ### Example 29 | 30 | Let's start with the [bb-a-USD pool](https://app.balancer.fi/#/pool/0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe) on Ethereum 31 | 32 | `0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe` 33 | 34 | #### Query the Gauges Subgraph: 35 | 36 | ``` 37 | { 38 | liquidityGauges(where:{ 39 | poolId: "0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe" 40 | }) 41 | { 42 | id 43 | } 44 | } 45 | ``` 46 | 47 | #### Result: 48 | 49 | ``` 50 | { 51 | "data": { 52 | "liquidityGauges": [ 53 | { 54 | "id": "0x68d019f64a7aa97e2d4e7363aee42251d08124fb" 55 | } 56 | ] 57 | } 58 | } 59 | ``` 60 | 61 | Now that we have our Gauge contract address, we can query what the pending tokens are with the following pseudocode: 62 | 63 | ``` 64 | gaugeAddress="0x68d019f64a7aa97e2d4e7363aee42251d08124fb"; 65 | userAddress=; 66 | gaugeAbi=; 67 | gauge=contract(gaugeAddress, gaugeAbi) 68 | 69 | // How to get pending BAL **ONLY ON MAINNET** 70 | pendingBAL = gauge.claimable_tokens(userAddress).call(); 71 | 72 | // How to get pending tokens 73 | tokenAddress=; 74 | pendingToken = gauge.claimable_rewards(userAddress, tokenAddress).call(); 75 | ``` 76 | 77 | {% hint style="warning" %} 78 | On Polygon and Arbitrum, the Gauges treat BAL the same as any other "reward" token, therefore instead of calling `claimable_tokens` __ on those networks, you will use `claimable_rewards` __ and pass in that network's BAL address. 79 | {% endhint %} 80 | 81 | ## How to Claim Pending Tokens for a Given Pool? 82 | 83 | ### Mainnet Ethereum 84 | 85 | Use the [`claim_rewards()`](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/liquidity-mining/contracts/gauges/ethereum/LiquidityGaugeV5.vy#L440-L450) function on the pool's gauge contract. 86 | 87 | ```python 88 | def claim_rewards(_addr: address = msg.sender, _receiver: address = ZERO_ADDRESS): 89 | """ 90 | @notice Claim available reward tokens for `_addr` 91 | @param _addr Address to claim for 92 | @param _receiver Address to transfer rewards to - if set to 93 | ZERO_ADDRESS, uses the default reward receiver 94 | for the caller 95 | """ 96 | if _receiver != ZERO_ADDRESS: 97 | assert _addr == msg.sender # dev: cannot redirect when claiming for another user 98 | self._checkpoint_rewards(_addr, self.totalSupply, True, _receiver) 99 | ``` 100 | 101 | ### Child Chains (L2, Sidechains, etc) 102 | 103 | Use the [`get_rewards()`](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/liquidity-mining/contracts/gauges/ChildChainStreamer.vy#L139-L148) function on the pool's streamer contract. 104 | 105 | ```python 106 | def get_reward(): 107 | """ 108 | @notice Claim pending rewards for `reward_receiver` 109 | """ 110 | last_update: uint256 = self.last_update_time 111 | for token in self.reward_tokens: 112 | if token == ZERO_ADDRESS: 113 | break 114 | self._update_reward(token, last_update) 115 | self.last_update_time = block.timestamp 116 | ``` 117 | 118 | ## What Tokens Exist for a Certain Gauge? 119 | 120 | ### Sample Query 121 | 122 | ``` 123 | { 124 | liquidityGauges(where:{ 125 | poolId: "0x32296969ef14eb0c6d29669c550d4a0449130230000200000000000000000080" 126 | }) 127 | { 128 | id 129 | tokens { 130 | id 131 | symbol 132 | decimals 133 | totalDeposited 134 | } 135 | } 136 | } 137 | ``` 138 | 139 | ### Sample Response 140 | 141 | ``` 142 | { 143 | "data": { 144 | "liquidityGauges": [ 145 | { 146 | "id": "0xcd4722b7c24c29e0413bdcd9e51404b4539d14ae", 147 | "tokens": [ 148 | { 149 | "id": "0x5a98fcbea516cf06857215779fd812ca3bef1b32-0xcd4722b7c24c29e0413bdcd9e51404b4539d14ae", 150 | "symbol": "LDO", 151 | "decimals": 18, 152 | "totalDeposited": "150000" 153 | } 154 | ] 155 | } 156 | ] 157 | } 158 | } 159 | ``` 160 | 161 | {% hint style="warning" %} 162 | Be aware that if there are no tokens other than BAL for a given Gauge, the tokens array will come back empty. 163 | {% endhint %} 164 | -------------------------------------------------------------------------------- /resources/vebal-and-gauges/estimating-gauge-incentive-aprs/data-fetching.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | In this subsection, we explain the needed steps to obtain all relevant data to 4 | calculate the LM APR for a given gauge 5 | --- 6 | 7 | # Data Fetching 8 | 9 | ## 1. **Obtain the current gauge allowlist from the front-end repo** 10 | 11 | The easiest way to obtain the current gauge allowlist is by referencing the Balancer labs front-end repo **** [**pools allowlist**](https://github.com/balancer-labs/frontend-v2/blob/develop/src/constants/pools.ts)**** 12 | 13 | This repository contains the pool addresses that have been allowlisted by our governance process to receive gauge votes. This configuration is currently undergoing weekly changes. 14 | 15 | The allowlisted pools are referenced in the Allowlist of the Stakable entry such as 16 | 17 | ```javascript 18 | Stakable: { 19 | AllowList: [ 20 | '0x06df3b2bbb68adc8b0e302443692037ed9f91b42000000000000000000000063', 21 | '0x072f14b85add63488ddad88f855fda4a99d6ac9b000200000000000000000027', 22 | '0x0b09dea16768f0799065c475be02919503cb2a3500020000000000000000001a', 23 | '0x186084ff790c65088ba694df11758fae4943ee9e000200000000000000000013', 24 | '0x1e19cf2d73a72ef1332c882f20534b6519be0276000200000000000000000112', 25 | ] 26 | } 27 | ``` 28 | 29 | ## **2. Obtain the working supply for a given gauge by reading its Vyper contract fields** 30 | 31 | In this first step, we are obtaining the so called “working supply” from the underlying gauge Vyper contracts. We can obtain this value by cross-referencing the poolId from the gauge allowlist with the [Balancer Gauge Subgraph](https://thegraph.com/hosted-service/subgraph/balancer-labs/balancer-gauges). 32 | 33 | You can obtain a list of all liquidity gauges by using following query 34 | 35 | ```graphql 36 | { 37 | votingEscrows { 38 | stakedSupply 39 | } 40 | liquidityGauges { 41 | symbol 42 | poolAddressjav 43 | poolId 44 | totalSupply 45 | id 46 | shares { 47 | balance 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | **An example response can consist of following entries, here an example of the D2D-gauge:** 54 | 55 | ``` 56 | "symbol": "50N/A-50N/A-gauge", 57 | "poolAddress": "0x8f4205e1604133d1875a3e771ae7e4f2b0865639", 58 | "poolId": "0x8f4205e1604133d1875a3e771ae7e4f2b086563900020000000000000000010e", 59 | "totalSupply": "179736.932086272708683666", 60 | "id": "0xc43d32bc349cea7e0fe829f53e26096c184756fa", 61 | ...... 62 | ``` 63 | 64 | This data can now be used to cross-reference the `poolId` with the VyperContract ID, which is needed for the next step of obtaining the `working_supply` of the corresponding gauge. For demonstration purposes we show how to read the data on Etherscan. You can use any preferred method such as [ethers](https://docs.ethers.io/v5/) to read the relevant contract data on your front-end. 65 | 66 | ### Read vyperContract data for gauge 67 | 68 | [Etherscan link](https://etherscan.io/address/0xc43d32bc349cea7e0fe829f53e26096c184756fa) 69 | 70 | **Javascript example with ethers** 71 | 72 | ```javascript 73 | import { ethers } from "ethers"; 74 | //Load contract ABI -> can be extracted from etherscan 75 | import vyperABI from './vyperABI.json' 76 | //Create a provider instance 77 | const provider = new ethers.providers.InfuraProvider("homestead" ,'YOUR_API_KEY'); 78 | //Instantiate vyper contract 79 | const vyperContract = new ethers.Contract( 80 | "0xc43d32bc349cea7e0fe829f53e26096c184756fa", 81 | vyperABI, 82 | provider 83 | ); 84 | let resp = 0; 85 | let working_supply = 0 86 | resp = await vyperContract.working_supply(); 87 | if (resp > 0) { 88 | working_supply = ethers.utils.formatEther(resp); 89 | } 90 | ``` 91 | 92 | ## **3. Obtain the relative weight for each gauge via the gauge controller contract** 93 | 94 | This step utilizes the gauge controller contract to obtain the currently active gauge weight for a given gauge 95 | 96 | {% hint style="info" %} 97 | **The gauge subgraph does not store historical vote allocations. Only the currently running vote round and the previous votes are stored. It is therefore not possible to infer historical metrics from the balancer-gauges subgraph endpoint. Alternative methods like Dune queries can be used to obtain historical gauge data. More details about historical votes can be found** [**here**](https://dune.com/balancerlabs/veBAL-Analysis) 98 | {% endhint %} 99 | 100 | The following list shows the gauge controller contracts for each chain: 101 | 102 | | Chain | Gauge Controller Contract ID | 103 | | -------- | ---------------------------------------------- | 104 | | Mainnet | **0xC128468b7Ce63eA702C1f104D55A2566b13D3ABD** | 105 | | Polygon | - | 106 | | Arbitrum | - | 107 | | Optimism | - | 108 | 109 | To obtain the currently active relative voting weight, read the `gauge_relative_weight` field. \ 110 | You can cross-reference your results with the [**veBAL voting Dune dashboard**](https://app.balancer.fi/#/vebal) 111 | 112 | ## **4. Infer the price per Balancer Pool Token (BPT) price from the balancer-v2 subgraph** 113 | 114 | We will now calculate the price per Balancer Pool Token (BPT) by obtaining the relevant pool data from the balancer-v2-subgraph and cross-referencing it with up-to-date price data 115 | 116 | {% hint style="info" %} 117 | **The balancer-v2 subgraph has a field “totalLiquidity” for the corresponding balancer pools. Please do not use this value for APR calculations as it infers price data from internal pricingAssets that are not always up-to-date. Therefore it is crucial to calculate the TVL and corresponding price per BPT via external pricing providers such as CoinGecko** 118 | {% endhint %} 119 | 120 | **Query Balancer pool data from the** [**balancer-v2 subgraph endpoint**](https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2/graphql)**** 121 | 122 | ```graphql 123 | { 124 | balancers(first: 500) { 125 | id 126 | pools(first: 500) { 127 | name 128 | totalLiquidity 129 | totalShares 130 | poolType 131 | tokens { 132 | symbol 133 | id 134 | balance 135 | weight 136 | } 137 | id 138 | } 139 | totalLiquidity 140 | } 141 | } 142 | ``` 143 | 144 | The response should contain a list of all pools. Alternatively you can apply a filter on the query instead of “first 500” based on the gauge allowlist to only obtain the relevant pool-set. 145 | 146 | You need following data to infer the price per BPT 147 | 148 | * totalShares 149 | * tokens 150 | * an external pricing service provider like Coingecko 151 | 152 | To calculate the price per BPT, do the following 153 | 154 | $$ 155 | pricePerBPT = \frac{\sum(token_i * priceOfToken_i)}{totalShares} 156 | $$ 157 | 158 | ## 5. Fetch the current BAL price 159 | 160 | Fetch the current price of BAL with your favourite pricing service provider. The easiest and free method is through the CoinGecko API such as 161 | 162 | ```javascript 163 | const bal_mainnet_token_id = '0xba100000625a3754423978a60c9317c58a424e3d' 164 | fetch('https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=' + bal_mainnet_token_id + '%2C&vs_currencies=usd'); 165 | ``` 166 | 167 | -------------------------------------------------------------------------------- /deep-dive/guided-tour-of-balancer-vault/episode-2-joins/detour-the-init-join.md: -------------------------------------------------------------------------------- 1 | # Detour: The INIT Join 2 | 3 | {% hint style="warning" %} 4 | If you made your way here randomly or after finishing [Episode 2: Joins](./), this may seem a bit discontinuous. This page is intended as a detour [during the `onJoinPool()` call](./#pools-basepool.sol-onjoinpool) in `BasePool`. 5 | 6 | If you're not interested in `INIT` joins, hop on over to [Episode 3: Deploying a Pool](../episode-3-deploying-a-pool.md) 7 | {% endhint %} 8 | 9 | ## The Code 10 | 11 | ### `pools/BasePool.sol` 12 | 13 | ``` 14 | if (totalSupply() == 0) { 15 | (uint256 bptAmountOut, uint256[] memory amountsIn) = _onInitializePool(poolId, sender, recipient, userData); 16 | ... 17 | } else { 18 | ... 19 | } 20 | ``` 21 | 22 | Now that we're interested in the first join on a pool, we enter the `totalSupply() == 0` if block. The first call we make it to `_onInitializePool()`. This is a [virtual function in BasePool](https://github.com/balancer-labs/balancer-v2-monorepo/blob/weighted-deployment/contracts/pools/BasePool.sol#L374-L379), but it is [implemented in WeightedPool](https://github.com/balancer-labs/balancer-v2-monorepo/blob/weighted-deployment/contracts/pools/weighted/WeightedPool.sol#L201-L228). 23 | 24 | ### `pools/weighted/WeightedPool.sol` 25 | 26 | ``` 27 | WeightedPool.JoinKind kind = userData.joinKind(); 28 | _require(kind == WeightedPool.JoinKind.INIT, Errors.UNINITIALIZED); 29 | ``` 30 | 31 | First in `_onInitializePool()`, we make sure that the user has sent a join of `JoinKind` `INIT`. This `JoinKind` is different from others since users do _not_ provide a BPT output amount/limit; the pool calculates the precise amount of BPT out since no one can front-run this operation. 32 | 33 | ``` 34 | uint256[] memory amountsIn = userData.initialAmountsIn(); 35 | ``` 36 | 37 | Next we decode the `amountsIn` from the `userData` in [`initialAmountsIn()`](https://github.com/balancer-labs/balancer-v2-monorepo/blob/weighted-deployment/contracts/pools/weighted/WeightedPoolUserDataHelpers.sol#L32-L34). 38 | 39 | ### `pools/weighted/WeightedPoolUserDataHelpers.sol` 40 | 41 | ``` 42 | function initialAmountsIn(bytes memory self) internal pure returns (uint256[] memory amountsIn) { 43 | (, amountsIn) = abi.decode(self, (WeightedPool.JoinKind, uint256[])); 44 | } 45 | ``` 46 | 47 | This function is simply decoding the `amountsIn` from `bytes` into a `uint256[]` array. 48 | 49 | ### `pools/weighted/WeightedPool.sol` 50 | 51 | #### `_onInitializePool()` 52 | 53 | ``` 54 | uint256[] memory amountsIn = userData.initialAmountsIn(); 55 | InputHelpers.ensureInputLengthMatch(_getTotalTokens(), amountsIn.length); 56 | _upscaleArray(amountsIn, _scalingFactors()); 57 | 58 | uint256[] memory normalizedWeights = _normalizedWeights(); 59 | ``` 60 | 61 | After getting our amountsIn, we verify their lengths and upscale them according to the decimals we stored during pool creation. Next we move to get `normalizedWeights` in [`_normalizedWeights()`](https://github.com/balancer-labs/balancer-v2-monorepo/blob/weighted-deployment/contracts/pools/weighted/WeightedPool.sol#L120-L137). 62 | 63 | #### `_normalizedWeights()` 64 | 65 | ``` 66 | function _normalizedWeights() internal view virtual returns (uint256[] memory) { 67 | uint256 totalTokens = _getTotalTokens(); 68 | uint256[] memory normalizedWeights = new uint256[](totalTokens); 69 | 70 | // prettier-ignore 71 | { 72 | if (totalTokens > 0) { normalizedWeights[0] = _normalizedWeight0; } else { return normalizedWeights; } 73 | if (totalTokens > 1) { normalizedWeights[1] = _normalizedWeight1; } else { return normalizedWeights; } 74 | if (totalTokens > 2) { normalizedWeights[2] = _normalizedWeight2; } else { return normalizedWeights; } 75 | if (totalTokens > 3) { normalizedWeights[3] = _normalizedWeight3; } else { return normalizedWeights; } 76 | if (totalTokens > 4) { normalizedWeights[4] = _normalizedWeight4; } else { return normalizedWeights; } 77 | if (totalTokens > 5) { normalizedWeights[5] = _normalizedWeight5; } else { return normalizedWeights; } 78 | if (totalTokens > 6) { normalizedWeights[6] = _normalizedWeight6; } else { return normalizedWeights; } 79 | if (totalTokens > 7) { normalizedWeights[7] = _normalizedWeight7; } else { return normalizedWeights; } 80 | } 81 | 82 | return normalizedWeights; 83 | } 84 | ``` 85 | 86 | First, we call `_getTotalTokens()` in [`BasePool.sol`](https://github.com/balancer-labs/balancer-v2-monorepo/blob/weighted-deployment/contracts/pools/BasePool.sol#L163-L165), which simply returns `_totalTokens`. From there, we take our immutable variables `normalizedWeights` and pack them into the dynamic array `normalizedWeights[]`, returning the array as soon as it's been filled with all the corresponding weights for `totalTokens`. 87 | 88 | #### `_onInitializePool()` 89 | 90 | ``` 91 | uint256 invariantAfterJoin = WeightedMath._calculateInvariant(normalizedWeights, amountsIn); 92 | 93 | // Set the initial BPT to the value of the invariant times the number of tokens. This makes BPT supply more 94 | // consistent in Pools with similar compositions but different number of tokens. 95 | uint256 bptAmountOut = Math.mul(invariantAfterJoin, _getTotalTokens()); 96 | 97 | _lastInvariant = invariantAfterJoin; 98 | 99 | return (bptAmountOut, amountsIn); 100 | ``` 101 | 102 | Now that we have our `normalizedWeights` and we've decoded our `amountsIn`, we have everything we need to calculate the starting invariant. This is calculated in [WeightedMath.sol](https://github.com/balancer-labs/balancer-v2-monorepo/blob/weighted-deployment/contracts/pools/weighted/WeightedMath.sol#L47-L65), where it puts these amount and weight values into the generalized constant product formula, $$\prod_{i=0}^{n}{{B_i}^{w_i}}$$. Next, the starting BPT amount, `bptAmountOut`, is calculated by multiplying the just-determined invariant with the number of tokens in the pool (from `_getTotalTokens()` in [`BasePool.sol`](https://github.com/balancer-labs/balancer-v2-monorepo/blob/weighted-deployment/contracts/pools/BasePool.sol#L163-L165), again). 103 | 104 | Now we set `_lastInvariant` to the invariant we just calculated. This value is saved for later in order to determine due protocol fees. Finally, we return our `bptAmountOut` and `amountsIn` and hop back up to [`onJoinPool()` in `BasePool.sol`](https://github.com/balancer-labs/balancer-v2-monorepo/blob/weighted-deployment/contracts/pools/BasePool.sol#L197-L245). 105 | 106 | ### `pools/BasePool.sol` 107 | 108 | ``` 109 | if (totalSupply() == 0) { 110 | (uint256 bptAmountOut, uint256[] memory amountsIn) = _onInitializePool(poolId, sender, recipient, userData); 111 | 112 | // On initialization, we lock _MINIMUM_BPT by minting it for the zero address. This BPT acts as a minimum 113 | // as it will never be burned, which reduces potential issues with rounding, and also prevents the Pool from 114 | // ever being fully drained. 115 | _require(bptAmountOut >= _MINIMUM_BPT, Errors.MINIMUM_BPT); 116 | _mintPoolTokens(address(0), _MINIMUM_BPT); 117 | _mintPoolTokens(recipient, bptAmountOut - _MINIMUM_BPT); 118 | 119 | // amountsIn are amounts entering the Pool, so we round up. 120 | _downscaleUpArray(amountsIn, scalingFactors); 121 | 122 | return (amountsIn, new uint256[](_getTotalTokens())); 123 | } else { 124 | ... 125 | } 126 | ``` 127 | 128 | We now mint BPT to two addresses: the user performing the `INIT` join, and the zero address. The zero address gets `_MINIMUM_BPT` (=1e6 wei) to prevent rounding issues and avoiding total pool drain. This is taken from the recipient's share, and is considered to be negligible. 129 | 130 | We now downscale the amounts according to their decimals, and return the `amountsIn` alongside a newly crafted array of zeros, which signify that the INIT join is incurring no protocol fees. 131 | 132 | ## Fin 133 | 134 | Now that we've completed our detour, feel free to hop back over to the [`onJoinPool()` call in `BasePool`](./#pools-basepool.sol-onjoinpool). 135 | -------------------------------------------------------------------------------- /deep-dive/inside-balancer-contracts/timelock-authorizer.md: -------------------------------------------------------------------------------- 1 | # Timelock Authorizer 2 | 3 | ## Credit 4 | 5 | Credit for the below article goes to [0xSkly](https://twitter.com/0xSkly) of the BeethovenX Team. You can find the original article on [Medium](https://medium.com/@0xSkly/inside-balancer-code-timelockauthorizer-5f5a0f7bec12). 6 | 7 | Disclaimer: The article has been lightly edited for formatting and uniformity with the rest of the docs, but has been kept in a first-person format. Any opinions expressed below are those of the author and should not be considered opinions of Balancer Labs, BeethovenX, BalancerDAO, or any other organization. 8 | 9 | ## Inside The Contract 10 | 11 | Fine grained authorization mechanisms are key when protecting a complex protocol like Balancer. Another key aspect is execution transparency combined with a delay so users can react on changes to the protocol before they go live. 12 | 13 | Currently, Balancer achieves this thorough a classic `Timelock` contract which handles execution delay combined with the `Authorizer` contract which handles authorization. They’ve now combined this into one contract with the fitting name `TimelockAuthorizer`. A main problem I have with `Timelock` contracts and other proxy contracts is that it obfuscates intent, making it harder for an average user to decode what is executed because of the hashed proxy call. Have you ever tried to follow a timelock transactions executed by a Gnosis Multisig proxy contract? Good luck. 14 | 15 | After this rather long intro, let’s do a deep dive into how this new contract works and if it makes execution intent easier to track. 16 | 17 | ### How are Authentication and Authorization Applied? 18 | 19 | Just as a quick recap, authentication is figuring out **who** you are, authorization is figuring out if you are **allowed to perform** this action. At the basis of this lies the `Authentication` contract. It provides the `authenticate` modifier. 20 | 21 | ![](https://miro.medium.com/max/1400/1\*zR5qW7RNlcMUK83lyC-MmA.png) 22 | 23 | We see the modifier calls the `_authenticateCaller` function which resolves an `actionId` and delegates a call to the virtual function `_canPerform` with the `sender` and the `actionId` which reverts when returned `false`. Just from looking at this snippet, we can assume that the `actionId`, which is based on the `msg.sig`, resembles the function to execute whereas `_canPerform` is expected to check if the caller is allowed to do this action. 24 | 25 | So let’s have a closer look at this `actionId` which seems to be a key concept. Why is it not just `msg.sig` which is the function signature you may ask. 26 | 27 | ![](https://miro.medium.com/max/1400/1\*9N4AdJ0SeTmJ3K66qkKOfQ.png) 28 | 29 | We see it creates a hash together with `_actionIdDisambiguator` . The comments do a good job describing this property. 30 | 31 | ![](https://miro.medium.com/max/1400/1\*xpwaMkCNWbCQ7l27BsA5kA.png) 32 | 33 | The nice thing about this is that it allows to share permissions, so that for example all contracts deployed from the same factory share the same permissions. Otherwise one would need to add permissions for each pool deployed from a factory which would be rather cumbersome. 34 | 35 | So now that we’ve got this out of the way, let’s move on to the `_canPerform` function, which apparently is responsible for authorization. For this we look at the `BasePoolAuthorization` contract which serves as a base for all pools. 36 | 37 | ![](https://miro.medium.com/max/1400/1\*8c3VvzZykE\_UhzmlGcLKbw.png) 38 | 39 | So if it’s an owner only action, only the owner can do this action (what surprise), otherwise we again delegate the call to our final destination, the `Authorizer,` finally coming back to the actual thing I wanted to talk about. What a detour, but it was kinda necessary to understand the full context. 40 | 41 | ### Inside the `TimelockAuthorizer` 42 | 43 | From what we have seen, we expect this contract to handle function execution permissions based on the `actionId` + `msg.sender` + `targetContractAddress` and also support some form of timelocked execution. We stopped at `_getAuthorizer().canPerform(..)` so let’s continue there 44 | 45 | ![](https://miro.medium.com/max/1400/1\*OX4OxQ7EMpfWEQZfB1YHrA.png) 46 | 47 | We see that `msg.sender` is referenced as `account` and the `targetContractAddress` is the `where` parameter. We check if this `actionId` has a delay configured (which means this action is timelocked) by checking `_delaysPerActionId[actionId] > 0`. If there is no delay configured, we just check if permissions are given. 48 | 49 | ![](https://miro.medium.com/max/1400/1\*RzhLWUQTIywHdHLHnYqQ5A.png) 50 | 51 | where `_isPermissionGranted` is a mapping between the hash over `actionId, account, where` to a boolean. 52 | 53 | If it has a delay, then only the `_executor` is allowed to call this function ( `account == address(_executor)`) which means the contract behind this `_executor` address is somehow executing a timelocked action. So let’s see whats up with this contract which is assigned in the constructor 54 | 55 | ``` 56 | _executor = new TimelockExecutor(); 57 | ``` 58 | 59 | The contract only has 1 function `execute` which basically proxies a function call 60 | 61 | ![](https://miro.medium.com/max/1400/1\*i\_OPXh5d\_ivQYbBLPH4qOA.png) 62 | 63 | The function can only be called by `address(authorizer)` which is assigned in the constructor 64 | 65 | ``` 66 | constructor() { authorizer = TimelockAuthorizer(msg.sender);} 67 | ``` 68 | 69 | where `msg.sender` is the `TimelockAuthorizer` which deploys the contract in its own constructor call. So only the `TimelockAuthorizer` contract can call the `execute` function. Therefore we can already assume the execution flow for a timelocked action to be something like `TimelockAuthorizer.execute(...) => TimelockExecutor.execute(...) => targetContract.performAction()` 70 | 71 | Now that we know what the contract behind the`_executor` is, let’s go back and figure out how this all comes together. For this, we jump into the `schedule` function which is the start of a function call protected with a delay (timelocked). 72 | 73 | ![](https://miro.medium.com/max/1400/1\*Z0TJLveOoNRsLuTwDB5Mkg.png) 74 | 75 | Seems pretty straight forward, we decode the action the caller wants to perform, verify he or she is permitted to do so and schedule it. Note that there is an additional `executors` argument passed along which is an array of addresses. We’ll see in a bit what that is about. Following the execution chain, it pulls the configured delay for this action and we end up in `_scheduleWithDelay`. 76 | 77 | ![](https://miro.medium.com/max/1400/1\*HKgyw5xIqEFKxYuD5A0utA.png) 78 | 79 | We define the `scheduledExecutionId` which increments on the `_scheduledExecutions` arrays length. We calculate the execution time and push the whole configuration into the array. If you pass no `executors` then it sets this `protected` flag, which I don’t know yet what it does, but I'm sure we’ll find out. Finally we see what the `executors` address array is for. All those addresses are granted the permission to execute this specific scheduled action without the need to give them permanent permissions to execute all actions with this `actionId`. This adds a nice additional layer of control. 80 | 81 | Now we made it to the final stretch, the execution of the scheduled action. 82 | 83 | ![](https://miro.medium.com/max/1400/1\*8sCH6MVkfzMmYKNdMpOSFA.png) 84 | 85 | It is triggered via its `scheduledExecutionId`, making sure enough time has passed via `block.timestamp > scheduledExecution.executableAt` and executes it via `_executor.execute(...)`. We also see now what happens when the `protected` flag is set. It checks the permission on the `msg.sender` , so if you pass an empty array for `executors` when scheduling an action, only addresses which have explicit permission for this `actionId` can execute it. 86 | 87 | There we are, we have seen the whole execution flow of actions which can be executed immediately and delayed (timelocked) actions. What we have not seen yet is how we grant & revoke permissions and configure delays for specific actions. But that’s for another time. 88 | 89 | ### Conclusion 90 | 91 | Merging the `Timelock` & `Authorizer` contract enables for an even more fine grained control over Balancer permissions and removes one level of indirection. Unfortunately, it still requires some sort of script to decode the scheduled actions where it would be nice for people if they could just inspect them from Etherscan. But to make this possible would come with its own drawbacks. 92 | -------------------------------------------------------------------------------- /resources/deploy-pools-from-factory/creation/README.md: -------------------------------------------------------------------------------- 1 | # Creation 2 | 3 | ## Overview 4 | 5 | {% hint style="warning" %} 6 | Before creating a new pool, make sure there isn't already a similar pool; there's no advantage to fragmenting liquidity! 7 | {% endhint %} 8 | 9 | Anyone can create a pool on Balancer. WeightedPools can be deployed using the [Pool Creator Interface](https://app.balancer.fi/#/pool/create) ([Polygon link](https://polygon.balancer.fi/#/pool/create)) ([Arbitrum link](https://arbitrum.balancer.fi/#/pool/create)), but users can deploy any pools programmatically. It's **highly recommended** that you deploy a pool on a **testnet** before doing so on a production network. 10 | 11 | You can deploy most if not all pools with balpy's `poolCreation` script -- [more info](./#deploying-a-pool-with-balpy). 12 | 13 | ## What kind of pool should I deploy? 14 | 15 | That totally depends on your use-case. You should read through the [descriptions of different Balancer PoolTypes](https://docs.balancer.fi/products/balancer-pools) to decide what you want to deploy. 16 | 17 | ## Deployment Process Summary 18 | 19 | ### Step 1: Deploy the Pool From Factory 20 | 21 | Each `poolType` has a factory from which users can deploy new pools. To deploy a pool, you must call that factory's `create()` function with the arguments that correspond to that specific pool. The below section goes over common arguments, and the subpages in this section go further into detail for the pool-specific arguments. 22 | 23 | ### Step 2: Add liquidity with the `INIT` join 24 | 25 | The `INIT` join can be done only once when the pool have a BPT `totalSupply` of 0. Almost all pools require you to use a `JoinKind` of type `INIT` before you can use the pool. 26 | 27 | {% hint style="info" %} 28 | Linear Pools are an exception to this rule; you can swap into the pool right away to receive BPT out. 29 | {% endhint %} 30 | 31 | {% hint style="warning" %} 32 | While StablePhantom Pools do use `INIT` joins, they require that in addition to the "normal" tokens, you must also pass the pools own BPT in a quantity of `2**112-1`. 33 | {% endhint %} 34 | 35 | ## Common Arguments 36 | 37 | `name` - The name of the pool corresponding Balancer Pool Token (BPT) 38 | 39 | `symbol` - The short symbol for the BPT 40 | 41 | `tokens` - A numerically sorted array of all tokens in the pool 42 | 43 | `swapFeePercentage` - How much of a swap fee the pool collects ([more below](./#fees)) 44 | 45 | `owner` - The "owner" of the pool: account that has some limited control over pool parameters ([more below](./#owner-rights)) 46 | 47 | ## Fees and Owners 48 | 49 | Two factors your should consider before deploying your pool are how fees and owners should be set. Pools can have static fees or dynamic fees, read [more about them in the main docs](https://docs.balancer.fi/concepts/fees#static-and-dynamic-fees). 50 | 51 | ### Fees 52 | 53 | #### Static Fees 54 | 55 | If you want static fees, you should set the fee you want the pool to have forever, and set the `owner` to the zero address `0x0000000000000000000000000000000000000000`. 56 | 57 | #### Dynamic Fees 58 | 59 | If you want dynamic fees, you should set the fee to an initial value, and set the `owner` either as the address that you want to control the pool fee, or to the delegate address. An address that is set as the owner has permission to set the fee to anything between 0.0001% and 10% whenever they want. 60 | 61 | If the pool owner is set to the delegate address (`0xBA1BA1ba1BA1bA1bA1Ba1BA1ba1BA1bA1ba1ba1B`) then Governance-approved fee-setters have permission to change the fee. Currently [Gauntlet has this authority](https://medium.com/gauntlet-networks/balancer-v2-pools-trading-fee-methodology-7a65df671b8c). 62 | 63 | ### Owner Rights 64 | 65 | Aside from setting swap fees, pool owners have other right on some pools that may play a role when deciding on an owner. 66 | 67 | * [Stable Pools](../../../references/valuing-balancer-lp-tokens/pools/stablepools.md#permissioned-functions) 68 | * Changing `ampParameter` 69 | * [MetaStable Pools](../../../references/valuing-balancer-lp-tokens/pools/metastablepools.md#permissioned-functions) 70 | * Changing `ampParameter` 71 | * Changing `cacheDuration` 72 | * [Liquidity Bootstrapping Pools](../../../references/valuing-balancer-lp-tokens/pools/liquiditybootstrappingpool.md#permissioned-functions) 73 | * Changing `swapEnable` 74 | * Changing weights 75 | * [Investment Pools](../../../references/valuing-balancer-lp-tokens/pools/investmentpools.md#permissioned-functions) 76 | * Changing `swapEnable` 77 | * Changing weights 78 | * Collecting management fees 79 | 80 | ## Deploying a pool with balpy 81 | 82 | The Balancer Python library [balpy](https://pypi.org/project/balpy/) supports deploying pools. Using the [samples in the balpy GitHub repository](https://github.com/balancer-labs/balpy/tree/main/samples/poolCreation), you can deploy pools from config files. There are sample config files for many pools including: 83 | 84 | * Weighted Pools 85 | * Oracle Pools (WeightedPool2Tokens) 86 | * Stable Pools 87 | * Liquidity Bootstrapping Pools 88 | * MetaStable Pools 89 | * Investment Pools 90 | * AaveLinear Pools 91 | * StablePhantom Pools 92 | 93 | More pools configs will be added as new factories are deployed. 94 | 95 | Once you have set up the necessary [environment variables](https://github.com/balancer-labs/balpy#environment-variables) and created your [virtual environment](https://github.com/balancer-labs/balpy#install), you can run the sample script with the command below. The script will ensure that you have sufficient token balances and **will execute token allowance approvals** if you do not have sufficient allowances. 96 | 97 | ``` 98 | python3 poolCreationSample.py 99 | ``` 100 | 101 | ## Deploying a pool with TypeScript 102 | 103 | {% hint style="info" %} 104 | This tutorial will illustrate deploying an **Ethereum** mainnet **WeightedPool** using **hardhat** and **ethers.** It also assumes that you have your artifacts built. 105 | 106 | Modify accordingly if you wish to deploy a different PoolType, use a different network, or use JS/Buidler/Truffle/etc. 107 | {% endhint %} 108 | 109 | ### Defining Addresses 110 | 111 | ``` 112 | // Contracts 113 | const VAULT = '0xBA12222222228d8Ba445958a75a0704d566BF2C8'; 114 | const WEIGHTED_POOL_FACTORY = '0x8E9aa87E45e92bad84D5F8DD1bff34Fb92637dE9'; 115 | const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; 116 | 117 | // Tokens -- MUST be sorted numerically 118 | const MKR = '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2'; 119 | const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; 120 | const USDT = '0xdac17f958d2ee523a2206206994597c13d831ec7'; 121 | const tokens = [MKR, WETH, USDT]; 122 | ``` 123 | 124 | {% hint style="danger" %} 125 | Your `tokens` array **must be sorted numerically**. All other corresponding arrays (ex. `weights`, defined below) should reflect this ordering 126 | {% endhint %} 127 | 128 | ### Pool Arguments 129 | 130 | We now define the name, symbol, swap fee, and token weights. In this example, we want the weights to be 70/15/15 corresponding to MKR/WETH/USDT. Weights must add up to 1 represented with 18 decimals. Swap fee must be between 0.0001% and 10%, where 100% is 1 represented with 18 decimals. 131 | 132 | ``` 133 | const NAME = 'Three-token Test Pool'; 134 | const SYMBOL = '70MKR-15WETH-15USDT'; 135 | const swapFeePercentage = 0.005e18; // 0.5% 136 | const weights = [0.7e18, 0.15e18, 0.15e18]; 137 | ``` 138 | 139 | ### Creating the Pool in the Factory 140 | 141 | In this block, we call the `create` function on the `WeightedPoolFactory` to deploy a new `WeightedPool`. We then get the `poolId` from the newly deployed pool. 142 | 143 | ``` 144 | const factory = await ethers.getContractAt('WeightedPoolFactory', 145 | WEIGHTED_POOL_FACTORY); 146 | 147 | // If you're creating a different type of pool, look up the create 148 | // function for your corresponding pool in that pool factory's ABI 149 | const tx = await factory.create(NAME, SYMBOL, tokens, weights, 150 | swapFeePercentage, ZERO_ADDRESS); 151 | const receipt = await tx.wait(); 152 | 153 | // We need to get the new pool address out of the PoolCreated event 154 | const events = receipt.events.filter((e) => e.event === 'PoolCreated'); 155 | const poolAddress = events[0].args.pool; 156 | 157 | // We're going to need the PoolId later, so ask the contract for it 158 | const pool = await ethers.getContractAt('WeightedPool', poolAddress); 159 | const poolId = await pool.getPoolId(); 160 | ``` 161 | 162 | ### Adding Tokens to Your New Pool 163 | 164 | Before we send tokens to the Vault, we must approve appropriate allowances so that it can move our tokens. We can send infinite approvals (`2e256 - 1`) or enough to satisfy the amounts we wish to move. 165 | 166 | ``` 167 | const vault = await ethers.getContractAt('Vault', VAULT); 168 | 169 | // Tokens must be in the same order 170 | // Values must be decimal-normalized! (USDT has 6 decimals) 171 | const initialBalances = [16.667e18, 3.5714e18, 7500e6]; 172 | 173 | // Need to approve the Vault to transfer the tokens! 174 | // Can do through Etherscan, or programmatically 175 | for (var i in tokens) { 176 | const tokenContract = await ethers.getContractAt('ERC20', tokens[i]); 177 | await tokenContract.approve(VAULT, initialBalances[i]); 178 | } 179 | ``` 180 | 181 | Now we must join the pool using a `JoinKind` of type `INIT` ([more info on the different types of `JoinKind`](../../joins-and-exits/pool-joins.md)). This requires a list of `initialBalances`, which must be in the **same order as the sorted token addresses**. We must [encode the userData](../../../helpers/encoding.md) for our join. We then call `joinPool` on the `Vault`, since that is where all tokens are held. 182 | 183 | ``` 184 | // Construct userData 185 | const JOIN_KIND_INIT = 0; 186 | const initUserData = 187 | ethers.utils.defaultAbiCoder.encode(['uint256', 'uint256[]'], 188 | [JOIN_KIND_INIT, initialBalances]); 189 | 190 | const joinPoolRequest = { 191 | assets: tokens, 192 | maxAmountsIn: initialBalances, 193 | userData: initUserData, 194 | fromInternalBalance: false 195 | } 196 | 197 | // define caller as the address you're calling from 198 | caller = '0x...YOUR_ADDRESS_HERE...'; 199 | 200 | // joins are done on the Vault 201 | const tx = await vault.joinPool(poolId, caller, caller, joinPoolRequest); 202 | 203 | // You can wait for it like this, or just print the tx hash and monitor 204 | const receipt = await tx.wait(); 205 | ``` 206 | 207 | At this point you should have a funded `WeightedPool`, visible on the Balancer UI. In this mainnet example, you would be able to reach this page at: https://app.balancer.fi/#/pool/`` 208 | -------------------------------------------------------------------------------- /resources/swaps/batch-swaps.md: -------------------------------------------------------------------------------- 1 | # Batch Swaps 2 | 3 | ## Batch Swap Overview 4 | 5 | Balancer V2 allows powerful multi-hop trades, or "batch swaps", which pull the best prices from all the pools registered with the Vault. 6 | 7 | The Vault exposes the `batchSwap` function to allow multi-hop trades with the the interface below. 8 | 9 | ``` 10 | batchSwap(SwapKind kind, 11 | BatchSwapStep[] swaps, 12 | IAsset[] assets, 13 | FundManagement funds, 14 | int256[] limits, 15 | uint256 deadline) returns (int256[] assetDeltas) 16 | ``` 17 | 18 | To simplify the inputs to this function, we have grouped related fields into a number of structs which are explained below. 19 | 20 | ### BatchSwapStep struct 21 | 22 | ``` 23 | struct BatchSwapStep { 24 | bytes32 poolId; 25 | uint256 assetInIndex; 26 | uint256 assetOutIndex; 27 | uint256 amount; 28 | bytes userData; 29 | } 30 | ``` 31 | 32 | * `poolId`: The id of the pool to trade with. 33 | * `assetInIndex`: The index of the token within `assets` which to use as an input of this step. 34 | * `assetOutIndex`: The index of the token within `assets` which is the output of this step. 35 | * `amount`: The meaning of `amount` depends on the value of `kind` which passed to the `batchSwap` function. 36 | * `GIVEN_IN`: The amount of tokens we are sending to the pool in this step. 37 | * `GIVEN_OUT`: The amount of tokens we want to receive from the pool in this step. 38 | * `userData`: Any additional data which the pool requires to perform the swap. This allows pools to have more flexible swapping logic in future - for all current Balancer pools this can be left empty. 39 | 40 | {% hint style="info" %} 41 | When performing multi-hop trades, it's not always possible to know the value of `amount`exactly. For example, consider a case where we want to trade USDC for ETH but the trade is being routed through a DAI-USDC pool and then a ETH-DAI pool. It's not possible to know exactly how much DAI we'll receive from this first step so we can't set `amount` to that value at the time we send the transaction. 42 | 43 | For this reason setting `amount` to 0 will be interpreted to use the full output of the previous trade. We can then trade USDC for DAI and then all of the DAI we receive will be traded for ETH. 44 | {% endhint %} 45 | 46 | ### FundManagement struct 47 | 48 | The `FundManagement` struct defines where the input tokens for the first swap are coming from and where any tokens received from swaps should be sent. The `FundManagement` struct is defined as below. 49 | 50 | ``` 51 | struct FundManagement { 52 | address sender; 53 | bool fromInternalBalance; 54 | address payable recipient; 55 | bool toInternalBalance; 56 | } 57 | ``` 58 | 59 | * `sender`: The address from which tokens will be taken to perform the trade 60 | * `fromInternalBalance`: Whether the trade should use tokens owned by the `sender` which are already stored in the Vault. 61 | * `recipient`: The address to which tokens will be sent to after the trade. 62 | * `toInternalBalance`: Whether the tokens should be sent to the `recipient` or stored within their internal balance within the Vault. 63 | 64 | For more information on internal balances see [Core Concepts](broken-reference/). 65 | 66 | ### BatchSwap function 67 | 68 | ``` 69 | batchSwap(SwapKind kind, 70 | BatchSwapStep[] swaps, 71 | IAsset[] assets, 72 | FundManagement funds, 73 | int256[] limits, 74 | uint256 deadline) returns (int256[] assetDeltas) 75 | ``` 76 | 77 | * `kind`: The type of batch swap we want to perform - either "Out Given In" or "In Given Out." We either know the amount of tokens we're sending to the pool and want to know how many we'll receive, or vice versa. 78 | * `assets`: An array of tokens which are used in the batch swap. This is referenced from within `swaps` 79 | * `limits`: An array of maximum amounts of each `asset` to be transferred. For tokens going **in** to the Vault, the `limit` shall be a positive number. For tokens going **out** of the Vault, the `limit` shall be a negative number. If the `amount` to be transferred for a given asset is greater than its `limit`, the trade will fail with error `BAL#507: SWAP_LIMIT`. 80 | * **How do you determine what your `limits` should be?** If you want to compute `limits`, it is recommended to use `queryBatchSwap` and then [add a slippage tolerance](batch-swaps.md#adding-a-slippage-tolerance). 81 | * `deadline`: The UNIX timestamp at which our trade must be completed by - if the transaction is confirmed after this time, the transaction will fail. 82 | 83 | ## `queryBatchSwap` 84 | 85 | `queryBatchSwap` is an extremely useful function in the `Vault` contract. With `queryBatchSwap`, you can get the exact amounts for a given swap with the on-chain state. You can use these amounts to calculate input/output limits based on a slippage tolerance. 86 | 87 | {% hint style="warning" %} 88 | You should **NOT** use `queryBatchSwap` to calculate limits from a smart contract that is executing a swap. **This will leave you vulnerable to sandwich attacks.** 89 | 90 | You should only use `queryBatchSwap` **before** sending a `batchSwap` transaction, when calculating your `batchSwap` arguments off-chain. 91 | {% endhint %} 92 | 93 | Calling `queryBatchSwap` is very similar to calling [`batchSwap`](batch-swaps.md#batchswap-function) itself, just without the `limit` and `deadline` arguments. 94 | 95 | ``` 96 | queryBatchSwap(SwapKind kind, 97 | BatchSwapStep[] swaps, 98 | IAsset[] assets, 99 | FundManagement funds) 100 | returns (int256[] assetDeltas) 101 | ``` 102 | 103 | {% hint style="info" %} 104 | To use `queryBatchSwap`, you must use `eth_call`. 105 | 106 | You may notice that `queryBatchSwap` shows up on Etherscan as a `write` function, but this is simply due to the fact that the function fully executes a `batchSwap` before reverting. 107 | {% endhint %} 108 | 109 | ### Adding a Slippage Tolerance 110 | 111 | Once you have received your `assetDeltas` from calling [`queryBatchSwap`](batch-swaps.md#querybatchswap), you can calculate `limits` for a `batchSwap` by applying your slippage tolerance. 112 | 113 | #### `GIVEN_IN` 114 | 115 | If we are performing a `GIVEN_IN` `batchSwap` and wanted to apply a 1% slippage tolerance, we would multiple our negative `assetDeltas` by 0.99. We do not need to modify our positive amounts because we know the exact amount we are putting in. 116 | 117 | #### `GIVEN_OUT` 118 | 119 | If we are performing a `GIVEN_OUT` `batchSwap` and wanted to apply a 1% slippage tolerance, we would multiple our positive `assetDeltas` by 1.01. We do not need to modify our negative amounts because we know the exact amount we are getting out. 120 | 121 | ## Multi-hop Examples 122 | 123 | In these examples, we’re trading token A for token C, through the intermediate token B (we could illustrate this as A->B->C). Tokens A, B, and C could be in different pools, or in the same pool. 124 | 125 | "Given In" means the caller knows the exact amount of the incoming token, and is asking the pool to calculate the tokenOut amount. The opposite is true of "Given Out." 126 | 127 | ### Example 1 ("Given In") 128 | 129 | The first case is a "Given In" Batch Swap: say we have 10 A and want to know how much C we can get for it. We can accomplish this with a two-step multi-hop swap: A for B, then B for C. 130 | 131 | Since we know we have 10 A to start, the swap kind is `GIVEN_IN`, and the amount for the first swap is 10. The first swap will produce some output amount of B, but we don’t know in advance how much. 132 | 133 | Since we don’t know the amount of B when constructing the multi-hop, we initialize the amount in the second swap to 0, which instructs the multi-hop logic to use the calculated output amount from the first swap as input to the second. 134 | 135 | | Parameter | Swap 1 | Swap 2 | 136 | | --------- | ------ | ------ | 137 | | Amount | 10 | 0 | 138 | | Token In | A | B | 139 | | Token Out | B | C | 140 | 141 | Say we get 5 B from the first swap. The amount of the second swap is then set to 5 in the Vault logic, and the second swap produces some output amount of C. (The caller would then validate the overall swap by comparing this value to the minimum amountOut of C.) 142 | 143 | ### Example 2 ("Given Out") 144 | 145 | The second case is a “Given Out” Batch Swap: say we want 20 C, and want to know how much A that will cost. Here we need to do the swaps “backwards,” first trading C for B, then B for A. 146 | 147 | Since we know we want to get 20 C out, the swap kind is `GIVEN_OUT`, and the amount for the first swap is 20. The first swap will produce some require input amount of token B, but as before, we don’t know how much in advance. So again we set the amount of the second swap to zero. 148 | 149 | After the first swap, the amount of B will be known, and the zero amount in the second swap instructs the multi-hop logic to substitute the calculated amount from the first swap. Since this is a “Given In” Batch Swap, the result will be the required input amount of token A. (The caller would then validate the overall swap by comparing this value to the maximum amountIn of A.) 150 | 151 | | Parameter | Swap 1 | Swap 2 | 152 | | --------- | ------ | ------ | 153 | | Amount | 20 | 0 | 154 | | Token Out | C | B | 155 | | Token In | B | A | 156 | 157 | So in both cases, setting the amount of a swap within a batch to zero causes the multi-hop logic to substitute the calculated amount from the previous swap. If the batch swap kind is “Given In,” the calculated amount will be the “output” of the previous step. If the batch swap kind is “Given Out,” the calculated amount will be the “input” from the previous step. 158 | 159 | Of course, the amount of the first swap in a batch cannot be zero. The batch swap must begin with a known piece of data: for a "Given In," the input amount; or for a "Given Out," the output amount. 160 | 161 | ## Parallel Examples 162 | 163 | As described in the examples above, batch swaps are most commonly used for multi-hop trades. Although much less common, it is also possible to use batch swaps for a set of unrelated swaps to be performed in parallel. 164 | 165 | ### Example 3 (Parallel Single Swaps - "Given In") 166 | 167 | In this case, the input amount of each swap must be provided explicitly. In this `GIVEN_IN` batch swap, the user will supply 99 A, 42 C, and 5 E, and will be returned computed amounts of B, D, and F. 168 | 169 | | Parameter | Swap 1 | Swap 2 | Swap 3 | 170 | | --------- | ------ | ------ | ------ | 171 | | Amount | 99 | 42 | 5 | 172 | | Token In | A | C | E | 173 | | Token Out | B | D | F | 174 | 175 | ### Example 4 (Combined Swaps - "Given Out") 176 | 177 | And of course, it is also possible to combine multi-hop trades and single-hop swaps in parallel. This example performs two trades in `GIVEN_OUT` fashion: A->B->C->D and E->F. The final outputs will be 100 D and 50 F, if the user can supply the computed amounts of A and E. 178 | 179 | | Parameter | Swap 1 | Swap 2 | Swap 3 | Swap 4 | 180 | | --------- | ------ | ------ | ------ | ------ | 181 | | Amount | 100 | 0 | 0 | 50 | 182 | | Token Out | D | C | B | F | 183 | | Token In | C | B | A | E | 184 | -------------------------------------------------------------------------------- /resources/smart-order-router.md: -------------------------------------------------------------------------------- 1 | # Smart Order Router 2 | 3 | ## Summary 4 | 5 | {% hint style="info" %} 6 | This section goes over SOR v2. For information on SOR v1, please refer to the [old documentation](https://docs.balancer.fi/v/v1/smart-contracts/sor/). 7 | {% endhint %} 8 | 9 | SOR v2 finds optimal trading routes across different and arbitrary types of pools. This generality enables Balancer to be the most flexible AMM protocol that exists. By abstracting the complexity of various pool types from traders, users can issue swaps within the aggregated liquidity in the Vault and not need to worry about the different pool math involved. 10 | 11 | The only prerequisite for SOR v2 to work with a pool is that it has first and second differentiable (either numerically or analytically)`spotPriceAfterSwap` functions. 12 | 13 | The final objective of SOR is to find a trade that maximizes the return for the user. One requirement for this to be true is that after the swap is done, each of the paths/routes used ends up having the same spot price. This means that there is no arbitrage possible after the trade (at least with the pools used) and therefore no money left on the table. 14 | 15 | ## Glossary 16 | 17 | * `tokenIn`: the address of the token being sold by user 18 | * `tokenOut`: the address of the token being bought by user 19 | * `swapType` < `'swapExactIn'` , `'swapExactOut'`>: the type of swap being done. The user can select the amount they want to sell (in `tokenIn`) or the amount they want to buy (in `tokenOut`) 20 | * `targetAmountSwap`: the amount the user wants to buy OR sell, depending on `swapType`. If `swapType` is `'swapExactIn'` , then `targetAmountSwap` is the amount the user wants to sell. Conversely, if `swapType` is `'swapExactOut'` , then `targetAmountSwap` is the amount the user wants to buy. 21 | * `pools`: is a dictionary that is loaded from subgraph with all the pools available on Balancer 22 | * `paths`: a path is a sequence of pools that enable the trade `tokenIn` to `tokenOut` to happen. Paths can contain one hop (direct trades) or two hops. For example a trade of DAI for BAL could have direct trades with pools that contain both DAI and BAL and also multihop paths, for example DAI→WETH→BAL or DAI→USDC→BAL. 23 | * `pairType` < `'token->token'` , `'token->BPT'` ,`'BPT->token'` >: This is something new to SOR v2. Join/Exit pools with a single token are considered by SOR as a swap just like a swap between two underlying tokens in a pool. This allows for cool use cases like swapping DAI for GUSD in two hops if there is a big stable pool (say pool A) with \ and another stable pool with \, BPTA being the BPT of pool A. So if a hop involves joining a pool, i.e. DAI for BPTA, this `pairType` would be `'token->BPT'` 24 | * `maxPools`: is the maximum number of swaps the final solution of SOR can have. A multihop has two swaps, for example. 25 | * `returnToken`: is the token whose amount is unknown for the swap and SOR is calculating. Simply put, if `swapType` is `'swapExactIn'` the user is inputting how much they want to **sell** (`targetAmountSwap` is in `tokenIn`) and will get from SOR a `returnAmount` which is how much they will get back in `tokenOut`. Conversely, if `swapType` is `'swapExactOut'`, then the user is inputting how much they want to **buy** (`targetAmountSwap` is in `tokenOut`) and will get from SOR a `returnAmount` which is how much they have to pay in `tokenIn`. 26 | * `costReturnToken`: is how much an additional swap would add in gas costs in terms of `returnToken`. For example, for a `'swapExactIn'` swap of DAI for BAL, `costReturnToken` could be 0.1 BAL (BAL is the `returnToken`). This is simply calculated by: 27 | 28 | $$ 29 | costReturnToken = \frac{gasPrice \cdot gasAddSwap}{priceReturnTokenInETH} 30 | $$ 31 | 32 | ## Basic algorithm 33 | 34 | The basic flow of SOR v2 is as follows 35 | 36 | 1. Check how many pools (`initialNumPaths`) of the most liquid pools we need to be able to trade the `targetAmountSwap`. Usually `targetAmountSwap` is much lower than the liquidity available in the largest pools so this is usually 1. Start with next step with `b = initialNumPaths` where b is the number of paths being considered. 37 | 2. Find out the best `b` paths and the best distribution of `targetAmountSwap` which is a list called `swapAmounts` with `b` amounts to be swapped in each of the `b` paths. The sum of all amounts in `swapAmounts` is always `targetAmountSwap`. This step also calculates the `returnAmount` the suggested swaps return. 38 | 3. Compare `returnAmount` of previous step with `bestReturnAmount` (which is the best return amount so far) considering the additional costs of adding an extra path (`costReturnToken * nSwapsInPath`). If the `returnAmount` is better than `bestReturnAmount` then increment `b` and return to step 2). If not the algorithm has found the final solution. IMPORTANT: notice that if `swapType` is `'swapExactIn'` having a better `returnAmount` means that it is higher than `bestReturnAmount`, i.e. the user is getting more `tokenOut` . Conversely, if `swapType` is `'swapExactOut'` having a better `returnAmount` means that it is lower than `bestReturnAmount`, i.e. the user is paying less `tokenIn` for the desired amount (`targetAmountSwap`) of `tokenOut` they want to buy. 39 | 40 | ## Deep dive into step 2) 41 | 42 | The most challenging part of the algorithm is to find out what is the best combination of paths and swapAmounts for a swap given how many paths we want to include (variable `b`mentioned above). 43 | 44 | Step 2) can be broken down into the following sub-steps: 45 | 46 | 2.1. Start `swapAmounts` list based on the previous `bestSwapAmounts` by adding another element to the list which is defined as `targetAmountSwap/b` and scale down all the elements of `bestSwapAmounts` by `1-1/b` so that the sum still is equal to `targetAmountSwap`. For example, if the previous `bestSwapAmounts` for `b=2` were `[150, 30]` then the new initial `swapAmounts` for `b=3` will be `[100, 20, 60]` 47 | 48 | 2.2 Given `swapAmounts` from above, find the best paths using `getBestPathIds()` (see detailed information about this function in appendix below). 49 | 50 | 2.3 Given current `swapAmounts` and `bestPathIds` find new `swapAmounts` that maximize the return for the given `bestPathIds` 51 | 52 | 2.4 Return to step 2.2) above with new `swapAmounts` and check if function `getBestPathIds()` returns the same `bestPathIds`. If yes, this means that we have converged to the best paths and swapAmounts and step 2) is finalized. If not, then go to step 2.3) again 53 | 54 | ## Deep dive into step 2.3) 55 | 56 | Step 2.3) finds the `swapAmounts` that will maximize the returns given an initial guess of `swapAmounts` and the list of paths that should be used (`bestPathIds`). This is all done in function `iterateSwapAmounts()` which will be described below in its different steps. 57 | 58 | 2.3.1 Call `iterateSwapAmountsApproximation()` and which does a first iteration in approximating `swapAmounts` to the optimal ones (this function is explained in detail in the appendix below). This function returns `prices` which are the prices after swap for each of the paths considering their respective `amountSwap` in `swapAmounts` as long as that `amountSwap` is NOT at the limit of the path (i.e. equal to `path.limitAmount`) AND is not zero. 59 | 60 | 2.3.2 Check if `prices` are the same within a given error margin. If this is true, then the objective of not leaving paths in a state that can be arbed has been achieved and the optimal distribution of swap amounts for these paths has been found. If prices are still not close enough to each other go back to step 2.3.1. 61 | 62 | ## Appendix 63 | 64 | ### getBestPathIds() 65 | 66 | `getBestPathIds()` takes as inputs `swapAmounts` and all available `paths` and is expected to find the paths that give the best return for the given `swapAmounts`. The algorithm to do so is very simple: sort the `swapAmounts` by descending order and starting with the largest `swapAmount`, find the path that has the best return. Select this path and remove it from the list of available paths as we do not want to use the same path twice (as using it the first time would affect its price and slippage). The continue for each subsequent `swapAmount` until we chose a path for each `swapAmount` 67 | 68 | ### iterateSwapAmountsApproximation() 69 | 70 | `iterateSwapAmountsApproximation()` takes as inputs `swapAmounts` and `bestPathIds` and is expected to find new `swapAmounts` that have spot prices after swap as close as possible. 71 | 72 | To help illustrate this, let's start with two paths (1 and 2) and initial `swapAmounts` equal to $$A_i$$ and $$A_2$$ . Remember that always the sum of the individual amounts is `targetAmountSwap`. 73 | 74 | The objective is to find a target spot price (`targetSP`) and $$A'_1$$ and $$A'_2$$ . such that: 75 | 76 | $$ 77 | SPaA_1(A_1) =SPaA_2(A_2)= targetSP \\ 78 | A_1'+A_2' = A_1+A_2 = A_T 79 | $$ 80 | 81 | To help the visualization, this is what we are looking to achieve: 82 | 83 | 84 | 85 | To calculate a candidate for `targetSP` we need to use the derivatives of $$SPaS_1$$ and $$SPaS_2$$ at $$A_1$$ and $$A_2$$ respectively. 86 | 87 | Using simple trigonometry we can say that: 88 | 89 | $$ 90 | SPaS_1(A_1)-targetSP = SPaS_1'(A_1)\cdot(A_1'-A_1)\\ 91 | SPaS_2(A_2)-targetSP = SPaS_2'(A_2)\cdot(A_2'-A_2) 92 | $$ 93 | 94 | The solution for this system of equations is: 95 | 96 | $$ 97 | targetSP = \frac{\frac{SPaS_1(A_1)}{SPaS_1'(A_1)}+\frac{SPaS_2(A_2)}{SPaS_2'(A_2)}}{\frac{1}{SPaS_1'(A_1)}+\frac{1}{SPaS_1'(A_1)}} 98 | $$ 99 | 100 | In other words, the target spot price is the average of the spot prices after swap weighted by the inverse of their derivatives. The derivatives of $$SPaS$$ can be seen as the slippage of that path. Generalizing for any number of paths we have: 101 | 102 | $$ 103 | targetSP = \frac{\sum_i{\frac{SPaS_i(A_i)}{SPaS'_i(A_i)}}} {\sum_i{\frac{1}{SPaS'_i(A_i)}}} 104 | $$ 105 | 106 | After calculating `targetSP` it's easy to replace it in the equations above to find each $$A'_i$$ : 107 | 108 | $$ 109 | A'_i = \frac{SPaS_i(Ai) - targetSP}{SPaS'_i(Ai)}+A_i 110 | $$ 111 | 112 | Notice that the paths have limits in the amounts they can be used to swap. The lower boundary is always zero, since you cannot swap a negative number. The upper boundary is usually defined by 50% of the balance a pool has in the token being swapped. These limits have to be taken into account and respected in the choice of `swapAmounts`. Function `redistributeInputAmounts()` does exactly that. Let's take a look at it into more detail below. 113 | 114 | ### redistributeInputAmounts() 115 | 116 | If after the calculations done in `iterateSwapAmountsApproximation()` above we end up with $$A'_i$$ that is negative or above the limit of path $$i$$ , then we have to set it to 0 or $$A_{limit_i}$$ respectively. The excesses have to be 'redistributed' to the other viable paths (i.e. paths that do not have swap amounts below zero or above the path limit), otherwise the sum of $$A_i$$ for all paths $$i$$ is not going to be equal to `targetAmountSwap` as it should. 117 | 118 | Function `redistributeInputAmounts()` might need to be calculated several times in a row because as the excesses are redistributed to paths that are viable, their swap amounts might go below zero or beyond the limit. We call `redistributeInputAmounts()` iteratively until all swap amounts are above or equal to zero **and** below or equal to their path limit amounts. 119 | --------------------------------------------------------------------------------