diff --git a/natspec-smells.config.js b/natspec-smells.config.js index 458623a3..54baf0c0 100644 --- a/natspec-smells.config.js +++ b/natspec-smells.config.js @@ -4,5 +4,5 @@ /** @type {import('@defi-wonderland/natspec-smells').Config} */ module.exports = { - include: 'src' + include: 'src/**/*.sol' }; diff --git a/src/contracts/BFactory.sol b/src/contracts/BFactory.sol index 8036a9d7..8929c8c6 100644 --- a/src/contracts/BFactory.sol +++ b/src/contracts/BFactory.sol @@ -1,19 +1,25 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.25; -// Builds new BPools, logging their addresses and providing `isBPool(address) -> (bool)` import {BPool} from './BPool.sol'; import {IBFactory} from 'interfaces/IBFactory.sol'; import {IBPool} from 'interfaces/IBPool.sol'; +/** + * @title BFactory + * @notice Creates new BPools, logging their addresses and acting as a registry of pools. + */ contract BFactory is IBFactory { + /// @dev Mapping indicating whether the address is a BPool. mapping(address => bool) internal _isBPool; + /// @dev bLabs address. address internal _blabs; constructor() { _blabs = msg.sender; } + /// @inheritdoc IBFactory function newBPool() external returns (IBPool _pool) { IBPool bpool = new BPool(); _isBPool[address(bpool)] = true; @@ -22,12 +28,14 @@ contract BFactory is IBFactory { return bpool; } + /// @inheritdoc IBFactory function setBLabs(address b) external { require(msg.sender == _blabs, 'ERR_NOT_BLABS'); emit LOG_BLABS(msg.sender, b); _blabs = b; } + /// @inheritdoc IBFactory function collect(IBPool pool) external { require(msg.sender == _blabs, 'ERR_NOT_BLABS'); uint256 collected = pool.balanceOf(address(this)); @@ -35,10 +43,12 @@ contract BFactory is IBFactory { require(xfer, 'ERR_ERC20_FAILED'); } + /// @inheritdoc IBFactory function isBPool(address b) external view returns (bool) { return _isBPool[b]; } + /// @inheritdoc IBFactory function getBLabs() external view returns (address) { return _blabs; } diff --git a/src/contracts/BMath.sol b/src/contracts/BMath.sol index 0bb625db..9e670d56 100644 --- a/src/contracts/BMath.sol +++ b/src/contracts/BMath.sol @@ -6,15 +6,22 @@ import {BNum} from './BNum.sol'; contract BMath is BConst, BNum { /** - * - * calcSpotPrice - * sP = spotPrice - * bI = tokenBalanceIn ( bI / wI ) 1 - * bO = tokenBalanceOut sP = ----------- * ---------- - * wI = tokenWeightIn ( bO / wO ) ( 1 - sF ) - * wO = tokenWeightOut - * sF = swapFee - * + * @notice Calculate the spot price of a token in terms of another one + * @dev The price denomination depends on the decimals of the tokens. + * @dev To obtain the price with 18 decimals the next formula should be applied to the result + * @dev spotPrice = spotPrice ÷ (10^tokenInDecimals) × (10^tokenOutDecimals) + * @param tokenBalanceIn The balance of the input token in the pool + * @param tokenWeightIn The weight of the input token in the pool + * @param tokenBalanceOut The balance of the output token in the pool + * @param tokenWeightOut The weight of the output token in the pool + * @param swapFee The swap fee of the pool + * @dev Formula: + * sP = spotPrice + * bI = tokenBalanceIn ( bI / wI ) 1 + * bO = tokenBalanceOut sP = ----------- * ---------- + * wI = tokenWeightIn ( bO / wO ) ( 1 - sF ) + * wO = tokenWeightOut + * sF = swapFee */ function calcSpotPrice( uint256 tokenBalanceIn, @@ -31,16 +38,21 @@ contract BMath is BConst, BNum { } /** - * - * calcOutGivenIn - * aO = tokenAmountOut - * bO = tokenBalanceOut - * bI = tokenBalanceIn / / bI \ (wI / wO) \ - * aI = tokenAmountIn aO = bO * | 1 - | -------------------------- | ^ | - * wI = tokenWeightIn \ \ ( bI + ( aI * ( 1 - sF )) / / - * wO = tokenWeightOut - * sF = swapFee - * + * @notice Calculate the amount of token out given the amount of token in for a swap + * @param tokenBalanceIn The balance of the input token in the pool + * @param tokenWeightIn The weight of the input token in the pool + * @param tokenBalanceOut The balance of the output token in the pool + * @param tokenWeightOut The weight of the output token in the pool + * @param tokenAmountIn The amount of the input token + * @param swapFee The swap fee of the pool + * @dev Formula: + * aO = tokenAmountOut + * bO = tokenBalanceOut + * bI = tokenBalanceIn / / bI \ (wI / wO) \ + * aI = tokenAmountIn aO = bO * | 1 - | -------------------------- | ^ | + * wI = tokenWeightIn \ \ ( bI + ( aI * ( 1 - sF )) / / + * wO = tokenWeightOut + * sF = swapFee */ function calcOutGivenIn( uint256 tokenBalanceIn, @@ -61,16 +73,21 @@ contract BMath is BConst, BNum { } /** - * - * calcInGivenOut - * aI = tokenAmountIn - * bO = tokenBalanceOut / / bO \ (wO / wI) \ - * bI = tokenBalanceIn bI * | | ------------ | ^ - 1 | - * aO = tokenAmountOut aI = \ \ ( bO - aO ) / / - * wI = tokenWeightIn -------------------------------------------- - * wO = tokenWeightOut ( 1 - sF ) - * sF = swapFee - * + * @notice Calculate the amount of token in given the amount of token out for a swap + * @param tokenBalanceIn The balance of the input token in the pool + * @param tokenWeightIn The weight of the input token in the pool + * @param tokenBalanceOut The balance of the output token in the pool + * @param tokenWeightOut The weight of the output token in the pool + * @param tokenAmountOut The amount of the output token + * @param swapFee The swap fee of the pool + * @dev Formula: + * aI = tokenAmountIn + * bO = tokenBalanceOut / / bO \ (wO / wI) \ + * bI = tokenBalanceIn bI * | | ------------ | ^ - 1 | + * aO = tokenAmountOut aI = \ \ ( bO - aO ) / / + * wI = tokenWeightIn -------------------------------------------- + * wO = tokenWeightOut ( 1 - sF ) + * sF = swapFee */ function calcInGivenOut( uint256 tokenBalanceIn, @@ -91,16 +108,22 @@ contract BMath is BConst, BNum { } /** - * - * calcPoolOutGivenSingleIn - * pAo = poolAmountOut / \ - * tAi = tokenAmountIn /// / // wI \ \\ \ wI \ - * wI = tokenWeightIn //| tAi *| 1 - || 1 - -- | * sF || + tBi \ -- \ - * tW = totalWeight pAo=|| \ \ \\ tW / // | ^ tW | * pS - pS - * tBi = tokenBalanceIn \\ ------------------------------------- / / - * pS = poolSupply \\ tBi / / - * sF = swapFee \ / - * + * @notice Calculate the amount of pool tokens that should be minted, + * given a single token in when joining a pool + * @param tokenBalanceIn The balance of the input token in the pool + * @param tokenWeightIn The weight of the input token in the pool + * @param poolSupply The total supply of the pool tokens + * @param totalWeight The total weight of the pool + * @param tokenAmountIn The amount of the input token + * @param swapFee The swap fee of the pool + * @dev Formula: + * pAo = poolAmountOut / \ + * tAi = tokenAmountIn /// / // wI \ \\ \ wI \ + * wI = tokenWeightIn //| tAi *| 1 - || 1 - -- | * sF || + tBi \ -- \ + * tW = totalWeight pAo=|| \ \ \\ tW / // | ^ tW | * pS - pS + * tBi = tokenBalanceIn \\ ------------------------------------- / / + * pS = poolSupply \\ tBi / / + * sF = swapFee \ / */ function calcPoolOutGivenSingleIn( uint256 tokenBalanceIn, @@ -129,16 +152,21 @@ contract BMath is BConst, BNum { } /** - * - * calcSingleInGivenPoolOut - * tAi = tokenAmountIn //(pS + pAo)\ / 1 \\ - * pS = poolSupply || --------- | ^ | --------- || * bI - bI - * pAo = poolAmountOut \\ pS / \(wI / tW)// - * bI = balanceIn tAi = -------------------------------------------- - * wI = weightIn / wI \ - * tW = totalWeight | 1 - ---- | * sF - * sF = swapFee \ tW / - * + * @notice Given amount of pool tokens out, calculate the amount of tokens in that should be sent + * @param tokenBalanceIn The balance of the input token in the pool + * @param tokenWeightIn The weight of the input token in the pool + * @param poolSupply The current total supply + * @param totalWeight The sum of the weight of all tokens in the pool + * @param poolAmountOut The expected amount of pool tokens + * @param swapFee The swap fee of the pool + * @dev Formula: + * tAi = tokenAmountIn //(pS + pAo)\ / 1 \\ + * pS = poolSupply || --------- | ^ | --------- || * bI - bI + * pAo = poolAmountOut \\ pS / \(wI / tW)// + * bI = balanceIn tAi = -------------------------------------------- + * wI = weightIn / wI \ + * tW = totalWeight | 1 - ---- | * sF + * sF = swapFee \ tW / */ function calcSingleInGivenPoolOut( uint256 tokenBalanceIn, @@ -166,17 +194,22 @@ contract BMath is BConst, BNum { } /** - * - * calcSingleOutGivenPoolIn - * tAo = tokenAmountOut / / \\ - * bO = tokenBalanceOut / // pS - (pAi * (1 - eF)) \ / 1 \ \\ - * pAi = poolAmountIn | bO - || ----------------------- | ^ | --------- | * b0 || - * ps = poolSupply \ \\ pS / \(wO / tW)/ // - * wI = tokenWeightIn tAo = \ \ // - * tW = totalWeight / / wO \ \ - * sF = swapFee * | 1 - | 1 - ---- | * sF | - * eF = exitFee \ \ tW / / - * + * @notice Calculate the amount of token out given the amount of pool tokens in + * @param tokenBalanceOut The balance of the output token in the pool + * @param tokenWeightOut The weight of the output token in the pool + * @param poolSupply The total supply of the pool tokens + * @param totalWeight The total weight of the pool + * @param poolAmountIn The amount of pool tokens + * @param swapFee The swap fee of the pool + * @dev Formula: + * tAo = tokenAmountOut / / \\ + * bO = tokenBalanceOut / // pS - (pAi * (1 - eF)) \ / 1 \ \\ + * pAi = poolAmountIn | bO - || ----------------------- | ^ | --------- | * b0 || + * ps = poolSupply \ \\ pS / \(wO / tW)/ // + * wI = tokenWeightIn tAo = \ \ // + * tW = totalWeight / / wO \ \ + * sF = swapFee * | 1 - | 1 - ---- | * sF | + * eF = exitFee \ \ tW / / */ function calcSingleOutGivenPoolIn( uint256 tokenBalanceOut, @@ -207,17 +240,22 @@ contract BMath is BConst, BNum { } /** - * - * calcPoolInGivenSingleOut - * pAi = poolAmountIn // / tAo \\ / wO \ \ - * bO = tokenBalanceOut // | bO - -------------------------- |\ | ---- | \ - * tAo = tokenAmountOut pS - || \ 1 - ((1 - (tO / tW)) * sF)/ | ^ \ tW / * pS | - * ps = poolSupply \\ -----------------------------------/ / - * wO = tokenWeightOut pAi = \\ bO / / - * tW = totalWeight ------------------------------------------------------------- - * sF = swapFee ( 1 - eF ) - * eF = exitFee - * + * @notice Calculate the amount of pool tokens in given an amount of single token out + * @param tokenBalanceOut The balance of the output token in the pool + * @param tokenWeightOut The weight of the output token in the pool + * @param poolSupply The total supply of the pool tokens + * @param totalWeight The total weight of the pool + * @param tokenAmountOut The amount of the output token + * @param swapFee The swap fee of the pool + * @dev Formula: + * pAi = poolAmountIn // / tAo \\ / wO \ \ + * bO = tokenBalanceOut // | bO - -------------------------- |\ | ---- | \ + * tAo = tokenAmountOut pS - || \ 1 - ((1 - (tO / tW)) * sF)/ | ^ \ tW / * pS | + * ps = poolSupply \\ -----------------------------------/ / + * wO = tokenWeightOut pAi = \\ bO / / + * tW = totalWeight ------------------------------------------------------------- + * sF = swapFee ( 1 - eF ) + * eF = exitFee */ function calcPoolInGivenSingleOut( uint256 tokenBalanceOut, diff --git a/src/contracts/BPool.sol b/src/contracts/BPool.sol index 660118af..62586ba0 100644 --- a/src/contracts/BPool.sol +++ b/src/contracts/BPool.sol @@ -2,31 +2,39 @@ pragma solidity 0.8.25; import {BMath} from './BMath.sol'; - import {BToken} from './BToken.sol'; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {IBPool} from 'interfaces/IBPool.sol'; +/** + * @title BPool + * @notice Pool contract that holds tokens, allows to swap, add and remove liquidity. + */ contract BPool is BToken, BMath, IBPool { + /// @dev True if a call to the contract is in progress, False otherwise bool internal _mutex; - - address internal _factory; // BFactory address to push token exitFee to - address internal _controller; // has CONTROL role - - // `setSwapFee` and `finalize` require CONTROL - // `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN` + /// @dev BFactory address to push token exitFee to + address internal _factory; + /// @dev Has CONTROL role + address internal _controller; + /// @dev Fee for swapping uint256 internal _swapFee; + /// @dev Status of the pool. True if finalized, False otherwise bool internal _finalized; - + /// @dev Array of bound tokens address[] internal _tokens; + /// @dev Metadata for each bound token mapping(address => Record) internal _records; + /// @dev Sum of all token weights uint256 internal _totalWeight; + /// @dev Logs the call data modifier _logs_() { emit LOG_CALL(msg.sig, msg.sender, msg.data); _; } + /// @dev Prevents reentrancy in non-view functions modifier _lock_() { require(!_mutex, 'ERR_REENTRY'); _mutex = true; @@ -34,6 +42,7 @@ contract BPool is BToken, BMath, IBPool { _mutex = false; } + /// @dev Prevents reentrancy in view functions modifier _viewlock_() { require(!_mutex, 'ERR_REENTRY'); _; @@ -46,6 +55,7 @@ contract BPool is BToken, BMath, IBPool { _finalized = false; } + /// @inheritdoc IBPool function setSwapFee(uint256 swapFee) external _logs_ _lock_ { require(!_finalized, 'ERR_IS_FINALIZED'); require(msg.sender == _controller, 'ERR_NOT_CONTROLLER'); @@ -54,11 +64,13 @@ contract BPool is BToken, BMath, IBPool { _swapFee = swapFee; } + /// @inheritdoc IBPool function setController(address manager) external _logs_ _lock_ { require(msg.sender == _controller, 'ERR_NOT_CONTROLLER'); _controller = manager; } + /// @inheritdoc IBPool function finalize() external _logs_ _lock_ { require(msg.sender == _controller, 'ERR_NOT_CONTROLLER'); require(!_finalized, 'ERR_IS_FINALIZED'); @@ -70,6 +82,7 @@ contract BPool is BToken, BMath, IBPool { _pushPoolShare(msg.sender, INIT_POOL_SUPPLY); } + /// @inheritdoc IBPool function bind(address token, uint256 balance, uint256 denorm) external _logs_ _lock_ { require(msg.sender == _controller, 'ERR_NOT_CONTROLLER'); require(!_records[token].bound, 'ERR_IS_BOUND'); @@ -90,6 +103,7 @@ contract BPool is BToken, BMath, IBPool { _pullUnderlying(token, msg.sender, balance); } + /// @inheritdoc IBPool function unbind(address token) external _logs_ _lock_ { require(msg.sender == _controller, 'ERR_NOT_CONTROLLER'); require(_records[token].bound, 'ERR_NOT_BOUND'); @@ -109,6 +123,7 @@ contract BPool is BToken, BMath, IBPool { _pushUnderlying(token, msg.sender, IERC20(token).balanceOf(address(this))); } + /// @inheritdoc IBPool function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn) external _logs_ _lock_ { require(_finalized, 'ERR_NOT_FINALIZED'); @@ -129,6 +144,7 @@ contract BPool is BToken, BMath, IBPool { _pushPoolShare(msg.sender, poolAmountOut); } + /// @inheritdoc IBPool function exitPool(uint256 poolAmountIn, uint256[] calldata minAmountsOut) external _logs_ _lock_ { require(_finalized, 'ERR_NOT_FINALIZED'); @@ -153,6 +169,7 @@ contract BPool is BToken, BMath, IBPool { } } + /// @inheritdoc IBPool function swapExactAmountIn( address tokenIn, uint256 tokenAmountIn, @@ -196,6 +213,7 @@ contract BPool is BToken, BMath, IBPool { return (tokenAmountOut, spotPriceAfter); } + /// @inheritdoc IBPool function swapExactAmountOut( address tokenIn, uint256 maxAmountIn, @@ -239,6 +257,7 @@ contract BPool is BToken, BMath, IBPool { return (tokenAmountIn, spotPriceAfter); } + /// @inheritdoc IBPool function joinswapExternAmountIn( address tokenIn, uint256 tokenAmountIn, @@ -264,6 +283,7 @@ contract BPool is BToken, BMath, IBPool { return poolAmountOut; } + /// @inheritdoc IBPool function joinswapPoolAmountOut( address tokenIn, uint256 poolAmountOut, @@ -291,6 +311,7 @@ contract BPool is BToken, BMath, IBPool { return tokenAmountIn; } + /// @inheritdoc IBPool function exitswapPoolAmountIn( address tokenOut, uint256 poolAmountIn, @@ -320,6 +341,7 @@ contract BPool is BToken, BMath, IBPool { return tokenAmountOut; } + /// @inheritdoc IBPool function exitswapExternAmountOut( address tokenOut, uint256 tokenAmountOut, @@ -349,6 +371,7 @@ contract BPool is BToken, BMath, IBPool { return poolAmountIn; } + /// @inheritdoc IBPool function getSpotPrice(address tokenIn, address tokenOut) external view _viewlock_ returns (uint256 spotPrice) { require(_records[tokenIn].bound, 'ERR_NOT_BOUND'); require(_records[tokenOut].bound, 'ERR_NOT_BOUND'); @@ -363,6 +386,7 @@ contract BPool is BToken, BMath, IBPool { ); } + /// @inheritdoc IBPool function getSpotPriceSansFee(address tokenIn, address tokenOut) external view _viewlock_ returns (uint256 spotPrice) { require(_records[tokenIn].bound, 'ERR_NOT_BOUND'); require(_records[tokenOut].bound, 'ERR_NOT_BOUND'); @@ -377,81 +401,118 @@ contract BPool is BToken, BMath, IBPool { ); } + /// @inheritdoc IBPool function isFinalized() external view returns (bool) { return _finalized; } + /// @inheritdoc IBPool function isBound(address t) external view returns (bool) { return _records[t].bound; } + /// @inheritdoc IBPool function getNumTokens() external view returns (uint256) { return _tokens.length; } + /// @inheritdoc IBPool function getCurrentTokens() external view _viewlock_ returns (address[] memory tokens) { return _tokens; } + /// @inheritdoc IBPool function getFinalTokens() external view _viewlock_ returns (address[] memory tokens) { require(_finalized, 'ERR_NOT_FINALIZED'); return _tokens; } + /// @inheritdoc IBPool function getDenormalizedWeight(address token) external view _viewlock_ returns (uint256) { require(_records[token].bound, 'ERR_NOT_BOUND'); return _records[token].denorm; } + /// @inheritdoc IBPool function getTotalDenormalizedWeight() external view _viewlock_ returns (uint256) { return _totalWeight; } + /// @inheritdoc IBPool function getNormalizedWeight(address token) external view _viewlock_ returns (uint256) { require(_records[token].bound, 'ERR_NOT_BOUND'); uint256 denorm = _records[token].denorm; return bdiv(denorm, _totalWeight); } + /// @inheritdoc IBPool function getBalance(address token) external view _viewlock_ returns (uint256) { require(_records[token].bound, 'ERR_NOT_BOUND'); return IERC20(token).balanceOf(address(this)); } + /// @inheritdoc IBPool function getSwapFee() external view _viewlock_ returns (uint256) { return _swapFee; } + /// @inheritdoc IBPool function getController() external view _viewlock_ returns (address) { return _controller; } - // == - // 'Underlying' token-manipulation functions make external calls but are NOT locked - // You must `_lock_` or otherwise ensure reentry-safety - + /** + * @dev Pulls tokens from the sender. Tokens needs to be approved first. Calls are not locked. + * @param erc20 address of the token to pull + * @param from address to pull the tokens from + * @param amount amount of tokens to pull + */ function _pullUnderlying(address erc20, address from, uint256 amount) internal virtual { bool xfer = IERC20(erc20).transferFrom(from, address(this), amount); require(xfer, 'ERR_ERC20_FALSE'); } + /** + * @dev Pushes tokens to the receiver. Calls are not locked. + * @param erc20 address of the token to push + * @param to address to push the tokens to + * @param amount amount of tokens to push + */ function _pushUnderlying(address erc20, address to, uint256 amount) internal virtual { bool xfer = IERC20(erc20).transfer(to, amount); require(xfer, 'ERR_ERC20_FALSE'); } + /** + * @dev Pulls pool tokens from the sender. + * @param from address to pull the pool tokens from + * @param amount amount of pool tokens to pull + */ function _pullPoolShare(address from, uint256 amount) internal { _pull(from, amount); } + /** + * @dev Pushes pool tokens to the receiver. + * @param to address to push the pool tokens to + * @param amount amount of pool tokens to push + */ function _pushPoolShare(address to, uint256 amount) internal { _push(to, amount); } + /** + * @dev Mints an amount of pool tokens. + * @param amount amount of pool tokens to mint + */ function _mintPoolShare(uint256 amount) internal { _mint(address(this), amount); } + /** + * @dev Burns an amount of pool tokens. + * @param amount amount of pool tokens to burn + */ function _burnPoolShare(uint256 amount) internal { _burn(address(this), amount); } diff --git a/src/contracts/BToken.sol b/src/contracts/BToken.sol index 884c335e..0aaefc48 100644 --- a/src/contracts/BToken.sol +++ b/src/contracts/BToken.sol @@ -1,18 +1,28 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.25; -import {BNum} from './BNum.sol'; - import {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol'; -contract BToken is BNum, ERC20 { +contract BToken is ERC20 { constructor() ERC20('Balancer Pool Token', 'BPT') {} + /** + * @notice Increase the allowance of the spender. + * @param dst The address which will spend the funds. + * @param amt The amount of tokens to increase the allowance by. + * @return True if the operation is successful. + */ function increaseApproval(address dst, uint256 amt) external returns (bool) { _approve(msg.sender, dst, allowance(msg.sender, dst) + amt); return true; } + /** + * @notice Decrease the allowance of the spender. + * @param dst The address which will spend the funds. + * @param amt The amount of tokens to decrease the allowance by. + * @return True if the operation is successful. + */ function decreaseApproval(address dst, uint256 amt) external returns (bool) { uint256 oldValue = allowance(msg.sender, dst); if (amt > oldValue) { @@ -23,10 +33,20 @@ contract BToken is BNum, ERC20 { return true; } + /** + * @notice Transfer tokens from one this contract to another. + * @param to The address which you want to transfer to. + * @param amt The amount of tokens to be transferred. + */ function _push(address to, uint256 amt) internal virtual { _transfer(address(this), to, amt); } + /** + * @notice Pull tokens from another address to this contract. + * @param from The address which you want to transfer from. + * @param amt The amount of tokens to be transferred. + */ function _pull(address from, uint256 amt) internal virtual { _transfer(from, address(this), amt); } diff --git a/src/interfaces/IBFactory.sol b/src/interfaces/IBFactory.sol index d07a3e6d..dc0999dd 100644 --- a/src/interfaces/IBFactory.sol +++ b/src/interfaces/IBFactory.sol @@ -4,17 +4,48 @@ pragma solidity 0.8.25; import {IBPool} from 'interfaces/IBPool.sol'; interface IBFactory { + /** + * @notice Emitted when creating a new pool + * @param caller The caller of the function that will be set as the controller + * @param pool The address of the new pool + */ event LOG_NEW_POOL(address indexed caller, address indexed pool); + /** + * @notice Emitted when setting the BLabs address + * @param caller The caller of the set BLabs function + * @param bLabs The address of the new BLabs + */ event LOG_BLABS(address indexed caller, address indexed bLabs); + /** + * @notice Creates a new BPool, assigning the caller as the pool controller + * @return pool The new BPool + */ function newBPool() external returns (IBPool pool); + /** + * @notice Sets the BLabs address in the factory + * @param b The new BLabs address + */ function setBLabs(address b) external; + /** + * @notice Collects the fees of a pool and transfers it to BLabs address + * @param pool The address of the pool to collect fees from + */ function collect(IBPool pool) external; + /** + * @notice Checks if an address is a BPool created from this factory + * @param b The address to check + * @return isBPool True if the address is a BPool, False otherwise + */ function isBPool(address b) external view returns (bool isBPool); + /** + * @notice Gets the BLabs address + * @return bLabs The address of the BLabs + */ function getBLabs() external view returns (address bLabs); } diff --git a/src/interfaces/IBPool.sol b/src/interfaces/IBPool.sol index c111948e..c02f1a98 100644 --- a/src/interfaces/IBPool.sol +++ b/src/interfaces/IBPool.sol @@ -4,12 +4,26 @@ pragma solidity 0.8.25; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; interface IBPool is IERC20 { + /** + * @dev Struct for token records. + * @param bound If token is bound to pool. + * @param index Internal index of token array. + * @param denorm Denormalized weight of token. + */ struct Record { - bool bound; // is token bound to pool - uint256 index; // internal - uint256 denorm; // denormalized weight + bool bound; + uint256 index; + uint256 denorm; } + /** + * @notice Emitted when a swap is executed + * @param caller The caller of the swap function + * @param tokenIn The address of the token being swapped in + * @param tokenOut The address of the token being swapped out + * @param tokenAmountIn The amount of tokenIn being swapped in + * @param tokenAmountOut The amount of tokenOut being swapped out + */ event LOG_SWAP( address indexed caller, address indexed tokenIn, @@ -18,26 +32,85 @@ interface IBPool is IERC20 { uint256 tokenAmountOut ); + /** + * @notice Emitted when a join operation is executed + * @param caller The caller of the function + * @param tokenIn The address of the token being sent to the pool + * @param tokenAmountIn The balance of the token being sent to the pool + */ event LOG_JOIN(address indexed caller, address indexed tokenIn, uint256 tokenAmountIn); + /** + * @notice Emitted when a token amount is removed from the pool + * @param caller The caller of the function + * @param tokenOut The address of the token being removed from the pool + * @param tokenAmountOut The amount of the token being removed from the pool + */ event LOG_EXIT(address indexed caller, address indexed tokenOut, uint256 tokenAmountOut); + /** + * @notice Emitted when a call is executed on the pool + * @param sig The signature of the function selector being called + * @param caller The caller of the function + * @param data The complete data of the call + */ event LOG_CALL(bytes4 indexed sig, address indexed caller, bytes data) anonymous; + /** + * @notice Sets the new swap fee + * @param swapFee The new swap fee + */ function setSwapFee(uint256 swapFee) external; + /** + * @notice Sets the new controller + * @param manager The new controller + */ function setController(address manager) external; + /** + * @notice Finalize the pool, removing the restrictions on the pool + */ function finalize() external; + /** + * @notice Binds a token to the pool + * @param token The address of the token to bind + * @param balance The balance of the token to bind + * @param denorm The denormalized weight of the token to bind + */ function bind(address token, uint256 balance, uint256 denorm) external; + /** + * @notice Unbinds a token from the pool + * @param token The address of the token to unbind + */ function unbind(address token) external; + /** + * @notice Joins a pool, providing each token in the pool with a proportional amount + * @param poolAmountOut The amount of pool tokens to mint + * @param maxAmountsIn The maximum amount of tokens to send to the pool + */ function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn) external; + /** + * @notice Exits a pool, receiving each token in the pool with a proportional amount + * @param poolAmountIn The amount of pool tokens to burn + * @param minAmountsOut The minimum amount of tokens to receive from the pool + */ function exitPool(uint256 poolAmountIn, uint256[] calldata minAmountsOut) external; + /** + * @notice Swaps an exact amount of tokens in for an amount of tokens out + * @param tokenIn The address of the token to swap in + * @param tokenAmountIn The amount of token to swap in + * @param tokenOut The address of the token to swap out + * @param minAmountOut The minimum amount of token to receive from the swap + * @param maxPrice The maximum price to pay for the swap + * @return tokenAmountOut The amount of token swapped out + * @return spotPriceAfter The spot price after the swap + */ function swapExactAmountIn( address tokenIn, uint256 tokenAmountIn, @@ -46,6 +119,16 @@ interface IBPool is IERC20 { uint256 maxPrice ) external returns (uint256 tokenAmountOut, uint256 spotPriceAfter); + /** + * @notice Swaps as many tokens in as possible for an exact amount of tokens out + * @param tokenIn The address of the token to swap in + * @param maxAmountIn The maximum amount of token to swap in + * @param tokenOut The address of the token to swap out + * @param tokenAmountOut The amount of token to swap out + * @param maxPrice The maximum price to pay for the swap + * @return tokenAmountIn The amount of token swapped in + * @return spotPriceAfter The spot price after the swap + */ function swapExactAmountOut( address tokenIn, uint256 maxAmountIn, @@ -54,53 +137,141 @@ interface IBPool is IERC20 { uint256 maxPrice ) external returns (uint256 tokenAmountIn, uint256 spotPriceAfter); + /** + * @notice Joins a pool providing a single token in, specifying the exact amount of token given + * @param tokenIn The address of the token to swap in and join + * @param tokenAmountIn The amount of token to join + * @param minPoolAmountOut The minimum amount of pool token to receive + * @return poolAmountOut The amount of pool token received + */ function joinswapExternAmountIn( address tokenIn, uint256 tokenAmountIn, uint256 minPoolAmountOut ) external returns (uint256 poolAmountOut); + /** + * @notice Joins a pool providing a single token in, specifying the exact amount of pool tokens received + * @param tokenIn The address of the token to swap in and join + * @param poolAmountOut The amount of pool token to receive + * @param maxAmountIn The maximum amount of token to introduce to the pool + * @return tokenAmountIn The amount of token in introduced + */ function joinswapPoolAmountOut( address tokenIn, uint256 poolAmountOut, uint256 maxAmountIn ) external returns (uint256 tokenAmountIn); + /** + * @notice Exits a pool providing a specific amount of pool tokens in, and receiving only a single token + * @param tokenOut The address of the token to swap out and exit + * @param poolAmountIn The amount of pool token to burn + * @param minAmountOut The minimum amount of token to receive + * @return tokenAmountOut The amount of token received + */ function exitswapPoolAmountIn( address tokenOut, uint256 poolAmountIn, uint256 minAmountOut ) external returns (uint256 tokenAmountOut); + /** + * @notice Exits a pool expecting a specific amount of token out, and providing pool token + * @param tokenOut The address of the token to swap out and exit + * @param tokenAmountOut The amount of token to receive + * @param maxPoolAmountIn The maximum amount of pool token to burn + * @return poolAmountIn The amount of pool token burned + */ function exitswapExternAmountOut( address tokenOut, uint256 tokenAmountOut, uint256 maxPoolAmountIn ) external returns (uint256 poolAmountIn); + /** + * @notice Gets the spot price of tokenIn in terms of tokenOut + * @param tokenIn The address of the token to swap in + * @param tokenOut The address of the token to swap out + * @return spotPrice The spot price of the swap + */ function getSpotPrice(address tokenIn, address tokenOut) external view returns (uint256 spotPrice); + /** + * @notice Gets the spot price of tokenIn in terms of tokenOut without the fee + * @param tokenIn The address of the token to swap in + * @param tokenOut The address of the token to swap out + * @return spotPrice The spot price of the swap without the fee + */ function getSpotPriceSansFee(address tokenIn, address tokenOut) external view returns (uint256 spotPrice); + /** + * @notice Gets the finalized status of the pool + * @return isFinalized True if the pool is finalized, False otherwise + */ function isFinalized() external view returns (bool isFinalized); + /** + * @notice Gets the bound status of a token + * @param t The address of the token to check + * @return isBound True if the token is bound, False otherwise + */ function isBound(address t) external view returns (bool isBound); + /** + * @notice Gets the number of tokens in the pool + * @return numTokens The number of tokens in the pool + */ function getNumTokens() external view returns (uint256 numTokens); + /** + * @notice Gets the current array of tokens in the pool, while the pool is not finalized + * @return tokens The array of tokens in the pool + */ function getCurrentTokens() external view returns (address[] memory tokens); + /** + * @notice Gets the final array of tokens in the pool, after finalization + * @return tokens The array of tokens in the pool + */ function getFinalTokens() external view returns (address[] memory tokens); + /** + * @notice Gets the denormalized weight of a token in the pool + * @param token The address of the token to check + * @return denormWeight The denormalized weight of the token in the pool + */ function getDenormalizedWeight(address token) external view returns (uint256 denormWeight); + /** + * @notice Gets the total denormalized weight of the pool + * @return totalDenormWeight The total denormalized weight of the pool + */ function getTotalDenormalizedWeight() external view returns (uint256 totalDenormWeight); + /** + * @notice Gets the normalized weight of a token in the pool + * @param token The address of the token to check + * @return normWeight The normalized weight of the token in the pool + */ function getNormalizedWeight(address token) external view returns (uint256 normWeight); + /** + * @notice Gets the Pool's ERC20 balance of a token + * @param token The address of the token to check + * @return balance The Pool's ERC20 balance of the token + */ function getBalance(address token) external view returns (uint256 balance); + /** + * @notice Gets the swap fee of the pool + * @return swapFee The swap fee of the pool + */ function getSwapFee() external view returns (uint256 swapFee); + /** + * @notice Gets the controller of the pool + * @return controller The controller of the pool + */ function getController() external view returns (address controller); }