Last active
June 21, 2022 08:33
-
-
Save rayeaster/038e848fbc79120fbf422972af96b217 to your computer and use it in GitHub Desktop.
Balancer Pool Token pricing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.8.10; | |
pragma experimental ABIEncoderV2; | |
/** | |
* @dev Wrappers over Solidity's arithmetic operations with added overflow | |
* checks. | |
* | |
* Arithmetic operations in Solidity wrap on overflow. This can easily result | |
* in bugs, because programmers usually assume that an overflow raises an | |
* error, which is the standard behavior in high level programming languages. | |
* `SafeMath` restores this intuition by reverting the transaction when an | |
* operation overflows. | |
* | |
* Using this library instead of the unchecked operations eliminates an entire | |
* class of bugs, so it's recommended to use it always. | |
*/ | |
library SafeMath { | |
/** | |
* @dev Returns the addition of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `+` operator. | |
* | |
* Requirements: | |
* | |
* - Addition cannot overflow. | |
*/ | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
require(c >= a, "SafeMath: addition overflow"); | |
return c; | |
} | |
/** | |
* @dev Returns the subtraction of two unsigned integers, reverting on | |
* overflow (when the result is negative). | |
* | |
* Counterpart to Solidity's `-` operator. | |
* | |
* Requirements: | |
* | |
* - Subtraction cannot overflow. | |
*/ | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
return sub(a, b, "SafeMath: subtraction overflow"); | |
} | |
/** | |
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on | |
* overflow (when the result is negative). | |
* | |
* Counterpart to Solidity's `-` operator. | |
* | |
* Requirements: | |
* | |
* - Subtraction cannot overflow. | |
*/ | |
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b <= a, errorMessage); | |
uint256 c = a - b; | |
return c; | |
} | |
/** | |
* @dev Returns the multiplication of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `*` operator. | |
* | |
* Requirements: | |
* | |
* - Multiplication cannot overflow. | |
*/ | |
function mul(uint256 a, uint256 b) internal pure returns (uint256) { | |
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the | |
// benefit is lost if 'b' is also tested. | |
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 | |
if (a == 0) { | |
return 0; | |
} | |
uint256 c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
/** | |
* @dev Returns the integer division of two unsigned integers. Reverts on | |
* division by zero. The result is rounded towards zero. | |
* | |
* Counterpart to Solidity's `/` operator. Note: this function uses a | |
* `revert` opcode (which leaves remaining gas untouched) while Solidity | |
* uses an invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* | |
* - The divisor cannot be zero. | |
*/ | |
function div(uint256 a, uint256 b) internal pure returns (uint256) { | |
return div(a, b, "SafeMath: division by zero"); | |
} | |
/** | |
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on | |
* division by zero. The result is rounded towards zero. | |
* | |
* Counterpart to Solidity's `/` operator. Note: this function uses a | |
* `revert` opcode (which leaves remaining gas untouched) while Solidity | |
* uses an invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* | |
* - The divisor cannot be zero. | |
*/ | |
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b > 0, errorMessage); | |
uint256 c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
/** | |
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), | |
* Reverts when dividing by zero. | |
* | |
* Counterpart to Solidity's `%` operator. This function uses a `revert` | |
* opcode (which leaves remaining gas untouched) while Solidity uses an | |
* invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* | |
* - The divisor cannot be zero. | |
*/ | |
function mod(uint256 a, uint256 b) internal pure returns (uint256) { | |
return mod(a, b, "SafeMath: modulo by zero"); | |
} | |
/** | |
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), | |
* Reverts with custom message when dividing by zero. | |
* | |
* Counterpart to Solidity's `%` operator. This function uses a `revert` | |
* opcode (which leaves remaining gas untouched) while Solidity uses an | |
* invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* | |
* - The divisor cannot be zero. | |
*/ | |
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b != 0, errorMessage); | |
return a % b; | |
} | |
} | |
/* | |
* @dev Provides information about the current execution context, including the | |
* sender of the transaction and its data. While these are generally available | |
* via msg.sender and msg.data, they should not be accessed in such a direct | |
* manner, since when dealing with GSN meta-transactions the account sending and | |
* paying for execution may not be the actual sender (as far as an application | |
* is concerned). | |
* | |
* This contract is only required for intermediate, library-like contracts. | |
*/ | |
abstract contract Context { | |
function _msgSender() internal view virtual returns (address) { | |
return msg.sender; | |
} | |
function _msgData() internal view virtual returns (bytes memory) { | |
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 | |
return msg.data; | |
} | |
} | |
/** | |
* @dev Interface of the ERC20 standard as defined in the EIP. | |
*/ | |
interface IERC20 { | |
/** | |
* @dev Returns the amount of tokens in existence. | |
*/ | |
function totalSupply() external view returns (uint256); | |
/** | |
* @dev Returns the amount of tokens owned by `account`. | |
*/ | |
function balanceOf(address account) external view returns (uint256); | |
/** | |
* @dev Moves `amount` tokens from the caller's account to `recipient`. | |
* | |
* Returns a boolean value indicating whether the operation succeeded. | |
* | |
* Emits a {Transfer} event. | |
*/ | |
function transfer(address recipient, uint256 amount) external returns (bool); | |
/** | |
* @dev Returns the remaining number of tokens that `spender` will be | |
* allowed to spend on behalf of `owner` through {transferFrom}. This is | |
* zero by default. | |
* | |
* This value changes when {approve} or {transferFrom} are called. | |
*/ | |
function allowance(address owner, address spender) external view returns (uint256); | |
/** | |
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens. | |
* | |
* Returns a boolean value indicating whether the operation succeeded. | |
* | |
* IMPORTANT: Beware that changing an allowance with this method brings the risk | |
* that someone may use both the old and the new allowance by unfortunate | |
* transaction ordering. One possible solution to mitigate this race | |
* condition is to first reduce the spender's allowance to 0 and set the | |
* desired value afterwards: | |
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 | |
* | |
* Emits an {Approval} event. | |
*/ | |
function approve(address spender, uint256 amount) external returns (bool); | |
/** | |
* @dev Moves `amount` tokens from `sender` to `recipient` using the | |
* allowance mechanism. `amount` is then deducted from the caller's | |
* allowance. | |
* | |
* Returns a boolean value indicating whether the operation succeeded. | |
* | |
* Emits a {Transfer} event. | |
*/ | |
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); | |
/** | |
* @dev Emitted when `value` tokens are moved from one account (`from`) to | |
* another (`to`). | |
* | |
* Note that `value` may be zero. | |
*/ | |
event Transfer(address indexed from, address indexed to, uint256 value); | |
/** | |
* @dev Emitted when the allowance of a `spender` for an `owner` is set by | |
* a call to {approve}. `value` is the new allowance. | |
*/ | |
event Approval(address indexed owner, address indexed spender, uint256 value); | |
} | |
/** | |
* @dev Collection of functions related to the address type | |
*/ | |
library Address { | |
/** | |
* @dev Returns true if `account` is a contract. | |
* | |
* [IMPORTANT] | |
* ==== | |
* It is unsafe to assume that an address for which this function returns | |
* false is an externally-owned account (EOA) and not a contract. | |
* | |
* Among others, `isContract` will return false for the following | |
* types of addresses: | |
* | |
* - an externally-owned account | |
* - a contract in construction | |
* - an address where a contract will be created | |
* - an address where a contract lived, but was destroyed | |
* ==== | |
*/ | |
function isContract(address account) internal view returns (bool) { | |
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts | |
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned | |
// for accounts without code, i.e. `keccak256('')` | |
bytes32 codehash; | |
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; | |
// solhint-disable-next-line no-inline-assembly | |
assembly { codehash := extcodehash(account) } | |
return (codehash != accountHash && codehash != 0x0); | |
} | |
/** | |
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to | |
* `recipient`, forwarding all available gas and reverting on errors. | |
* | |
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost | |
* of certain opcodes, possibly making contracts go over the 2300 gas limit | |
* imposed by `transfer`, making them unable to receive funds via | |
* `transfer`. {sendValue} removes this limitation. | |
* | |
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. | |
* | |
* IMPORTANT: because control is transferred to `recipient`, care must be | |
* taken to not create reentrancy vulnerabilities. Consider using | |
* {ReentrancyGuard} or the | |
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. | |
*/ | |
function sendValue(address payable recipient, uint256 amount) internal { | |
require(address(this).balance >= amount, "Address: insufficient balance"); | |
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value | |
(bool success, ) = recipient.call{ value: amount }(""); | |
require(success, "Address: unable to send value, recipient may have reverted"); | |
} | |
/** | |
* @dev Performs a Solidity function call using a low level `call`. A | |
* plain`call` is an unsafe replacement for a function call: use this | |
* function instead. | |
* | |
* If `target` reverts with a revert reason, it is bubbled up by this | |
* function (like regular Solidity function calls). | |
* | |
* Returns the raw returned data. To convert to the expected return value, | |
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. | |
* | |
* Requirements: | |
* | |
* - `target` must be a contract. | |
* - calling `target` with `data` must not revert. | |
* | |
* _Available since v3.1._ | |
*/ | |
function functionCall(address target, bytes memory data) internal returns (bytes memory) { | |
return functionCall(target, data, "Address: low-level call failed"); | |
} | |
/** | |
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with | |
* `errorMessage` as a fallback revert reason when `target` reverts. | |
* | |
* _Available since v3.1._ | |
*/ | |
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { | |
return _functionCallWithValue(target, data, 0, errorMessage); | |
} | |
/** | |
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], | |
* but also transferring `value` wei to `target`. | |
* | |
* Requirements: | |
* | |
* - the calling contract must have an ETH balance of at least `value`. | |
* - the called Solidity function must be `payable`. | |
* | |
* _Available since v3.1._ | |
*/ | |
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { | |
return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); | |
} | |
/** | |
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but | |
* with `errorMessage` as a fallback revert reason when `target` reverts. | |
* | |
* _Available since v3.1._ | |
*/ | |
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { | |
require(address(this).balance >= value, "Address: insufficient balance for call"); | |
return _functionCallWithValue(target, data, value, errorMessage); | |
} | |
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { | |
require(isContract(target), "Address: call to non-contract"); | |
// solhint-disable-next-line avoid-low-level-calls | |
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data); | |
if (success) { | |
return returndata; | |
} else { | |
// Look for revert reason and bubble it up if present | |
if (returndata.length > 0) { | |
// The easiest way to bubble the revert reason is using memory via assembly | |
// solhint-disable-next-line no-inline-assembly | |
assembly { | |
let returndata_size := mload(returndata) | |
revert(add(32, returndata), returndata_size) | |
} | |
} else { | |
revert(errorMessage); | |
} | |
} | |
} | |
} | |
/** | |
* @dev Implementation of the {IERC20} interface. | |
* | |
* This implementation is agnostic to the way tokens are created. This means | |
* that a supply mechanism has to be added in a derived contract using {_mint}. | |
* For a generic mechanism see {ERC20PresetMinterPauser}. | |
* | |
* TIP: For a detailed writeup see our guide | |
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How | |
* to implement supply mechanisms]. | |
* | |
* We have followed general OpenZeppelin guidelines: functions revert instead | |
* of returning `false` on failure. This behavior is nonetheless conventional | |
* and does not conflict with the expectations of ERC20 applications. | |
* | |
* Additionally, an {Approval} event is emitted on calls to {transferFrom}. | |
* This allows applications to reconstruct the allowance for all accounts just | |
* by listening to said events. Other implementations of the EIP may not emit | |
* these events, as it isn't required by the specification. | |
* | |
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance} | |
* functions have been added to mitigate the well-known issues around setting | |
* allowances. See {IERC20-approve}. | |
*/ | |
contract ERC20 is Context, IERC20 { | |
using SafeMath for uint256; | |
using Address for address; | |
mapping (address => uint256) private _balances; | |
mapping (address => mapping (address => uint256)) private _allowances; | |
uint256 private _totalSupply; | |
string private _name; | |
string private _symbol; | |
uint8 private _decimals; | |
/** | |
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with | |
* a default value of 18. | |
* | |
* To select a different value for {decimals}, use {_setupDecimals}. | |
* | |
* All three of these values are immutable: they can only be set once during | |
* construction. | |
*/ | |
constructor (string memory name, string memory symbol) public { | |
_name = name; | |
_symbol = symbol; | |
_decimals = 18; | |
} | |
/** | |
* @dev Returns the name of the token. | |
*/ | |
function name() public view returns (string memory) { | |
return _name; | |
} | |
/** | |
* @dev Returns the symbol of the token, usually a shorter version of the | |
* name. | |
*/ | |
function symbol() public view returns (string memory) { | |
return _symbol; | |
} | |
/** | |
* @dev Returns the number of decimals used to get its user representation. | |
* For example, if `decimals` equals `2`, a balance of `505` tokens should | |
* be displayed to a user as `5,05` (`505 / 10 ** 2`). | |
* | |
* Tokens usually opt for a value of 18, imitating the relationship between | |
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is | |
* called. | |
* | |
* NOTE: This information is only used for _display_ purposes: it in | |
* no way affects any of the arithmetic of the contract, including | |
* {IERC20-balanceOf} and {IERC20-transfer}. | |
*/ | |
function decimals() public view returns (uint8) { | |
return _decimals; | |
} | |
/** | |
* @dev See {IERC20-totalSupply}. | |
*/ | |
function totalSupply() public view override returns (uint256) { | |
return _totalSupply; | |
} | |
/** | |
* @dev See {IERC20-balanceOf}. | |
*/ | |
function balanceOf(address account) public view override returns (uint256) { | |
return _balances[account]; | |
} | |
/** | |
* @dev See {IERC20-transfer}. | |
* | |
* Requirements: | |
* | |
* - `recipient` cannot be the zero address. | |
* - the caller must have a balance of at least `amount`. | |
*/ | |
function transfer(address recipient, uint256 amount) public virtual override returns (bool) { | |
_transfer(_msgSender(), recipient, amount); | |
return true; | |
} | |
/** | |
* @dev See {IERC20-allowance}. | |
*/ | |
function allowance(address owner, address spender) public view virtual override returns (uint256) { | |
return _allowances[owner][spender]; | |
} | |
/** | |
* @dev See {IERC20-approve}. | |
* | |
* Requirements: | |
* | |
* - `spender` cannot be the zero address. | |
*/ | |
function approve(address spender, uint256 amount) public virtual override returns (bool) { | |
_approve(_msgSender(), spender, amount); | |
return true; | |
} | |
/** | |
* @dev See {IERC20-transferFrom}. | |
* | |
* Emits an {Approval} event indicating the updated allowance. This is not | |
* required by the EIP. See the note at the beginning of {ERC20}; | |
* | |
* Requirements: | |
* - `sender` and `recipient` cannot be the zero address. | |
* - `sender` must have a balance of at least `amount`. | |
* - the caller must have allowance for ``sender``'s tokens of at least | |
* `amount`. | |
*/ | |
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { | |
_transfer(sender, recipient, amount); | |
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); | |
return true; | |
} | |
/** | |
* @dev Atomically increases the allowance granted to `spender` by the caller. | |
* | |
* This is an alternative to {approve} that can be used as a mitigation for | |
* problems described in {IERC20-approve}. | |
* | |
* Emits an {Approval} event indicating the updated allowance. | |
* | |
* Requirements: | |
* | |
* - `spender` cannot be the zero address. | |
*/ | |
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { | |
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); | |
return true; | |
} | |
/** | |
* @dev Atomically decreases the allowance granted to `spender` by the caller. | |
* | |
* This is an alternative to {approve} that can be used as a mitigation for | |
* problems described in {IERC20-approve}. | |
* | |
* Emits an {Approval} event indicating the updated allowance. | |
* | |
* Requirements: | |
* | |
* - `spender` cannot be the zero address. | |
* - `spender` must have allowance for the caller of at least | |
* `subtractedValue`. | |
*/ | |
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { | |
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); | |
return true; | |
} | |
/** | |
* @dev Moves tokens `amount` from `sender` to `recipient`. | |
* | |
* This is internal function is equivalent to {transfer}, and can be used to | |
* e.g. implement automatic token fees, slashing mechanisms, etc. | |
* | |
* Emits a {Transfer} event. | |
* | |
* Requirements: | |
* | |
* - `sender` cannot be the zero address. | |
* - `recipient` cannot be the zero address. | |
* - `sender` must have a balance of at least `amount`. | |
*/ | |
function _transfer(address sender, address recipient, uint256 amount) internal virtual { | |
require(sender != address(0), "ERC20: transfer from the zero address"); | |
require(recipient != address(0), "ERC20: transfer to the zero address"); | |
_beforeTokenTransfer(sender, recipient, amount); | |
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); | |
_balances[recipient] = _balances[recipient].add(amount); | |
emit Transfer(sender, recipient, amount); | |
} | |
/** @dev Creates `amount` tokens and assigns them to `account`, increasing | |
* the total supply. | |
* | |
* Emits a {Transfer} event with `from` set to the zero address. | |
* | |
* Requirements | |
* | |
* - `to` cannot be the zero address. | |
*/ | |
function _mint(address account, uint256 amount) internal virtual { | |
require(account != address(0), "ERC20: mint to the zero address"); | |
_beforeTokenTransfer(address(0), account, amount); | |
_totalSupply = _totalSupply.add(amount); | |
_balances[account] = _balances[account].add(amount); | |
emit Transfer(address(0), account, amount); | |
} | |
/** | |
* @dev Destroys `amount` tokens from `account`, reducing the | |
* total supply. | |
* | |
* Emits a {Transfer} event with `to` set to the zero address. | |
* | |
* Requirements | |
* | |
* - `account` cannot be the zero address. | |
* - `account` must have at least `amount` tokens. | |
*/ | |
function _burn(address account, uint256 amount) internal virtual { | |
require(account != address(0), "ERC20: burn from the zero address"); | |
_beforeTokenTransfer(account, address(0), amount); | |
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); | |
_totalSupply = _totalSupply.sub(amount); | |
emit Transfer(account, address(0), amount); | |
} | |
/** | |
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. | |
* | |
* This internal function is equivalent to `approve`, and can be used to | |
* e.g. set automatic allowances for certain subsystems, etc. | |
* | |
* Emits an {Approval} event. | |
* | |
* Requirements: | |
* | |
* - `owner` cannot be the zero address. | |
* - `spender` cannot be the zero address. | |
*/ | |
function _approve(address owner, address spender, uint256 amount) internal virtual { | |
require(owner != address(0), "ERC20: approve from the zero address"); | |
require(spender != address(0), "ERC20: approve to the zero address"); | |
_allowances[owner][spender] = amount; | |
emit Approval(owner, spender, amount); | |
} | |
/** | |
* @dev Sets {decimals} to a value other than the default one of 18. | |
* | |
* WARNING: This function should only be called from the constructor. Most | |
* applications that interact with token contracts will not expect | |
* {decimals} to ever change, and may work incorrectly if it does. | |
*/ | |
function _setupDecimals(uint8 decimals_) internal { | |
_decimals = decimals_; | |
} | |
/** | |
* @dev Hook that is called before any transfer of tokens. This includes | |
* minting and burning. | |
* | |
* Calling conditions: | |
* | |
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens | |
* will be to transferred to `to`. | |
* - when `from` is zero, `amount` tokens will be minted for `to`. | |
* - when `to` is zero, `amount` of ``from``'s tokens will be burned. | |
* - `from` and `to` are never both zero. | |
* | |
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. | |
*/ | |
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } | |
} | |
/** | |
* @title SafeERC20 | |
* @dev Wrappers around ERC20 operations that throw on failure (when the token | |
* contract returns false). Tokens that return no value (and instead revert or | |
* throw on failure) are also supported, non-reverting calls are assumed to be | |
* successful. | |
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, | |
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc. | |
*/ | |
library SafeERC20 { | |
using SafeMath for uint256; | |
using Address for address; | |
function safeTransfer(IERC20 token, address to, uint256 value) internal { | |
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); | |
} | |
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { | |
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); | |
} | |
/** | |
* @dev Deprecated. This function has issues similar to the ones found in | |
* {IERC20-approve}, and its usage is discouraged. | |
* | |
* Whenever possible, use {safeIncreaseAllowance} and | |
* {safeDecreaseAllowance} instead. | |
*/ | |
function safeApprove(IERC20 token, address spender, uint256 value) internal { | |
// safeApprove should only be called when setting an initial allowance, | |
// or when resetting it to zero. To increase and decrease it, use | |
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance' | |
// solhint-disable-next-line max-line-length | |
require((value == 0) || (token.allowance(address(this), spender) == 0), | |
"SafeERC20: approve from non-zero to non-zero allowance" | |
); | |
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); | |
} | |
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { | |
uint256 newAllowance = token.allowance(address(this), spender).add(value); | |
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); | |
} | |
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { | |
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); | |
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); | |
} | |
/** | |
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement | |
* on the return value: the return value is optional (but if data is returned, it must not be false). | |
* @param token The token targeted by the call. | |
* @param data The call data (encoded using abi.encode or one of its variants). | |
*/ | |
function _callOptionalReturn(IERC20 token, bytes memory data) private { | |
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since | |
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that | |
// the target address contains contract code and also asserts for success in the low-level call. | |
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); | |
if (returndata.length > 0) { // Return data is optional | |
// solhint-disable-next-line max-line-length | |
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); | |
} | |
} | |
} | |
/** | |
* @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are | |
* supported. | |
*/ | |
function _require(bool condition, uint256 errorCode) pure { | |
if (!condition) _revert(errorCode); | |
} | |
/** | |
* @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported. | |
*/ | |
function _revert(uint256 errorCode) pure { | |
// We're going to dynamically create a revert string based on the error code, with the following format: | |
// 'BAL#{errorCode}' | |
// where the code is left-padded with zeroes to three digits (so they range from 000 to 999). | |
// | |
// We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a | |
// number (8 to 16 bits) than the individual string characters. | |
// | |
// The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a | |
// much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a | |
// safe place to rely on it without worrying about how its usage might affect e.g. memory contents. | |
assembly { | |
// First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999 | |
// range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for | |
// the '0' character. | |
let units := add(mod(errorCode, 10), 0x30) | |
errorCode := div(errorCode, 10) | |
let tenths := add(mod(errorCode, 10), 0x30) | |
errorCode := div(errorCode, 10) | |
let hundreds := add(mod(errorCode, 10), 0x30) | |
// With the individual characters, we can now construct the full string. The "BAL#" part is a known constant | |
// (0x42414c23): we simply shift this by 24 (to provide space for the 3 bytes of the error code), and add the | |
// characters to it, each shifted by a multiple of 8. | |
// The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits | |
// per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte | |
// array). | |
let revertReason := shl(200, add(0x42414c23000000, add(add(units, shl(8, tenths)), shl(16, hundreds)))) | |
// We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded | |
// message will have the following layout: | |
// [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ] | |
// The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We | |
// also write zeroes to the next 28 bytes of memory, but those are about to be overwritten. | |
mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000) | |
// Next is the offset to the location of the string, which will be placed immediately after (20 bytes away). | |
mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) | |
// The string length is fixed: 7 characters. | |
mstore(0x24, 7) | |
// Finally, the string itself is stored. | |
mstore(0x44, revertReason) | |
// Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of | |
// the encoded message is therefore 4 + 32 + 32 + 32 = 100. | |
revert(0, 100) | |
} | |
} | |
library Errors { | |
// Math | |
uint256 internal constant ADD_OVERFLOW = 0; | |
uint256 internal constant SUB_OVERFLOW = 1; | |
uint256 internal constant SUB_UNDERFLOW = 2; | |
uint256 internal constant MUL_OVERFLOW = 3; | |
uint256 internal constant ZERO_DIVISION = 4; | |
uint256 internal constant DIV_INTERNAL = 5; | |
uint256 internal constant X_OUT_OF_BOUNDS = 6; | |
uint256 internal constant Y_OUT_OF_BOUNDS = 7; | |
uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8; | |
uint256 internal constant INVALID_EXPONENT = 9; | |
// Input | |
uint256 internal constant OUT_OF_BOUNDS = 100; | |
uint256 internal constant UNSORTED_ARRAY = 101; | |
uint256 internal constant UNSORTED_TOKENS = 102; | |
uint256 internal constant INPUT_LENGTH_MISMATCH = 103; | |
uint256 internal constant ZERO_TOKEN = 104; | |
// Shared pools | |
uint256 internal constant MIN_TOKENS = 200; | |
uint256 internal constant MAX_TOKENS = 201; | |
uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202; | |
uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203; | |
uint256 internal constant MINIMUM_BPT = 204; | |
uint256 internal constant CALLER_NOT_VAULT = 205; | |
uint256 internal constant UNINITIALIZED = 206; | |
uint256 internal constant BPT_IN_MAX_AMOUNT = 207; | |
uint256 internal constant BPT_OUT_MIN_AMOUNT = 208; | |
uint256 internal constant EXPIRED_PERMIT = 209; | |
// Pools | |
uint256 internal constant MIN_AMP = 300; | |
uint256 internal constant MAX_AMP = 301; | |
uint256 internal constant MIN_WEIGHT = 302; | |
uint256 internal constant MAX_STABLE_TOKENS = 303; | |
uint256 internal constant MAX_IN_RATIO = 304; | |
uint256 internal constant MAX_OUT_RATIO = 305; | |
uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306; | |
uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307; | |
uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308; | |
uint256 internal constant INVALID_TOKEN = 309; | |
uint256 internal constant UNHANDLED_JOIN_KIND = 310; | |
uint256 internal constant ZERO_INVARIANT = 311; | |
uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312; | |
uint256 internal constant ORACLE_NOT_INITIALIZED = 313; | |
uint256 internal constant ORACLE_QUERY_TOO_OLD = 314; | |
uint256 internal constant ORACLE_INVALID_INDEX = 315; | |
uint256 internal constant ORACLE_BAD_SECS = 316; | |
// Lib | |
uint256 internal constant REENTRANCY = 400; | |
uint256 internal constant SENDER_NOT_ALLOWED = 401; | |
uint256 internal constant PAUSED = 402; | |
uint256 internal constant PAUSE_WINDOW_EXPIRED = 403; | |
uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404; | |
uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405; | |
uint256 internal constant INSUFFICIENT_BALANCE = 406; | |
uint256 internal constant INSUFFICIENT_ALLOWANCE = 407; | |
uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408; | |
uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409; | |
uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410; | |
uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411; | |
uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412; | |
uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413; | |
uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414; | |
uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415; | |
uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416; | |
uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417; | |
uint256 internal constant SAFE_ERC20_CALL_FAILED = 418; | |
uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419; | |
uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420; | |
uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421; | |
uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422; | |
uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423; | |
uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424; | |
uint256 internal constant BUFFER_PERIOD_EXPIRED = 425; | |
// Vault | |
uint256 internal constant INVALID_POOL_ID = 500; | |
uint256 internal constant CALLER_NOT_POOL = 501; | |
uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502; | |
uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503; | |
uint256 internal constant INVALID_SIGNATURE = 504; | |
uint256 internal constant EXIT_BELOW_MIN = 505; | |
uint256 internal constant JOIN_ABOVE_MAX = 506; | |
uint256 internal constant SWAP_LIMIT = 507; | |
uint256 internal constant SWAP_DEADLINE = 508; | |
uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509; | |
uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510; | |
uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511; | |
uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512; | |
uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513; | |
uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514; | |
uint256 internal constant INVALID_POST_LOAN_BALANCE = 515; | |
uint256 internal constant INSUFFICIENT_ETH = 516; | |
uint256 internal constant UNALLOCATED_ETH = 517; | |
uint256 internal constant ETH_TRANSFER = 518; | |
uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519; | |
uint256 internal constant TOKENS_MISMATCH = 520; | |
uint256 internal constant TOKEN_NOT_REGISTERED = 521; | |
uint256 internal constant TOKEN_ALREADY_REGISTERED = 522; | |
uint256 internal constant TOKENS_ALREADY_SET = 523; | |
uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524; | |
uint256 internal constant NONZERO_TOKEN_BALANCE = 525; | |
uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526; | |
uint256 internal constant POOL_NO_TOKENS = 527; | |
uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528; | |
// Fees | |
uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600; | |
uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601; | |
uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602; | |
} | |
library LogExpMath { | |
// All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying | |
// two numbers, and multiply by ONE when dividing them. | |
// All arguments and return values are 18 decimal fixed point numbers. | |
int256 constant ONE_18 = 1e18; | |
// Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the | |
// case of ln36, 36 decimals. | |
int256 constant ONE_20 = 1e20; | |
int256 constant ONE_36 = 1e36; | |
// The domain of natural exponentiation is bound by the word size and number of decimals used. | |
// | |
// Because internally the result will be stored using 20 decimals, the largest possible result is | |
// (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221. | |
// The smallest possible result is 10^(-18), which makes largest negative argument | |
// ln(10^(-18)) = -41.446531673892822312. | |
// We use 130.0 and -41.0 to have some safety margin. | |
int256 constant MAX_NATURAL_EXPONENT = 130e18; | |
int256 constant MIN_NATURAL_EXPONENT = -41e18; | |
// Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point | |
// 256 bit integer. | |
int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17; | |
int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17; | |
uint256 constant MILD_EXPONENT_BOUND = 2**254 / uint256(ONE_20); | |
// 18 decimal constants | |
int256 constant x0 = 128000000000000000000; // 2ˆ7 | |
int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; // eˆ(x0) (no decimals) | |
int256 constant x1 = 64000000000000000000; // 2ˆ6 | |
int256 constant a1 = 6235149080811616882910000000; // eˆ(x1) (no decimals) | |
// 20 decimal constants | |
int256 constant x2 = 3200000000000000000000; // 2ˆ5 | |
int256 constant a2 = 7896296018268069516100000000000000; // eˆ(x2) | |
int256 constant x3 = 1600000000000000000000; // 2ˆ4 | |
int256 constant a3 = 888611052050787263676000000; // eˆ(x3) | |
int256 constant x4 = 800000000000000000000; // 2ˆ3 | |
int256 constant a4 = 298095798704172827474000; // eˆ(x4) | |
int256 constant x5 = 400000000000000000000; // 2ˆ2 | |
int256 constant a5 = 5459815003314423907810; // eˆ(x5) | |
int256 constant x6 = 200000000000000000000; // 2ˆ1 | |
int256 constant a6 = 738905609893065022723; // eˆ(x6) | |
int256 constant x7 = 100000000000000000000; // 2ˆ0 | |
int256 constant a7 = 271828182845904523536; // eˆ(x7) | |
int256 constant x8 = 50000000000000000000; // 2ˆ-1 | |
int256 constant a8 = 164872127070012814685; // eˆ(x8) | |
int256 constant x9 = 25000000000000000000; // 2ˆ-2 | |
int256 constant a9 = 128402541668774148407; // eˆ(x9) | |
int256 constant x10 = 12500000000000000000; // 2ˆ-3 | |
int256 constant a10 = 113314845306682631683; // eˆ(x10) | |
int256 constant x11 = 6250000000000000000; // 2ˆ-4 | |
int256 constant a11 = 106449445891785942956; // eˆ(x11) | |
/** | |
* @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent. | |
* | |
* Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`. | |
*/ | |
function pow(uint256 x, uint256 y) internal pure returns (uint256) { | |
if (y == 0) { | |
// We solve the 0^0 indetermination by making it equal one. | |
return uint256(ONE_18); | |
} | |
if (x == 0) { | |
return 0; | |
} | |
// Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to | |
// arrive at that result. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means | |
// x^y = exp(y * ln(x)). | |
// The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range. | |
_require(x < 2**255, Errors.X_OUT_OF_BOUNDS); | |
int256 x_int256 = int256(x); | |
// We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In | |
// both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end. | |
// This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range. | |
_require(y < MILD_EXPONENT_BOUND, Errors.Y_OUT_OF_BOUNDS); | |
int256 y_int256 = int256(y); | |
int256 logx_times_y; | |
if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) { | |
int256 ln_36_x = _ln_36(x_int256); | |
// ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just | |
// bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal | |
// multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the | |
// (downscaled) last 18 decimals. | |
logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18); | |
} else { | |
logx_times_y = _ln(x_int256) * y_int256; | |
} | |
logx_times_y /= ONE_18; | |
// Finally, we compute exp(y * ln(x)) to arrive at x^y | |
_require( | |
MIN_NATURAL_EXPONENT <= logx_times_y && logx_times_y <= MAX_NATURAL_EXPONENT, | |
Errors.PRODUCT_OUT_OF_BOUNDS | |
); | |
return uint256(exp(logx_times_y)); | |
} | |
/** | |
* @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent. | |
* | |
* Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`. | |
*/ | |
function exp(int256 x) internal pure returns (int256) { | |
_require(x >= MIN_NATURAL_EXPONENT && x <= MAX_NATURAL_EXPONENT, Errors.INVALID_EXPONENT); | |
if (x < 0) { | |
// We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it | |
// fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT). | |
// Fixed point division requires multiplying by ONE_18. | |
return ((ONE_18 * ONE_18) / exp(-x)); | |
} | |
// First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n, | |
// where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7 | |
// because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the | |
// decomposition. | |
// At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this | |
// decomposition, which will be lower than the smallest x_n. | |
// exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1. | |
// We mutate x by subtracting x_n, making it the remainder of the decomposition. | |
// The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause | |
// intermediate overflows. Instead we store them as plain integers, with 0 decimals. | |
// Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the | |
// decomposition. | |
// For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct | |
// it and compute the accumulated product. | |
int256 firstAN; | |
if (x >= x0) { | |
x -= x0; | |
firstAN = a0; | |
} else if (x >= x1) { | |
x -= x1; | |
firstAN = a1; | |
} else { | |
firstAN = 1; // One with no decimal places | |
} | |
// We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the | |
// smaller terms. | |
x *= 100; | |
// `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point | |
// one. Recall that fixed point multiplication requires dividing by ONE_20. | |
int256 product = ONE_20; | |
if (x >= x2) { | |
x -= x2; | |
product = (product * a2) / ONE_20; | |
} | |
if (x >= x3) { | |
x -= x3; | |
product = (product * a3) / ONE_20; | |
} | |
if (x >= x4) { | |
x -= x4; | |
product = (product * a4) / ONE_20; | |
} | |
if (x >= x5) { | |
x -= x5; | |
product = (product * a5) / ONE_20; | |
} | |
if (x >= x6) { | |
x -= x6; | |
product = (product * a6) / ONE_20; | |
} | |
if (x >= x7) { | |
x -= x7; | |
product = (product * a7) / ONE_20; | |
} | |
if (x >= x8) { | |
x -= x8; | |
product = (product * a8) / ONE_20; | |
} | |
if (x >= x9) { | |
x -= x9; | |
product = (product * a9) / ONE_20; | |
} | |
// x10 and x11 are unnecessary here since we have high enough precision already. | |
// Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series | |
// expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!). | |
int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places. | |
int256 term; // Each term in the sum, where the nth term is (x^n / n!). | |
// The first term is simply x. | |
term = x; | |
seriesSum += term; | |
// Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number, | |
// multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not. | |
term = ((term * x) / ONE_20) / 2; | |
seriesSum += term; | |
term = ((term * x) / ONE_20) / 3; | |
seriesSum += term; | |
term = ((term * x) / ONE_20) / 4; | |
seriesSum += term; | |
term = ((term * x) / ONE_20) / 5; | |
seriesSum += term; | |
term = ((term * x) / ONE_20) / 6; | |
seriesSum += term; | |
term = ((term * x) / ONE_20) / 7; | |
seriesSum += term; | |
term = ((term * x) / ONE_20) / 8; | |
seriesSum += term; | |
term = ((term * x) / ONE_20) / 9; | |
seriesSum += term; | |
term = ((term * x) / ONE_20) / 10; | |
seriesSum += term; | |
term = ((term * x) / ONE_20) / 11; | |
seriesSum += term; | |
term = ((term * x) / ONE_20) / 12; | |
seriesSum += term; | |
// 12 Taylor terms are sufficient for 18 decimal precision. | |
// We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor | |
// approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply | |
// all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication), | |
// and then drop two digits to return an 18 decimal value. | |
return (((product * seriesSum) / ONE_20) * firstAN) / 100; | |
} | |
/** | |
* @dev Logarithm (log(arg, base), with signed 18 decimal fixed point base and argument. | |
*/ | |
function log(int256 arg, int256 base) internal pure returns (int256) { | |
// This performs a simple base change: log(arg, base) = ln(arg) / ln(base). | |
// Both logBase and logArg are computed as 36 decimal fixed point numbers, either by using ln_36, or by | |
// upscaling. | |
int256 logBase; | |
if (LN_36_LOWER_BOUND < base && base < LN_36_UPPER_BOUND) { | |
logBase = _ln_36(base); | |
} else { | |
logBase = _ln(base) * ONE_18; | |
} | |
int256 logArg; | |
if (LN_36_LOWER_BOUND < arg && arg < LN_36_UPPER_BOUND) { | |
logArg = _ln_36(arg); | |
} else { | |
logArg = _ln(arg) * ONE_18; | |
} | |
// When dividing, we multiply by ONE_18 to arrive at a result with 18 decimal places | |
return (logArg * ONE_18) / logBase; | |
} | |
/** | |
* @dev Natural logarithm (ln(a)) with signed 18 decimal fixed point argument. | |
*/ | |
function ln(int256 a) internal pure returns (int256) { | |
// The real natural logarithm is not defined for negative numbers or zero. | |
_require(a > 0, Errors.OUT_OF_BOUNDS); | |
if (LN_36_LOWER_BOUND < a && a < LN_36_UPPER_BOUND) { | |
return _ln_36(a) / ONE_18; | |
} else { | |
return _ln(a); | |
} | |
} | |
/** | |
* @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument. | |
*/ | |
function _ln(int256 a) private pure returns (int256) { | |
if (a < ONE_18) { | |
// Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less | |
// than one, 1/a will be greater than one, and this if statement will not be entered in the recursive call. | |
// Fixed point division requires multiplying by ONE_18. | |
return (-_ln((ONE_18 * ONE_18) / a)); | |
} | |
// First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which | |
// we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is, | |
// ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot | |
// be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a. | |
// At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this | |
// decomposition, which will be lower than the smallest a_n. | |
// ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1. | |
// We mutate a by subtracting a_n, making it the remainder of the decomposition. | |
// For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point | |
// numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by | |
// ONE_18 to convert them to fixed point. | |
// For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide | |
// by it and compute the accumulated sum. | |
int256 sum = 0; | |
if (a >= a0 * ONE_18) { | |
a /= a0; // Integer, not fixed point division | |
sum += x0; | |
} | |
if (a >= a1 * ONE_18) { | |
a /= a1; // Integer, not fixed point division | |
sum += x1; | |
} | |
// All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format. | |
sum *= 100; | |
a *= 100; | |
// Because further a_n are 20 digit fixed point numbers, we multiply by ONE_20 when dividing by them. | |
if (a >= a2) { | |
a = (a * ONE_20) / a2; | |
sum += x2; | |
} | |
if (a >= a3) { | |
a = (a * ONE_20) / a3; | |
sum += x3; | |
} | |
if (a >= a4) { | |
a = (a * ONE_20) / a4; | |
sum += x4; | |
} | |
if (a >= a5) { | |
a = (a * ONE_20) / a5; | |
sum += x5; | |
} | |
if (a >= a6) { | |
a = (a * ONE_20) / a6; | |
sum += x6; | |
} | |
if (a >= a7) { | |
a = (a * ONE_20) / a7; | |
sum += x7; | |
} | |
if (a >= a8) { | |
a = (a * ONE_20) / a8; | |
sum += x8; | |
} | |
if (a >= a9) { | |
a = (a * ONE_20) / a9; | |
sum += x9; | |
} | |
if (a >= a10) { | |
a = (a * ONE_20) / a10; | |
sum += x10; | |
} | |
if (a >= a11) { | |
a = (a * ONE_20) / a11; | |
sum += x11; | |
} | |
// a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series | |
// that converges rapidly for values of `a` close to one - the same one used in ln_36. | |
// Let z = (a - 1) / (a + 1). | |
// ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1)) | |
// Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires | |
// division by ONE_20. | |
int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20); | |
int256 z_squared = (z * z) / ONE_20; | |
// num is the numerator of the series: the z^(2 * n + 1) term | |
int256 num = z; | |
// seriesSum holds the accumulated sum of each term in the series, starting with the initial z | |
int256 seriesSum = num; | |
// In each step, the numerator is multiplied by z^2 | |
num = (num * z_squared) / ONE_20; | |
seriesSum += num / 3; | |
num = (num * z_squared) / ONE_20; | |
seriesSum += num / 5; | |
num = (num * z_squared) / ONE_20; | |
seriesSum += num / 7; | |
num = (num * z_squared) / ONE_20; | |
seriesSum += num / 9; | |
num = (num * z_squared) / ONE_20; | |
seriesSum += num / 11; | |
// 6 Taylor terms are sufficient for 36 decimal precision. | |
// Finally, we multiply by 2 (non fixed point) to compute ln(remainder) | |
seriesSum *= 2; | |
// We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both | |
// with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal | |
// value. | |
return (sum + seriesSum) / 100; | |
} | |
/** | |
* @dev Intrnal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument, | |
* for x close to one. | |
* | |
* Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND. | |
*/ | |
function _ln_36(int256 x) private pure returns (int256) { | |
// Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits | |
// worthwhile. | |
// First, we transform x to a 36 digit fixed point value. | |
x *= ONE_18; | |
// We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1). | |
// ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1)) | |
// Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires | |
// division by ONE_36. | |
int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36); | |
int256 z_squared = (z * z) / ONE_36; | |
// num is the numerator of the series: the z^(2 * n + 1) term | |
int256 num = z; | |
// seriesSum holds the accumulated sum of each term in the series, starting with the initial z | |
int256 seriesSum = num; | |
// In each step, the numerator is multiplied by z^2 | |
num = (num * z_squared) / ONE_36; | |
seriesSum += num / 3; | |
num = (num * z_squared) / ONE_36; | |
seriesSum += num / 5; | |
num = (num * z_squared) / ONE_36; | |
seriesSum += num / 7; | |
num = (num * z_squared) / ONE_36; | |
seriesSum += num / 9; | |
num = (num * z_squared) / ONE_36; | |
seriesSum += num / 11; | |
num = (num * z_squared) / ONE_36; | |
seriesSum += num / 13; | |
num = (num * z_squared) / ONE_36; | |
seriesSum += num / 15; | |
// 8 Taylor terms are sufficient for 36 decimal precision. | |
// All that remains is multiplying by 2 (non fixed point). | |
return seriesSum * 2; | |
} | |
} | |
library FixedPoint { | |
uint256 internal constant ONE = 1e18; // 18 decimal places | |
uint256 internal constant MAX_POW_RELATIVE_ERROR = 10000; // 10^(-14) | |
// Minimum base for the power function when the exponent is 'free' (larger than ONE). | |
uint256 internal constant MIN_POW_BASE_FREE_EXPONENT = 0.7e18; | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
// Fixed Point addition is the same as regular checked addition | |
uint256 c = a + b; | |
_require(c >= a, Errors.ADD_OVERFLOW); | |
return c; | |
} | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
// Fixed Point addition is the same as regular checked addition | |
_require(b <= a, Errors.SUB_OVERFLOW); | |
uint256 c = a - b; | |
return c; | |
} | |
function mulDown(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 product = a * b; | |
_require(a == 0 || product / a == b, Errors.MUL_OVERFLOW); | |
return product / ONE; | |
} | |
function mulUp(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 product = a * b; | |
_require(a == 0 || product / a == b, Errors.MUL_OVERFLOW); | |
if (product == 0) { | |
return 0; | |
} else { | |
// The traditional divUp formula is: | |
// divUp(x, y) := (x + y - 1) / y | |
// To avoid intermediate overflow in the addition, we distribute the division and get: | |
// divUp(x, y) := (x - 1) / y + 1 | |
// Note that this requires x != 0, which we already tested for. | |
return ((product - 1) / ONE) + 1; | |
} | |
} | |
function divDown(uint256 a, uint256 b) internal pure returns (uint256) { | |
_require(b != 0, Errors.ZERO_DIVISION); | |
if (a == 0) { | |
return 0; | |
} else { | |
uint256 aInflated = a * ONE; | |
_require(aInflated / a == ONE, Errors.DIV_INTERNAL); // mul overflow | |
return aInflated / b; | |
} | |
} | |
function divUp(uint256 a, uint256 b) internal pure returns (uint256) { | |
_require(b != 0, Errors.ZERO_DIVISION); | |
if (a == 0) { | |
return 0; | |
} else { | |
uint256 aInflated = a * ONE; | |
_require(aInflated / a == ONE, Errors.DIV_INTERNAL); // mul overflow | |
// The traditional divUp formula is: | |
// divUp(x, y) := (x + y - 1) / y | |
// To avoid intermediate overflow in the addition, we distribute the division and get: | |
// divUp(x, y) := (x - 1) / y + 1 | |
// Note that this requires x != 0, which we already tested for. | |
return ((aInflated - 1) / b) + 1; | |
} | |
} | |
/** | |
* @dev Returns x^y, assuming both are fixed point numbers, rounding down. The result is guaranteed to not be above | |
* the true value (that is, the error function expected - actual is always positive). | |
*/ | |
function powDown(uint256 x, uint256 y) internal pure returns (uint256) { | |
uint256 raw = LogExpMath.pow(x, y); | |
uint256 maxError = add(mulUp(raw, MAX_POW_RELATIVE_ERROR), 1); | |
if (raw < maxError) { | |
return 0; | |
} else { | |
return sub(raw, maxError); | |
} | |
} | |
/** | |
* @dev Returns x^y, assuming both are fixed point numbers, rounding up. The result is guaranteed to not be below | |
* the true value (that is, the error function expected - actual is always negative). | |
*/ | |
function powUp(uint256 x, uint256 y) internal pure returns (uint256) { | |
uint256 raw = LogExpMath.pow(x, y); | |
uint256 maxError = add(mulUp(raw, MAX_POW_RELATIVE_ERROR), 1); | |
return add(raw, maxError); | |
} | |
/** | |
* @dev Returns the complement of a value (1 - x), capped to 0 if x is larger than 1. | |
* | |
* Useful when computing the complement for values with some level of relative error, as it strips this error and | |
* prevents intermediate negative values. | |
*/ | |
function complement(uint256 x) internal pure returns (uint256) { | |
return (x < ONE) ? (ONE - x) : 0; | |
} | |
} | |
enum SwapKind { GIVEN_IN, GIVEN_OUT } | |
struct SingleSwap { | |
bytes32 poolId; | |
SwapKind kind; | |
address assetIn; | |
address assetOut; | |
uint256 amount; | |
bytes userData; | |
} | |
struct FundManagement { | |
address sender; | |
bool fromInternalBalance; | |
address recipient; | |
bool toInternalBalance; | |
} | |
interface IBalancerV2Vault { | |
function swap(SingleSwap calldata singleSwap, FundManagement calldata funds, uint256 limit, uint256 deadline) external returns (uint256 amountCalculatedInOut); | |
function getPoolTokens(bytes32 poolId) external view returns (address[] memory tokens, uint256[] memory balances, uint256 lastChangeBlock); | |
} | |
interface IBalancerV2Pool { | |
function getInvariant() external view returns (uint256); | |
} | |
interface AggregatorV3Interface { | |
function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); | |
} | |
contract BptPricing { | |
using SafeERC20 for IERC20; | |
using Address for address; | |
using FixedPoint for uint256; | |
address public owner; | |
// constant address - Ethereum | |
bytes32 public constant balETHPoolId = 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014; | |
address public constant balETHPool = 0x5c6Ee304399DBdB9C8Ef030aB642B10820DB8F56; | |
address public constant balancerVault = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; | |
address public constant balETHChainlinkFeed = 0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b; | |
address public constant wrappedNativeToken = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;//wETH | |
address public constant balToken = 0xba100000625a3754423978a60c9317c58a424e3D;//BAL | |
uint256 public constant balPoolWeight = 800000000000000000; | |
uint256 public constant ethPoolWeight = 200000000000000000; | |
constructor() { | |
// for swap | |
IERC20(wrappedNativeToken).approve(balancerVault, type(uint256).max); | |
IERC20(balToken).approve(balancerVault, type(uint256).max); | |
} | |
function swapInBpt() external{ | |
SingleSwap memory singleSwap = SingleSwap(balETHPoolId, SwapKind.GIVEN_IN, wrappedNativeToken, balToken, IERC20(wrappedNativeToken).balanceOf(address(this)), ""); | |
FundManagement memory funds = FundManagement(address(this), false, address(this), false); | |
IBalancerV2Vault(balancerVault).swap(singleSwap, funds, 0, block.timestamp); | |
} | |
function calculateBpt(bool _beforeSwap) public view returns(uint256, uint256, uint256, uint256){ | |
uint256 _V = IBalancerV2Pool(balETHPool).getInvariant(); | |
(,int price,,,) = AggregatorV3Interface(balETHChainlinkFeed).latestRoundData(); | |
if (_beforeSwap){ | |
(,uint256[] memory _bals,) = IBalancerV2Vault(balancerVault).getPoolTokens(balETHPoolId); | |
(uint256 _p,,) = calcBptPrice(uint256(price), _V, IERC20(balETHPool).totalSupply(), balPoolWeight, ethPoolWeight); | |
return (_p, _bals[0], _bals[1], _V); | |
}else{ | |
(uint256 _p, uint256 _fairBal1, uint256 _fairBal2) = calcBptPrice(uint256(price), _V, IERC20(balETHPool).totalSupply(), balPoolWeight, ethPoolWeight); | |
return (_p, _fairBal1, _fairBal2, _V); | |
} | |
} | |
function calcBptPrice(uint256 relativeFairPrice, uint256 invariant, uint256 totalSupply, uint256 weight1, uint256 weight2) public pure returns(uint256, uint256, uint256){ | |
require(weight1.add(weight2) == 1e18, '!weights'); | |
uint256 _fairBal1 = invariant.mulDown(weight1.powDown(weight2).divDown(weight2.powDown(weight2))).divDown(relativeFairPrice.powDown(weight2)); | |
uint256 _fairBal2 = invariant.mulDown(weight2.powDown(weight1).divDown(weight1.powDown(weight1))).mulDown(relativeFairPrice.powDown(weight1)); | |
uint256 _fairBptPrice = _fairBal2.add(_fairBal1.mulDown(relativeFairPrice)).divDown(totalSupply); | |
return (_fairBptPrice, _fairBal1, _fairBal2); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import brownie | |
from brownie import * | |
import pytest | |
""" | |
test BPT pricing | |
@pytest.fixture | |
def bptpricing(): | |
return BptPricing.deploy({"from": a[0]}) | |
""" | |
def test_bpt_pricing_bal_eth(weth_whale, weth, bptpricing): | |
(_p, _bal1, _bal2, _V) = bptpricing.calculateBpt(True) | |
print('Before swap: bptPriceInETH=' + str(_p) + ', fairBal1=' + str(_bal1) + ', fairBal2=' + str(_bal2) + ', invariant=' + str(_V)) | |
## check bpt pricing against swap manipulation, e.g., swap WETH for BAL to push BAL price much higher | |
## balancer got some limit in one swap: https://dev.balancer.fi/references/error-codes#pools | |
## bal-eth pool: https://etherscan.io/address/0x5c6ee304399dbdb9c8ef030ab642b10820db8f56#code#F6#L36 | |
sugar_amount = _bal2 * 0.299 | |
weth.transfer(bptpricing.address, sugar_amount, {'from': weth_whale}) | |
bptpricing.swapInBpt() | |
(_pAfter, _bal1After, _bal2After, _VAfter) = bptpricing.calculateBpt(False) | |
print('After swap: bptPriceInETH=' + str(_pAfter) + ', fairBal1=' + str(_bal1After) + ', fairBal2=' + str(_bal2After) + ', invariant=' + str(_VAfter)) | |
# check if any serious change after swap | |
diff = 0 | |
if(_pAfter > _p): | |
diff = _pAfter - _p | |
else: | |
diff = _p - _pAfter | |
assert (diff / _p) <= 0.001 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://hackmd.io/@re73/SJHmQaCFq
another analysis using spot-price derivation https://hackmd.io/@shuklaayush/BkAtKbCY9